async def adventures(self, ctx): """A list of all adventures with success rates, name and time it takes.""" sword, shield = await self.bot.get_equipped_items_for(ctx.author) all_dungeons = list(self.bot.config.adventure_times.keys()) level = rpgtools.xptolevel(ctx.character_data["xp"]) damage = sword["damage"] if sword else 0 defense = shield["armor"] if shield else 0 msg = await ctx.send("Loading images...") chances = [] for adv in all_dungeons: success = rpgtools.calcchance(damage, defense, adv, int(level), returnsuccess=False) chances.append((success[0] - success[2], success[1] + success[2])) thing = functools.partial(rpgtools.makeadventures, chances) images = await self.bot.loop.run_in_executor(None, thing) await msg.delete() files = [ discord.File(img, filename=f"Adventure{idx + 1}.png") for idx, img in enumerate(images) ] pages = [ discord.Embed().set_image( url=f"attachment://Adventure{idx + 1}.png") for idx in range(len(images)) ] await self.bot.paginator.AdventurePaginator(embeds=pages, files=files).paginate(ctx)
async def best(self, ctx): _("""Shows the best players of your guild by XP.""") await ctx.trigger_typing() async with self.bot.pool.acquire() as conn: guild = await conn.fetchrow( 'SELECT * FROM guild WHERE "id"=$1;', ctx.character_data["guild"] ) players = await conn.fetch( 'SELECT "user", "name", "xp" FROM profile WHERE "guild"=$1 ORDER BY "xp" DESC LIMIT 10;', guild["id"], ) result = "" for idx, profile in enumerate(players): charname = await rpgtools.lookup(self.bot, profile[0]) text = _( "{name}, a character by `{charname}` with Level **{level}** (**{xp}** XP)" ).format( charname=charname, name=profile["name"], level=rpgtools.xptolevel(profile["xp"]), xp=profile["xp"], ) result = f"{result}{idx + 1}. {text}\n" await ctx.send( embed=discord.Embed( title=_("The Best Players of {name}").format(name=guild["name"]), description=result, colour=0xE7CA01, ) )
async def adventure(self, ctx, adventure_number: IntFromTo(1, 30)): _( """`<adventure_number>` - a whole number from 1 to 30 Send your character on an adventure with the difficulty `<adventure_number>`. The adventure will take `<adventure_number>` hours if no time booster is used, and half as long if a time booster is used. If you are in an alliance which owns a city with adventure buildings, your adventure time will be reduced by the adventure building level in %. Donators' time will also be reduced: - 5% reduction for Silver Donators - 10% reduction for Gold Donators - 25% reduction for Emerald Donators and above Be sure to check `{prefix}status` to check how much time is left, or to check if you survived or died.""" ) if adventure_number > rpgtools.xptolevel(ctx.character_data["xp"]): return await ctx.send( _("You must be on level **{level}** to do this adventure.").format( level=adventure_number ) ) time = timedelta(hours=adventure_number) if buildings := await self.bot.get_city_buildings(ctx.character_data["guild"]): time -= time * (buildings["adventure_building"] / 100)
async def evolve(self, ctx): _("""Evolve to the next level of your classes.""") level = int(rpgtools.xptolevel(ctx.character_data["xp"])) if level < 5: return await ctx.send(_("Your level isn't high enough to evolve.")) newindex = int(level / 5) - 1 updated = 0 new_classes = [] for class_ in ctx.character_data["class"]: if class_ != "No Class": new_classes.append( self.bot.get_class_evolves()[self.bot.get_class_line(class_)][ newindex ] ) updated += 1 else: new_classes.append("No Class") if updated == 0: return await ctx.send(_("You haven't got a class yet.")) await self.bot.pool.execute( 'UPDATE profile SET "class"=$1 WHERE "user"=$2;', new_classes, ctx.author.id ) await ctx.send( _("You are now a `{class1}` and a `{class2}`.").format( class1=new_classes[0], class2=new_classes[1] ) )
async def is_in_guild(userid, difficulty): user = await self.bot.pool.fetchrow( 'SELECT guild, xp FROM profile WHERE "user"=$1;', userid) if user[0] == guild["id"]: difficulty += int(rpgtools.xptolevel(user[1])) return difficulty return False
async def profile(self, ctx, *, person: User = Author): _("""View someone's or your own profile.""") await ctx.trigger_typing() targetid = person.id async with self.bot.pool.acquire() as conn: profile = await conn.fetchrow( 'SELECT * FROM profile WHERE "user"=$1;', targetid ) if not profile: return await ctx.send( _("**{person}** does not have a character.").format(person=person) ) sword, shield = await self.bot.get_equipped_items_for(targetid) ranks = await self.bot.get_ranks_for(targetid) mission = await self.bot.get_adventure(targetid) guild = await conn.fetchval( 'SELECT name FROM guild WHERE "id"=$1;', profile["guild"] ) v1 = sword["damage"] if sword else 0.0 v2 = shield["armor"] if shield else 0.0 damage, armor = await self.bot.generate_stats( targetid, v1, v2, classes=profile["class"], race=profile["race"] ) extras = (damage - v1, armor - v2) sworddmg = f"{v1}{' (+' + str(extras[0]) + ')' if extras[0] else ''}" shielddef = f"{v2}{' (+' + str(extras[1]) + ')' if extras[1] else ''}" url = ( f"{self.bot.config.okapi_url}/api/genprofile/beta" if self.bot.config.is_beta else f"{self.bot.config.okapi_url}/api/genprofile" ) async with self.bot.trusted_session.post( url, data={ "name": profile["name"], "color": profile["colour"], "image": profile["background"], "money": f"{profile['money']}", "pvpWins": f"{profile['pvpwins']}", "ecoRank": f"{ranks[0]}", "rank": f"{ranks[1]}", "level": rpgtools.xptolevel(profile["xp"]), "swordDamage": sworddmg, "shieldDamage": shielddef, # Dini you f****d up "swordName": sword["name"] if sword else "None Equipped", "shieldName": shield["name"] if shield else "None Equipped", "married": await rpgtools.lookup(self.bot, profile["marriage"]) or _("Not Married"), "guild": guild, "classes": profile["class"], "icons": [ self.bot.get_class_line(c).lower() for c in profile["class"] ], "mission": f"{mission[0]} - {mission[1] if not mission[2] else _('Finished')}" if mission else _("No Mission"), }, ) as req: img = BytesIO(await req.read()) await ctx.send(file=discord.File(fp=img, filename="Profile.png"))
async def status(self, ctx): _("""Checks your adventure status.""") num, time, done = ctx.adventure_data if not done: return await ctx.send( _("""\ You are currently on an adventure with difficulty `{difficulty}`. Time until it completes: `{time_left}` Adventure name: `{adventure}`""").format( difficulty=num, time_left=time, adventure=self.bot.config.adventure_names[num], )) sword, shield = await self.bot.get_equipped_items_for(ctx.author) sword, shield = await self.bot.generate_stats( ctx.author, sword["damage"] if sword else 0, shield["armor"] if shield else 0, classes=ctx.character_data["class"], race=ctx.character_data["race"], ) luck_booster = await self.bot.get_booster(ctx.author, "luck") current_level = int(rpgtools.xptolevel(ctx.character_data["xp"])) luck_multiply = ctx.character_data["luck"] if (buildings := await self.bot.get_city_buildings(ctx.character_data["guild"])): bonus = buildings["adventure_building"]
async def adventures(self, ctx): _( """Shows all adventures, their names, descriptions, and your chances to beat them in picture form. Your chances are determined by your equipped items, race and class bonuses, your level and your God-given luck.""" ) damage, defense = await self.bot.get_damage_armor_for(ctx.author) level = rpgtools.xptolevel(ctx.character_data["xp"]) luck_booster = await self.bot.get_booster(ctx.author, "luck") chances = [] for adv in range(1, 31): success = rpgtools.calcchance( damage, defense, adv, int(level), ctx.character_data["luck"], booster=luck_booster, returnsuccess=False, ) chances.append(success) async with self.bot.trusted_session.post( f"{self.bot.config.external.okapi_url}/api/genadventures", json={"percentages": chances}, headers={"Authorization": self.bot.config.external.okapi_token}, ) as r: images = await r.json() pages = [discord.Embed().set_image(url=image) for image in images] await self.bot.paginator.Paginator(extras=pages).paginate(ctx)
async def get_level(self, userid): async with self.bot.pool.acquire() as conn: ret = await conn.fetchval( 'SELECT xp FROM profile WHERE "user"=$1;', userid) if not ret: return ret else: return rpgtools.xptolevel(ret)
async def profile2(self, ctx, target: User = Author): _("""View someone's profile, not image based.""") rank_money, rank_xp = await self.bot.get_ranks_for(target) sword, shield = await self.bot.get_equipped_items_for(target) async with self.bot.pool.acquire() as conn: p_data = await conn.fetchrow( 'SELECT * FROM profile WHERE "user"=$1;', target.id) if not p_data: return await ctx.send( _("**{target}** does not have a character.").format( target=target)) mission = await self.bot.get_adventure(target) guild = await conn.fetchval( 'SELECT name FROM guild WHERE "id"=$1;', p_data["guild"]) try: colour = discord.Colour.from_rgb( *rpgtools.hex_to_rgb(p_data["colour"])) except ValueError: colour = 0x000000 if mission: timeleft = str( mission[1]).split(".")[0] if not mission[2] else "Finished" sword = f"{sword['name']} - {sword['damage']}" if sword else "No sword" shield = f"{shield['name']} - {shield['armor']}" if shield else "No shield" level = rpgtools.xptolevel(p_data["xp"]) em = discord.Embed(colour=colour, title=f"{target}: {p_data['name']}") em.set_thumbnail(url=target.avatar_url) em.add_field( name=_("General"), value=_("""\ **Money**: `${money}` **Level**: `{level}` **Class**: `{class_}` **Race**: `{race}` **PvP Wins**: `{pvp}` **Guild**: `{guild}`""").format( money=p_data["money"], level=level, class_="/".join(p_data["class"]), race=p_data["race"], pvp=p_data["pvpwins"], guild=guild, ), ) em.add_field( name=_("Ranks"), value=_("**Richest**: `{rank_money}`\n**XP**: `{rank_xp}`").format( rank_money=rank_money, rank_xp=rank_xp), ) em.add_field( name=_("Equipment"), value=_("Sword: {sword}\nShield: {shield}").format(sword=sword, shield=shield), ) if mission: em.add_field(name=_("Mission"), value=f"{mission[0]} - {timeleft}") await ctx.send(embed=em)
async def xp(self, ctx): _("""Shows your current XP and level.""") points = ctx.character_data["xp"] await ctx.send( _("You currently have **{points} XP**, which means you are on Level **{level}**. Missing to next level: **{missing}**" ).format( points=points, level=rpgtools.xptolevel(points), missing=rpgtools.xptonextlevel(points), ))
async def xp(self, ctx, user: UserWithCharacter = Author): _("""Shows current XP and level of a player.""") if user.id == ctx.author.id: points = ctx.character_data["xp"] await ctx.send( _("You currently have **{points} XP**, which means you are on Level **{level}**. Missing to next level: **{missing}**" ).format( points=points, level=rpgtools.xptolevel(points), missing=rpgtools.xptonextlevel(points), )) else: points = ctx.user_data["xp"] await ctx.send( _("{user} has **{points} XP** and is on Level **{level}**. Missing to next level: **{missing}**" ).format( user=user, points=points, level=rpgtools.xptolevel(points), missing=rpgtools.xptonextlevel(points), ))
async def adventure(self, ctx, dungeonnumber: IntFromTo(1, 20)): _("""Sends your character on an adventure.""") if dungeonnumber > int(rpgtools.xptolevel(ctx.character_data["xp"])): return await ctx.send( _("You must be on level **{level}** to do this adventure."). format(level=dungeonnumber)) time_booster = await self.bot.get_booster(ctx.author, "time") time = self.bot.config.adventure_times[dungeonnumber] if time_booster: time = time / 2 await self.bot.start_adventure(ctx.author, dungeonnumber, time) await ctx.send( _("Successfully sent your character out on an adventure. Use `{prefix}status` to see the current status of the mission." ).format(prefix=ctx.prefix))
async def adventure(self, ctx, dungeonnumber: IntFromTo(1, 30)): _("""Sends your character on an adventure.""") if dungeonnumber > int(rpgtools.xptolevel(ctx.character_data["xp"])): return await ctx.send( _("You must be on level **{level}** to do this adventure."). format(level=dungeonnumber)) time_booster = await self.bot.get_booster(ctx.author, "time") time = self.bot.config.adventure_times[dungeonnumber] if time_booster: time = time / 2 if (buildings := await self.bot.get_city_buildings(ctx.character_data["guild"])): time -= time * (buildings["adventure_building"] / 100)
async def evolve(self, ctx): _("""Evolve to the next level of your class.""") level = int(rpgtools.xptolevel(ctx.character_data["xp"])) if level < 5: return await ctx.send(_("Your level isn't high enough to evolve.")) if ctx.character_data["class"] == "No Class": return await ctx.send(_("You haven't got a class yet.")) newindex = int(level / 5) - 1 newclass = self.bot.get_class_evolves()[self.bot.get_class_line( ctx.character_data["class"])][newindex] await self.bot.pool.execute( 'UPDATE profile SET "class"=$1 WHERE "user"=$2;', newclass, ctx.author.id) await ctx.send( _("You are now a `{newclass}`.").format(newclass=newclass))
async def evolve(self, ctx): _( # xgettext: no-python-format """Evolve your class, bringing it to the next level and giving better class bonuses. You can evolve every 5 levels, i.e. at level 5, level 10, level 15, level 20, level 25 and finally level 30. - Warriors gain +1 defense per evolution - Thieves gain +8% for their success chance per evolution - Mages gain +1 damage per evolution - Rangers' pets' hunted item get +3 minimum stat and +6 maximum stat per evolution - This means level 1 pets can hunt items from stat 3 to stat 6; level 2 pets from stat 6 to stat 12 - Raiders gain +0.1 defense and damage raidstats - Ritualists gain +5% extra favor when sacrificing per evolution (- Paragons gain +1 damage *and* +1 defense per evolution)""" ) level = rpgtools.xptolevel(ctx.character_data["xp"]) if level < 5: return await ctx.send(_("Your level isn't high enough to evolve.")) newindex = int(level / 5) - 1 updated = 0 new_classes = [] for class_ in ctx.character_data["class"]: if class_ != "No Class": new_classes.append( self.bot.get_class_evolves()[self.bot.get_class_line(class_)][ newindex ] ) updated += 1 else: new_classes.append("No Class") if updated == 0: return await ctx.send(_("You haven't got a class yet.")) if ctx.character_data["class"] == new_classes: return await ctx.send(_("Nothing to evolve.")) await self.bot.pool.execute( 'UPDATE profile SET "class"=$1 WHERE "user"=$2;', new_classes, ctx.author.id ) await self.bot.cache.update_profile_cols_abs(ctx.author.id, class_=new_classes) await ctx.send( _("You are now a `{class1}` and a `{class2}`.").format( class1=new_classes[0], class2=new_classes[1] ) )
async def status(self, ctx): _( """Checks the remaining time of your adventures, or if you survived or died. Your chance is checked here, not in `{prefix}adventure`. Your chances are determined by your equipped items, race and class bonuses, your level, God-given luck and active luck boosters. If you are in an alliance which owns a city with an adventure building, your chance will be increased by 1% per building level. If you survive on your adventure, you will receive gold up to the adventure number times 60, XP up to 500 times the adventure number and either a loot or gear item. The chance of loot is dependent on the adventure number and whether you use the Ritualist class, [check our wiki](https://wiki.idlerpg.xyz/index.php?title=Loot) for the exact chances. God given luck affects the amount of gold and the gear items' damage/defense and value. If you are in a guild, its guild bank will receive 10% of the amount of gold extra. If you are married, your partner will receive a portion of your gold extra as well, [check the wiki](https://wiki.idlerpg.xyz/index.php?title=Family#Adventure_Bonus) for the exact portion.""" ) num, time, done = ctx.adventure_data if not done: # TODO: Embeds ftw return await ctx.send( embed=discord.Embed( title=_("Adventure Status"), description=_( "You are currently on an adventure with difficulty" " **{difficulty}**.\nTime until it completes:" " **{time_left}**\nAdventure name: **{adventure}**" ).format( difficulty=num, time_left=time, adventure=ADVENTURE_NAMES[num], ), colour=self.bot.config.game.primary_colour, ) ) damage, armor = await self.bot.get_damage_armor_for(ctx.author) luck_booster = await self.bot.get_booster(ctx.author, "luck") current_level = int(rpgtools.xptolevel(ctx.character_data["xp"])) luck_multiply = ctx.character_data["luck"] if ( buildings := await self.bot.get_city_buildings(ctx.character_data["guild"]) ): bonus = buildings["adventure_building"]
async def adventures(self, ctx): _("""Shows all adventures, their names, descriptions, and your chances to beat them in picture form. Your chances are determined by your equipped items, race and class bonuses, your level and your God-given luck.""" ) damage, defense = await self.bot.get_damage_armor_for(ctx.author) level = rpgtools.xptolevel(ctx.character_data["xp"]) luck_booster = await self.bot.get_booster(ctx.author, "luck") msg = await ctx.send(_("Loading images...")) chances = [] for adv in self.bot.config.adventure_times: success = rpgtools.calcchance( damage, defense, adv, int(level), ctx.character_data["luck"], booster=luck_booster, returnsuccess=False, ) chances.append((success[0] - success[2], success[1] + success[2])) async with self.bot.trusted_session.post( f"{self.bot.config.okapi_url}/api/genadventures", json={"percentages": chances}, ) as r: images = await r.json() await msg.delete() files = [ discord.File(filename=f"Adventure{idx + 1}.png", fp=BytesIO(b64decode(img[22:]))) for idx, img in enumerate(images) ] pages = [ discord.Embed().set_image( url=f"attachment://Adventure{idx + 1}.png") for idx in range(len(images)) ] await self.bot.paginator.AdventurePaginator(embeds=pages, files=files).paginate(ctx)
async def adventures(self, ctx): _("""A list of all adventures with success rates, name and time it takes.""" ) sword, shield = await self.bot.get_equipped_items_for(ctx.author) all_dungeons = list(self.bot.config.adventure_times.keys()) level = rpgtools.xptolevel(ctx.character_data["xp"]) damage = sword["damage"] if sword else 0 defense = shield["armor"] if shield else 0 msg = await ctx.send(_("Loading images...")) chances = [] for adv in all_dungeons: success = rpgtools.calcchance( damage, defense, adv, int(level), ctx.character_data["luck"], returnsuccess=False, ) chances.append((success[0] - success[2], success[1] + success[2])) async with self.bot.trusted_session.post( f"{self.bot.config.okapi_url}/api/genadventures", json={"percentages": chances}, ) as r: images = await r.json() await msg.delete() files = [ discord.File(filename=f"Adventure{idx + 1}.png", fp=BytesIO(b64decode(img[22:]))) for idx, img in enumerate(images) ] pages = [ discord.Embed().set_image( url=f"attachment://Adventure{idx + 1}.png") for idx in range(len(images)) ] await self.bot.paginator.AdventurePaginator(embeds=pages, files=files).paginate(ctx)
async def highscore(self, ctx): _("""The top 10 players by XP.""") await ctx.trigger_typing() players = await self.bot.pool.fetch( 'SELECT "user", "name", "xp" from profile ORDER BY "xp" DESC LIMIT 10;' ) result = "" for idx, profile in enumerate(players): username = await rpgtools.lookup(self.bot, profile["user"]) text = _( "{name}, a character by `{username}` with Level **{level}** (**{xp}** XP)" ).format( name=profile["name"], username=username, level=rpgtools.xptolevel(profile["xp"]), xp=profile["xp"], ) result = f"{result}{idx + 1}. {text}\n" result = discord.Embed(title=_("The Best Players"), description=result, colour=0xE7CA01) await ctx.send(embed=result)
async def adventure(self, ctx, dungeonnumber: IntFromTo(1, 20)): _("""Sends your character on an adventure.""") if dungeonnumber > int(rpgtools.xptolevel(ctx.character_data["xp"])): return await ctx.send( _("You must be on level **{level}** to do this adventure."). format(level=dungeonnumber)) time_booster = await self.bot.get_booster(ctx.author, "time") time = self.bot.config.adventure_times[dungeonnumber] if time_booster: time = time / 2 # Silver = -5%, Gold = -10%, Emerald = -25% # TODO: Maybe make a func to get the actual rank if await user_is_patron(self.bot, ctx.author): # save calls for normal people :) if await user_is_patron(self.bot, ctx.author, "Emerald Donators"): time = time * 0.75 elif await user_is_patron(self.bot, ctx.author, "Gold Donators"): time = time * 0.9 elif await user_is_patron(self.bot, ctx.author, "Silver Donators"): time = time * 0.95 await self.bot.start_adventure(ctx.author, dungeonnumber, time) await ctx.send( _("Successfully sent your character out on an adventure. Use `{prefix}status` to see the current status of the mission." ).format(prefix=ctx.prefix))
async def _class(self, ctx): _( """Change or select your primary or secondary class. - Warriors gain added defense - Thieves gain access to `{prefix}steal` - Mages gain added damage - Rangers gain access to a pet which can hunt for gear items - Raiders gain additional raidstats, used in raidbattles and raids - Ritualists gain additional favor from sacrificing items and are twice as likely to receive loot from adventures (- Paragons gain added damage *and* defense; the class is only available to donators) The second class unlocks at level 12. Selecting a class the first time is free (No Class -> Class), but changing it later will cost $5,000 (Class -> another Class) (This command has a cooldown of 24 hours)""" ) if rpgtools.xptolevel(ctx.character_data["xp"]) >= 12: val = await self.bot.paginator.Choose( title=_("Select class to change"), entries=[_("Primary Class"), _("Secondary Class")], return_index=True, ).paginate(ctx) else: val = 0 embeds = [ discord.Embed( title=_("Warrior"), description=_( "The tank class. Charge into battle with additional defense!\n+1" " defense per evolution." ), color=self.bot.config.primary_colour, ), discord.Embed( title=_("Thief"), description=_( # xgettext: no-python-format "The sneaky money stealer...\nGet access to `{prefix}steal` to" " steal 10% of a random player's money, if successful.\n+8% success" " chance per evolution." ).format(prefix=ctx.prefix), color=self.bot.config.primary_colour, ), discord.Embed( title=_("Mage"), description=_( "Utilise powerful magic for stronger attacks.\n+1 damage per" " evolution." ), color=self.bot.config.primary_colour, ), discord.Embed( title=_("Ranger"), description=_( "Item hunter and trainer of their very own pet.\nGet access to" " `{prefix}pet` to interact with your pet and let it get items for" " you.\n+3 minimum stat and +6 maximum stat per evolution." ).format(prefix=ctx.prefix), colour=self.bot.config.primary_colour, ), discord.Embed( title=_("Raider"), description=_( "A strong warrior who gives their life for the fight against" " Zerekiel.\nEvery evolution boosts your raidstats by an additional" " 10%." ), colour=self.bot.config.primary_colour, ), discord.Embed( title=_("Ritualist"), description=_( "A seer, a sacrificer and a follower.\nThe Ritualist devotes their" " life to the god they follow. For every evolution, their" " sacrifices are 5% more effective. They have twice the chance to" " get loot from adventures." ), colour=self.bot.config.primary_colour, ), ] choices = ["Warrior", "Thief", "Mage", "Ranger", "Raider", "Ritualist"] if await user_is_patron(self.bot, ctx.author): embeds.append( discord.Embed( title=_("Paragon"), description=_( "Absorb the appreciation of the devs into your soul to power" " up.\n+1 damage and defense per evolution." ), color=self.bot.config.primary_colour, ) ) choices.append("Paragon") lines = [ self.bot.get_class_line(class_) for class_ in ctx.character_data["class"] ] for line in lines: for e in embeds: if _(line) == e.title: embeds.remove(e) try: choices.remove(line) except ValueError: pass profession = await self.bot.paginator.ChoosePaginator( extras=embeds, choices=choices ).paginate(ctx) profession_ = self.bot.config.classes[profession][0] new_classes = copy(ctx.character_data["class"]) new_classes[val] = profession_ if not await ctx.confirm( _( "You are about to select the `{profession}` class for yourself." " {textaddon} Proceed?" ).format( textaddon=_( "This **costs nothing**, but changing it later will cost **$5000**." ) if ctx.character_data["class"][val] == "No Class" else _("This will cost **$5000**."), profession=profession, ) ): return await ctx.send(_("Class selection cancelled.")) if ctx.character_data["class"][val] == "No Class": async with self.bot.pool.acquire() as conn: await conn.execute( 'UPDATE profile SET "class"=$1 WHERE "user"=$2;', new_classes, ctx.author.id, ) await self.bot.cache.update_profile_cols_abs( ctx.author.id, class_=new_classes ) if profession == "Ranger": await conn.execute( 'INSERT INTO pets ("user") VALUES ($1);', ctx.author.id ) await ctx.send( _("Your new class is now `{profession}`.").format( profession=_(profession) ) ) else: if not await self.bot.has_money(ctx.author.id, 5000): await self.bot.reset_cooldown(ctx) return await ctx.send( _("You're too poor for a class change, it costs **$5000**.") ) async with self.bot.pool.acquire() as conn: await conn.execute( 'UPDATE profile SET "class"=$1, "money"="money"-$2 WHERE' ' "user"=$3;', new_classes, 5000, ctx.author.id, ) await self.bot.cache.update_profile_cols_rel( ctx.author.id, class_=new_classes, money=-5000 ) await conn.execute('DELETE FROM pets WHERE "user"=$1;', ctx.author.id) if profession == "Ranger": await conn.execute( 'INSERT INTO pets ("user") VALUES ($1);', ctx.author.id ) await self.bot.log_transaction( ctx, from_=ctx.author.id, to=2, subject="money", data={"Amount": 5000}, conn=conn, ) await ctx.send( _( "You selected the class `{profession}`. **$5000** was taken off" " your balance." ).format(profession=_(profession)) )
"You don't own any loot items with the IDs: {itemids}" ).format( itemids=", ".join([str(loot_id) for loot_id in loot_ids]) ) ) reward = await self.bot.paginator.Choose( title=_("Select a reward for the {amount} items".format(amount=count)), footer=_("Do you want favor? {prefix}sacrifice instead").format( prefix=ctx.prefix ), return_index=True, entries=[f"**${value}**", _("**{value} XP**").format(value=value // 4)], ).paginate(ctx) reward = ["money", "xp"][reward] if reward == "xp": old_level = int(rpgtools.xptolevel(ctx.character_data["xp"])) value = value // 4 async with self.bot.pool.acquire() as conn: if len(loot_ids) == 0: await conn.execute('DELETE FROM loot WHERE "user"=$1;', ctx.author.id) else: await conn.execute( 'DELETE FROM loot WHERE "id"=ANY($1) AND "user"=$2;', loot_ids, ctx.author.id, ) await conn.execute( f'UPDATE profile SET "{reward}"="{reward}"+$1 WHERE "user"=$2;', value, ctx.author.id,
subject="adventure", data={ "Gold": gold, "Item": item["name"], # compare against loot names if necessary "Value": item["value"], }, ) # TODO: Embeds ftw await ctx.send( _("You have completed your adventure and received **${gold}** as well" " as a new item: **{item}**. Experience gained: **{xp}**."). format(gold=gold, item=item["name"], xp=xp)) new_level = int(rpgtools.xptolevel(ctx.character_data["xp"] + xp)) if current_level != new_level: await self.bot.process_levelup(ctx, new_level, current_level) @has_char() @has_adventure() @commands.command(brief=_("Cancels your current adventure.")) @locale_doc async def cancel(self, ctx): _("""Cancels your ongoing adventure and allows you to start a new one right away. You will not receive any rewards if you cancel your adventure.""" ) if not await ctx.confirm( _("Are you sure you want to cancel your current adventure?")): return await ctx.send( _("Did not cancel your adventure. The journey continues..."))
async def _class(self, ctx): _("""Change your primary or secondary class.""") if int(rpgtools.xptolevel(ctx.character_data["xp"])) >= 12: val = await self.bot.paginator.Choose( title=_("Select class to change"), entries=[_("Primary Class"), _("Secondary Class")], return_index=True, ).paginate(ctx) else: val = 0 embeds = [ discord.Embed( title=_("Warrior"), description= _("The tank class. Charge into battle with additional defense!\n+1 defense per evolution." ), color=self.bot.config.primary_colour, ), discord.Embed( title=_("Thief"), description=_( # xgettext: no-python-format "The sneaky money stealer...\nGet access to `{prefix}steal` to steal 10% of the target's money, if successful.\n+8% success chance per evolution." ).format(prefix=ctx.prefix), color=self.bot.config.primary_colour, ), discord.Embed( title=_("Mage"), description= _("Utilise powerful magic for stronger attacks.\n+1 damage per evolution." ), color=self.bot.config.primary_colour, ), discord.Embed( title=_("Ranger"), description= _("Item hunter and trainer of their very own pet.\nGet access to `{prefix}pet` to interact with your pet and let it get items for you.\n+3 minimum stat and +6 maximum stat per evolution." ).format(prefix=ctx.prefix), colour=self.bot.config.primary_colour, ), discord.Embed( title=_("Raider"), description= _("A strong warrior who gives their life for the fight against Zerekiel.\nEvery evolution boosts your raidstats by an additional 10%." ), colour=self.bot.config.primary_colour, ), discord.Embed( title=_("Ritualist"), description= _("A seer, a sacrificer and a follower.\nThe Ritualist devotes their life to the god they follow. For every evolution, their sacrifices are 5% more effective. They have twice the chance to get loot from adventures." ), colour=self.bot.config.primary_colour, ), ] choices = ["Warrior", "Thief", "Mage", "Ranger", "Raider", "Ritualist"] if await user_is_patron(self.bot, ctx.author): embeds.append( discord.Embed( title=_("Paragon"), description= _("Absorb the appreciation of the devs into your soul to power up.\n+1 damage and defense per evolution." ), color=self.bot.config.primary_colour, )) choices.append("Paragon") lines = [ self.bot.get_class_line(class_) for class_ in ctx.character_data["class"] ] for line in lines: for e in embeds: if _(line) == e.title: embeds.remove(e) try: choices.remove(line) except ValueError: pass profession = await self.bot.paginator.ChoosePaginator( extras=embeds, choices=choices).paginate(ctx) profession_ = self.bot.config.classes[profession][0] new_classes = copy(ctx.character_data["class"]) new_classes[val] = profession_ if not await ctx.confirm( _("You are about to select the `{profession}` class for yourself. {textaddon} Proceed?" ). format( textaddon= _("This **costs nothing**, but changing it later will cost **$5000**." ) if ctx.character_data["class"][val] == "No Class" else _("This will cost **$5000**."), profession=profession, )): return await ctx.send(_("Class selection cancelled.")) if ctx.character_data["class"][val] == "No Class": async with self.bot.pool.acquire() as conn: await conn.execute( 'UPDATE profile SET "class"=$1 WHERE "user"=$2;', new_classes, ctx.author.id, ) if profession == "Ranger": await conn.execute( 'INSERT INTO pets ("user") VALUES ($1);', ctx.author.id) await ctx.send( _("Your new class is now `{profession}`.").format( profession=_(profession))) else: if not await self.bot.has_money(ctx.author.id, 5000): await self.bot.reset_cooldown(ctx) return await ctx.send( _("You're too poor for a class change, it costs **$5000**." )) async with self.bot.pool.acquire() as conn: await conn.execute( 'UPDATE profile SET "class"=$1, "money"="money"-$2 WHERE "user"=$3;', new_classes, 5000, ctx.author.id, ) await conn.execute('DELETE FROM pets WHERE "user"=$1;', ctx.author.id) if profession == "Ranger": await conn.execute( 'INSERT INTO pets ("user") VALUES ($1);', ctx.author.id) await ctx.send( _("You selected the class `{profession}`. **$5000** was taken off your balance." ).format(profession=_(profession)))
async def profile(self, ctx, *, person: User = Author): _("""View someone's or your own profile.""") await ctx.trigger_typing() targetid = person.id async with self.bot.pool.acquire() as conn: profile = await conn.fetchrow( 'SELECT * FROM profile WHERE "user"=$1;', targetid ) if not profile: return await ctx.send( _("**{person}** does not have a character.").format(person=person) ) items = await self.bot.get_equipped_items_for(targetid) mission = await self.bot.get_adventure(targetid) guild = await conn.fetchval( 'SELECT name FROM guild WHERE "id"=$1;', profile["guild"] ) v1 = sum(i["damage"] for i in items) v2 = sum(i["armor"] for i in items) damage, armor = await self.bot.generate_stats( targetid, v1, v2, classes=profile["class"], race=profile["race"] ) extras = (damage - v1, armor - v2) sworddmg = f"{v1}{' (+' + str(extras[0]) + ')' if extras[0] else ''}" shielddef = f"{v2}{' (+' + str(extras[1]) + ')' if extras[1] else ''}" right_hand = "None Equipped" left_hand = "None Equipped" any_count = sum(1 for i in items if i["hand"] == "any") if len(items) == 2 and any_count == 1 and items[0]["hand"] == "any": items = [items[1], items[0]] for i in items: if i["hand"] == "both": right_hand, left_hand = i["name"], i["name"] elif i["hand"] == "left": left_hand = i["name"] elif i["hand"] == "right": right_hand = i["name"] elif i["hand"] == "any": if right_hand == "None Equipped": right_hand = i["name"] else: left_hand = i["name"] url = f"{self.bot.config.okapi_url}/api/genprofile" async with self.bot.trusted_session.post( url, data={ "name": profile["name"], "color": profile["colour"], "image": profile["background"], "race": profile["race"], "classes": profile["class"], "damage": sworddmg, "defense": shielddef, "swordName": right_hand, "shieldName": left_hand, "level": rpgtools.xptolevel(profile["xp"]), "money": f"{profile['money']}", "pvpWins": f"{profile['pvpwins']}", "marriage": i if ( i := await rpgtools.lookup( self.bot, profile["marriage"], return_none=True ) ) else _("Not Married"), "guild": guild or _("No Guild"), "god": profile["god"] or _("No God"), "icons": [ self.bot.get_class_line(c).lower() for c in profile["class"] ], "adventure": f"Adventure {mission[0]}\n{mission[1] if not mission[2] else _('Finished')}" if mission else _("No Mission"), }, ) as req: img = BytesIO(await req.read())
async def status(self, ctx): _("""Checks your adventure status.""") num, time, done = ctx.adventure_data if not done: return await ctx.send( _("""\ You are currently on an adventure with difficulty `{difficulty}`. Time until it completes: `{time_left}` Adventure name: `{adventure}`""").format( difficulty=num, time_left=time, adventure=self.bot.config.adventure_names[num], )) sword, shield = await self.bot.get_equipped_items_for(ctx.author) sword, shield = await self.bot.generate_stats( ctx.author, sword["damage"] if sword else 0, shield["armor"] if shield else 0, classes=ctx.character_data["class"], race=ctx.character_data["race"], ) luck_booster = await self.bot.get_booster(ctx.author, "luck") current_level = int(rpgtools.xptolevel(ctx.character_data["xp"])) luck_multiply = ctx.character_data["luck"] success = rpgtools.calcchance( sword, shield, num, current_level, luck_multiply, returnsuccess=True, booster=bool(luck_booster), ) await self.bot.delete_adventure(ctx.author) if not success: await self.bot.pool.execute( 'UPDATE profile SET "deaths"="deaths"+1 WHERE "user"=$1;', ctx.author.id) return await ctx.send(_("You died on your mission. Try again!")) gold = round( random.randint(20 * (num - 1) or 1, 60 * (num - 1) or 70) * luck_multiply) if await self.bot.get_booster(ctx.author, "money"): gold = int(gold * 1.25) xp = random.randint(250 * num, 500 * num) async with self.bot.pool.acquire() as conn: if random.randint(1, 10) < 10: minstat = round(num * luck_multiply) maxstat = round(5 + int(num * 1.5) * luck_multiply) item = await self.bot.create_random_item( minstat=minstat if minstat < 35 else 35, maxstat=maxstat if maxstat < 35 else 35, minvalue=round(num * luck_multiply), maxvalue=round(num * 50 * luck_multiply), owner=ctx.author, ) else: item = items.get_item() await conn.execute( 'INSERT INTO loot ("name", "value", "user") VALUES ($1, $2, $3);', item["name"], item["value"], ctx.author.id, ) if (guild := ctx.character_data["guild"]): await conn.execute( 'UPDATE guild SET "money"="money"+$1 WHERE "id"=$2;', int(gold / 10), guild, ) await conn.execute( 'UPDATE profile SET "money"="money"+$1, "xp"="xp"+$2, "completed"="completed"+1 WHERE "user"=$3;', gold, xp, ctx.author.id, ) if (partner := ctx.character_data["marriage"]): await conn.execute( 'UPDATE profile SET "money"="money"+($1*(1+"lovescore"/1000000)) WHERE "user"=$2;', int(gold / 2), partner, )
class Adventure(commands.Cog): def __init__(self, bot): self.bot = bot @has_char() @commands.command(aliases=["missions", "dungeons"]) @locale_doc async def adventures(self, ctx): _("""A list of all adventures with success rates, name and time it takes.""" ) sword, shield = await self.bot.get_equipped_items_for(ctx.author) all_dungeons = list(self.bot.config.adventure_times.keys()) level = rpgtools.xptolevel(ctx.character_data["xp"]) damage = sword["damage"] if sword else 0 defense = shield["armor"] if shield else 0 msg = await ctx.send(_("Loading images...")) chances = [] for adv in all_dungeons: success = rpgtools.calcchance( damage, defense, adv, int(level), ctx.character_data["luck"], returnsuccess=False, ) chances.append((success[0] - success[2], success[1] + success[2])) thing = functools.partial(rpgtools.makeadventures, chances) images = await self.bot.loop.run_in_executor(None, thing) await msg.delete() files = [ discord.File(img, filename=f"Adventure{idx + 1}.png") for idx, img in enumerate(images) ] pages = [ discord.Embed().set_image( url=f"attachment://Adventure{idx + 1}.png") for idx in range(len(images)) ] await self.bot.paginator.AdventurePaginator(embeds=pages, files=files).paginate(ctx) @has_char() @has_no_adventure() @commands.command(aliases=["mission", "a", "dungeon"]) @locale_doc async def adventure(self, ctx, dungeonnumber: IntFromTo(1, 20)): _("""Sends your character on an adventure.""") if dungeonnumber > int(rpgtools.xptolevel(ctx.character_data["xp"])): return await ctx.send( _("You must be on level **{level}** to do this adventure."). format(level=dungeonnumber)) time_booster = await self.bot.get_booster(ctx.author, "time") time = self.bot.config.adventure_times[dungeonnumber] if time_booster: time = time / 2 await self.bot.start_adventure(ctx.author, dungeonnumber, time) await ctx.send( _("Successfully sent your character out on an adventure. Use `{prefix}status` to see the current status of the mission." ).format(prefix=ctx.prefix)) @has_char() @has_no_adventure() @user_cooldown(7200) @commands.command() @locale_doc async def activeadventure(self, ctx): _("""Go out on a docile adventure controlled by reactions.""") if not await ctx.confirm( _("You are going to be in a labyrinth of size 15x15. There are enemies, treasures and hidden traps. Reach the exit in the bottom right corner for a huge extra bonus!\nAre you ready?\n\nTip: Use a silent channel for this, you may want to read all the messages I will send." )): return msg = await ctx.send(_("**Generating a maze...**")) maze = Maze.generate(15, 15) direction_emojis = { "n": "\U00002b06", "e": "\U000027a1", "s": "\U00002b07", "w": "\U00002b05", } direction_emojis_inverse = { val: key for key, val in direction_emojis.items() } direction_names = { "n": _("North"), "e": _("East"), "s": _("South"), "w": _("West"), } all_directions = set(direction_names.keys()) x = 0 y = 0 sword, shield = await self.bot.get_equipped_items_for(ctx.author) attack, defense = await self.bot.generate_stats( ctx.author, float(sword["damage"] if sword else 0), float(shield["armor"] if shield else 0), ) attack = int(attack) defense = int(defense) hp = 1000 def free(cell): return all_directions - cell.walls def fmt_direction(direction): return direction_names[direction] def player_pos(): return maze[x, y] def is_at_end(): return x == 14 and y == 14 def move(x, y, direction): if direction == "n": y = y - 1 elif direction == "e": x = x + 1 elif direction == "s": y = y + 1 elif direction == "w": x = x - 1 return x, y async def wait_for_move(): possible = free(player_pos()) needed = [direction_emojis[direction] for direction in possible] try: await msg.clear_reactions() except discord.Forbidden: for r in msg.reactions: if str(r.emoji) not in needed: await msg.remove_reaction(r, ctx.guild.me) for r in needed: if r not in [str(r.emoji) for r in msg.reactions]: await msg.add_reaction(r) else: for direction in possible: await msg.add_reaction(direction_emojis[direction]) def check(r, u): return (u == ctx.author and r.message.id == msg.id and direction_emojis_inverse.get(str(r.emoji), None) in possible) r, u = await self.bot.wait_for("reaction_add", check=check, timeout=30) return direction_emojis_inverse[str(r.emoji)] async def update(): text = "" pos = player_pos() for direction in ("n", "e", "s", "w"): side = fmt_direction(direction) fake_x, fake_y = move(x, y, direction) fake_cell = maze[fake_x, fake_y] if direction in pos.walls: text2 = _("To the {side} is a wall.").format(side=side) elif fake_cell.enemy: text2 = _("To the {side} is an enemy.").format(side=side) elif fake_cell.treasure: text2 = _("To the {side} is a treasure.").format(side=side) else: text2 = _("To the {side} is a floor.").format(side=side) text = f"{text}\n{text2}" text2 = _("You are on {hp} HP").format(hp=hp) text = f"{text}\n\n{text2}" await msg.edit(content=text) async def handle_specials(hp): cell = player_pos() if cell.trap: damage = random.randint(30, 120) await ctx.send( _("You stepped on a trap and took {damage} damage!"). format(damage=damage)) cell.trap = False # Remove the trap return hp - damage elif cell.treasure: val = attack + defense money = random.randint(val, val * 25) await self.bot.pool.execute( 'UPDATE profile SET "money"="money"+$1 WHERE "user"=$2;', money, ctx.author.id, ) await ctx.send( _("You found a treasure with **${money}** inside!").format( money=money)) cell.treasure = False elif cell.enemy: def to_bar(hp): fields = hp // 100 return f"[{'▯' * fields}{'▮' * (10 - fields)}]" def is_valid_move(r, u): return (r.message.id == msg.id and u == ctx.author and str(r.emoji) in emojis) emojis = { "\U00002694": "attack", "\U0001f6e1": "defend", "\U00002764": "recover", } enemy = _("Enemy") enemy_hp = 1000 heal_hp = round(attack * 0.25) or 1 min_dmg = round(attack * 0.5) max_dmg = round(attack * 1.5) status1 = _("The Fight started") status2 = "" await msg.clear_reactions() for emoji in emojis: await msg.add_reaction(emoji) while enemy_hp > 0 and hp > 0: await msg.edit(content=f"""\ ``` {ctx.author.name}{" " * (38 - len(ctx.author.name) - len(enemy))}{enemy} ------------++++++++++++++------------ {to_bar(hp)} {hp} {enemy_hp} {to_bar(enemy_hp)} {status1} {status2} ```""") r, u = await self.bot.wait_for("reaction_add", check=is_valid_move, timeout=30) try: await msg.remove_reaction(r, u) except discord.Forbidden: pass enemy_move = random.choice(["attack", "defend", "recover"]) player_move = emojis[str(r.emoji)] if enemy_move == "recover": enemy_hp += heal_hp enemy_hp = 1000 if enemy_hp > 1000 else enemy_hp status1 = _("The Enemy healed themselves for {hp} HP" ).format(hp=heal_hp) if player_move == "recover": hp += heal_hp hp = 1000 if hp > 1000 else hp status2 = _("You healed yourself for {hp} HP").format( hp=heal_hp) if (enemy_move == "attack" and player_move == "defend") or (enemy_move == "defend" and player_move == "attack"): status1 = _("Attack blocked.") status2 = "" if enemy_move == "attack" and player_move != "defend": eff = random.randint(min_dmg, max_dmg) hp -= eff status1 = _("The Enemy hit you for {dmg} damage" ).format(dmg=eff) if player_move == "attack" and enemy_move != "defend": enemy_hp -= attack status2 = _("You hit the enemy for {dmg} damage" ).format(dmg=attack) if enemy_hp <= 0: cell.enemy = False return hp while not is_at_end(): await update() try: direction = await wait_for_move() except asyncio.TimeoutError: return await msg.edit(content=_("Timed out.")) x, y = move(x, y, direction) # Python namespacing sucks, to be honest try: hp = await handle_specials(hp ) # Should've used a class for this except asyncio.TimeoutError: return await msg.edit(content=_("Timed out.")) if hp <= 0: return await ctx.send(_("You died.")) val = attack + defense money = random.randint(val * 5, val * 100) await self.bot.pool.execute( 'UPDATE profile SET "money"="money"+$1 WHERE "user"=$2;', money, ctx.author.id, ) await ctx.send( _("You have reached the exit and were rewarded **${money}** for getting out!" ).format(money=money)) @has_char() @has_adventure() @commands.command(aliases=["s"]) @locale_doc async def status(self, ctx): _("""Checks your adventure status.""") num, time, done = ctx.adventure_data if not done: return await ctx.send( _("""\ You are currently on an adventure with difficulty `{difficulty}`. Time until it completes: `{time_left}` Adventure name: `{adventure}`""").format( difficulty=num, time_left=time, adventure=self.bot.config.adventure_names[num], )) sword, shield = await self.bot.get_equipped_items_for(ctx.author) sword, shield = await self.bot.generate_stats( ctx.author, sword["damage"] if sword else 0, shield["armor"] if shield else 0, classes=ctx.character_data["class"], race=ctx.character_data["race"], ) luck_booster = await self.bot.get_booster(ctx.author, "luck") current_level = int(rpgtools.xptolevel(ctx.character_data["xp"])) luck_multiply = ctx.character_data["luck"] success = rpgtools.calcchance( sword, shield, num, current_level, luck_multiply, returnsuccess=True, booster=bool(luck_booster), ) await self.bot.delete_adventure(ctx.author) if not success: await self.bot.pool.execute( 'UPDATE profile SET "deaths"="deaths"+1 WHERE "user"=$1;', ctx.author.id) return await ctx.send(_("You died on your mission. Try again!")) gold = round( random.randint(20 * (num - 1) or 1, 60 * (num - 1) or 70) * luck_multiply) if await self.bot.get_booster(ctx.author, "money"): gold = int(gold * 1.25) xp = random.randint(250 * num, 500 * num) async with self.bot.pool.acquire() as conn: if random.randint(1, 10) < 10: minstat = round(num * luck_multiply) maxstat = round(5 + int(num * 1.5) * luck_multiply) item = await self.bot.create_random_item( minstat=minstat if minstat < 35 else 35, maxstat=maxstat if maxstat < 35 else 35, minvalue=round(num * luck_multiply), maxvalue=round(num * 50 * luck_multiply), owner=ctx.author, ) else: item = items.get_item() await conn.execute( 'INSERT INTO loot ("name", "value", "user") VALUES ($1, $2, $3);', item["name"], item["value"], ctx.author.id, ) if (guild := ctx.character_data["guild"]): await conn.execute( 'UPDATE guild SET "money"="money"+$1 WHERE "id"=$2;', int(gold / 10), guild, ) await conn.execute( 'UPDATE profile SET "money"="money"+$1, "xp"="xp"+$2, "completed"="completed"+1 WHERE "user"=$3;', gold, xp, ctx.author.id, ) if (partner := ctx.character_data["marriage"]): await conn.execute( 'UPDATE profile SET "money"="money"+($1*(1+"lovescore"/1000000)) WHERE "user"=$2;', int(gold / 2), partner, ) await ctx.send( _("You have completed your adventure and received **${gold}** as well as a new item: **{item}**. Experience gained: **{xp}**." ).format(gold=gold, item=item["name"], xp=xp)) new_level = int(rpgtools.xptolevel(ctx.character_data["xp"] + xp)) if current_level == new_level: return if (reward := random.choice(["crates", "money", "item"])) == "crates": if new_level < 6: column = "crates_common" amount = new_level reward_text = f"**{amount}** <:CrateCommon:598094865666015232>" elif new_level < 10: column = "crates_uncommon" amount = round(new_level / 2) reward_text = f"**{amount}** <:CrateUncommon:598094865397579797>" elif new_level < 15: column = "crates_rare" amount = 2 reward_text = "**2** <:CrateRare:598094865485791233>" elif new_level < 20: column = "crates_rare" amount = 3 reward_text = "**3** <:CrateRare:598094865485791233>" else: column = "crates_magic" amount = 1 reward_text = "**1** <:CrateMagic:598094865611358209>" await self.bot.pool.execute( f'UPDATE profile SET {column}={column}+$1 WHERE "user"=$2;', amount, ctx.author.id, )
async def profile2(self, ctx, *, target: User = Author): _("""View someone's profile, not image based.""") rank_money, rank_xp = await self.bot.get_ranks_for(target) items = await self.bot.get_equipped_items_for(target) async with self.bot.pool.acquire() as conn: p_data = await conn.fetchrow( 'SELECT * FROM profile WHERE "user"=$1;', target.id ) if not p_data: return await ctx.send( _("**{target}** does not have a character.").format(target=target) ) mission = await self.bot.get_adventure(target) guild = await conn.fetchval( 'SELECT name FROM guild WHERE "id"=$1;', p_data["guild"] ) try: colour = discord.Colour.from_rgb(*rpgtools.hex_to_rgb(p_data["colour"])) except ValueError: colour = 0x000000 if mission: timeleft = str(mission[1]).split(".")[0] if not mission[2] else "Finished" right_hand = None left_hand = None any_count = sum(1 for i in items if i["hand"] == "any") if len(items) == 2 and any_count == 1 and items[0]["hand"] == "any": items = [items[1], items[0]] for i in items: if i["hand"] == "both": right_hand, left_hand = i, i elif i["hand"] == "left": left_hand = i elif i["hand"] == "right": right_hand = i elif i["hand"] == "any": if right_hand is None: right_hand = i else: left_hand = i right_hand = ( f"{right_hand['name']} - {right_hand['damage'] + right_hand['armor']}" if right_hand else _("None Equipped") ) left_hand = ( f"{left_hand['name']} - {left_hand['damage'] + left_hand['armor']}" if left_hand else _("None Equipped") ) level = rpgtools.xptolevel(p_data["xp"]) em = discord.Embed(colour=colour, title=f"{target}: {p_data['name']}") em.set_thumbnail(url=target.avatar_url) em.add_field( name=_("General"), value=_( """\ **Money**: `${money}` **Level**: `{level}` **Class**: `{class_}` **Race**: `{race}` **PvP Wins**: `{pvp}` **Guild**: `{guild}`""" ).format( money=p_data["money"], level=level, class_="/".join(p_data["class"]), race=p_data["race"], pvp=p_data["pvpwins"], guild=guild, ), ) em.add_field( name=_("Ranks"), value=_("**Richest**: `{rank_money}`\n**XP**: `{rank_xp}`").format( rank_money=rank_money, rank_xp=rank_xp ), ) em.add_field( name=_("Equipment"), value=_("Right Hand: {right_hand}\nLeft Hand: {left_hand}").format( right_hand=right_hand, left_hand=left_hand ), ) if mission: em.add_field(name=_("Mission"), value=f"{mission[0]} - {timeleft}") await ctx.send(embed=em)
async def adventure(self, ctx): _("""Starts a guild adventure.""") if await self.bot.get_guild_adventure(ctx.character_data["guild"]): await self.bot.reset_guild_cooldown(ctx) return await ctx.send( _( "Your guild is already on an adventure! Use `{prefix}guild status` to view how long it still lasts." ).format(prefix=ctx.prefix) ) guild = await self.bot.pool.fetchrow( 'SELECT * FROM guild WHERE "id"=$1;', ctx.character_data["guild"] ) msg = await ctx.send( _( "{author} seeks a guild adventure for **{guild}**! React to join! Unlimited players can join in the next minute. The minimum of players required is 3." ).format(author=ctx.author.mention, guild=guild["name"]) ) await msg.add_reaction("\U00002694") joined = [ctx.author] difficulty = int(rpgtools.xptolevel(ctx.character_data["xp"])) started = False def apply(r, u): return ( r.message.id == msg.id and str(r.emoji) == "\U00002694" and u not in joined and not u.bot ) while not started: try: r, u = await self.bot.wait_for("reaction_add", check=apply, timeout=30) user = await self.bot.pool.fetchrow( 'SELECT guild, xp FROM profile WHERE "user"=$1;', u.id ) if user and user["guild"] == guild["id"]: difficulty += int(rpgtools.xptolevel(user["xp"])) joined.append(u) await ctx.send( _("Alright, {user}, you have been added.").format( user=u.mention ) ) else: await ctx.send(_("You aren't in their guild.")) except asyncio.TimeoutError: if len(joined) < 3: return await ctx.send( _( "You didn't get enough other players for the guild adventure." ) ) started = True time = timedelta(hours=difficulty * 0.5) await self.bot.start_guild_adventure(guild["id"], difficulty, time) await ctx.send( _( """ Guild adventure for **{guild}** started! Participants: {participants} Difficulty is **{difficulty}** Time it will take: **{time}** """ ).format( guild=guild["name"], participants=", ".join([m.mention for m in joined]), difficulty=difficulty, time=time, ) )