def craft(userid, recipe, n=1): """Crafts (a given number of) an item.""" try: recipeid = find_by_name(recipe) except KeyError: return f'Error: cannot find recipe that crafts {recipe}.' name = items.get_attr(recipeid) artisan_level = users.xp_to_level(users.read_user(userid, key=users.ARTISAN_XP_KEY)) artisan_req = get_attr(recipeid, key=LEVEL_REQ_KEY) if artisan_level < artisan_req: return f'Error: {name} has a gathering requirement ({artisan_req}) ' \ f'higher than your artisan level ({artisan_level}).' inputs = get_attr(recipeid) loot = [] for itemid in list(inputs.keys()): if users.item_in_inventory(userid, itemid, number=n * inputs[itemid]): loot.extend((n * inputs[itemid]) * [itemid]) else: return f'Error: you do not have enough items to make {n} {items.add_plural(recipeid)} ' \ f'({n * inputs[itemid]} {items.add_plural(itemid)}).' users.update_inventory(userid, loot, remove=True) users.update_inventory(userid, n * [recipeid]) xp = n * items.get_attr(recipeid, key=items.XP_KEY) users.update_user(userid, xp, key=users.ARTISAN_XP_KEY) xp_formatted = '{:,}'.format(xp) return f'Successfully crafted {n} {items.add_plural(recipeid)}! You have also gained {xp_formatted} artisan xp!'
async def cancel(self, ctx): """Cancels your current action.""" try: task = adv.get_adventure(ctx.author.id) adventureid = task[0] if adventureid == '0': if users.item_in_inventory(ctx.author.id, '291', '1'): users.update_inventory(ctx.author.id, ['291'], remove=True) adv.remove(ctx.author.id) out = 'Slayer task cancelled!' else: out = 'Error: You do not have a reaper token.' elif adventureid == '1': adv.remove(ctx.author.id) out = 'Killing session cancelled!' elif adventureid == '2': adv.remove(ctx.author.id) out = 'Quest cancelled!' elif adventureid == '3': adv.remove(ctx.author.id) out = 'Gather cancelled!' else: out = f'Error: Invalid Adventure ID {adventureid}' except NameError: out = 'You are not currently doing anything.' await ctx.send(out)
def buy(userid, item, number): """Buys (a given amount) of an item and places it in the user's inventory.""" try: itemid = find_by_name(item) number = int(number) except KeyError: return f'Error: {item} is not an item.' except ValueError: return f'Error: {number} is not a number.' item_name = get_attr(itemid) if item_in_shop(itemid): items = open_shop() if int(items[itemid]) in users.get_completed_quests(userid) or int( items[itemid]) == 0: value = get_attr(itemid, key=VALUE_KEY) cost = 4 * number * value if users.item_in_inventory(userid, "0", cost): users.update_inventory(userid, [itemid] * number) users.update_inventory(userid, (4 * number * value) * ["0"], remove=True) value_formatted = '{:,}'.format(4 * value * number) return f'{number} {item_name} bought for {value_formatted} coins!' else: return f'You do not have enough coins to buy this item. ({cost} coins)' else: return 'Error: You do not have the requirements to buy this item.' else: return f'Error: {item_name} not in inventory or you do not have at least {number} in your inventory.'
def claim(userid, itemname, number): try: itemid = find_by_name(itemname) except KeyError: return f"Error: {itemname} is not an item." if not users.item_in_inventory(userid, itemid, number): return f'You do not have {add_plural(number, itemid)} in your inventory.' out = '__**CLAIM**__ :moneybag:\n' if int(itemid) == 402: out += 'You have received:\n' gems = {25: 4, 26: 16, 27: 64, 28: 128, 463: 1024, 465: 2048} loot = [] for _ in range(number): while True: gem_type = random.choice(gems.keys(), 1)[0] if random.randint(1, gems[gem_type]) == 1: loot.append(gem_type) break users.update_inventory(userid, loot) loot_counter = Counter(loot) for gemid in loot_counter.keys(): out += f'{add_plural(loot_counter[gemid], gemid)}\n' out += f'from your {add_plural(number, itemid)}.' users.update_inventory(userid, number * [itemid], remove=True) else: out += f'{get_attr(itemid)} is not claimable.' return out
def has_item_reqs(userid, questid): """Checks if a user can do a quest based on based on whether they have the required items in their inventory.""" item_reqs = get_attr(questid, key=ITEM_REQ_KEY) count = 0 for itemid in item_reqs: if users.item_in_inventory(userid, itemid, item_reqs[itemid]): count += 1 return count == len(item_reqs)
def cook(userid, food, n=1): """Cooks (a given number of) an item.""" try: foodid = find_by_name(food, cook=True) except KeyError: return f'Cannot find food called {food} that can be cooked.' except TypeError: return f'Cannot cook {food}.' name = items.get_attr(foodid) cooking_level = users.get_level(userid, key=users.COOK_XP_KEY) cooking_req = get_attr(foodid, key=COOKING_REQ_KEY) if cooking_level < cooking_req: return f'{name} has a cooking requirement ({cooking_req}) higher than your cooking level ({cooking_level}).' burn_chance = calc_burn(userid, foodid) num_cooked = 0 if burn_chance == 0: num_cooked = n else: for _ in range(n): if random.randint(1, 100) > burn_chance: num_cooked += 1 inputs = get_attr(foodid) food_input = [] for itemid in list(inputs.keys()): if users.item_in_inventory(userid, itemid, number=num_cooked * inputs[itemid]): food_input.extend((num_cooked * inputs[itemid]) * [itemid]) else: return f'Error: you do not have enough items to make {items.add_plural(n, foodid)} ' \ f'({items.add_plural(n * inputs[itemid], itemid)}).' users.update_inventory(userid, food_input, remove=True) users.update_inventory(userid, num_cooked * [foodid]) users.update_inventory(userid, (n - num_cooked) * ['469']) xp = XP_FACTOR * num_cooked * items.get_attr(foodid, key=items.XP_KEY) users.update_user(userid, xp, key=users.COOK_XP_KEY) level_after = users.xp_to_level(users.read_user(userid, users.COOK_XP_KEY)) xp_formatted = '{:,}'.format(xp) out = f'After cooking {items.add_plural(n, foodid)}, you successfully cook ' \ f'{num_cooked} and burn {n - num_cooked}! ' \ f'You have also gained {xp_formatted} cooking xp! ' if level_after > cooking_level: out += f'You have also gained {level_after - cooking_level} cooking levels!' return out
def craft(userid, recipe, n=1): """Crafts (a given number of) an item.""" try: recipeid = find_by_name(recipe.lower()) except KeyError: return f'Cannot find recipe that crafts {recipe}.' except TypeError: return f'Cannot craft {recipe}.' name = items.get_attr(recipeid) artisan_level = users.get_level(userid, key=users.ARTISAN_XP_KEY) artisan_req = get_attr(recipeid, key=ARTISAN_REQ_KEY) if artisan_level < artisan_req: return f'Error: {name} has a artisan requirement ({artisan_req}) ' \ f'higher than your artisan level ({artisan_level}).' inputs = get_attr(recipeid) recipe_input = [] for itemid in list(inputs.keys()): if users.item_in_inventory(userid, itemid, number=n * inputs[itemid]): recipe_input.extend((n * inputs[itemid]) * [itemid]) else: return f'Error: you do not have enough items to make {items.add_plural(n, recipeid)} ' \ f'({items.add_plural(n * inputs[itemid], itemid)}).' bonus = 0 if artisan_level == 99: for _ in range(n): if random.randint(1, 20) == 1: bonus += 1 equipment = users.read_user(userid, users.EQUIPMENT_KEY) goldsmith_bonus = 2 if equipment['9'] == '494' and recipeid == '59' else 1 users.update_inventory(userid, recipe_input, remove=True) users.update_inventory(userid, (n + bonus) * [recipeid]) xp = XP_FACTOR * goldsmith_bonus * n * items.get_attr(recipeid, key=items.XP_KEY) users.update_user(userid, xp, key=users.ARTISAN_XP_KEY) level_after = users.xp_to_level( users.read_user(userid, users.ARTISAN_XP_KEY)) xp_formatted = '{:,}'.format(xp) out = f'Successfully crafted {items.add_plural(n, recipeid)}! You have also gained {xp_formatted} artisan xp! ' if bonus > 0: out += f'Due to your 99 artisan perk, you have also created an extra {items.add_plural(bonus, recipeid)}! ' if level_after > artisan_level: out += f'You have also gained {level_after - artisan_level} artisan levels!' return out
def start_clue(userid, difficulty): """Starts a clue scroll.""" out = f'{CLUE_HEADER}' if not adv.is_on_adventure(userid): scrollid = str(EASY_CLUE_SCROLL_ID + difficulty - 1) if not users.item_in_inventory(userid, scrollid): return f'Error: you do not have a {DIFFICULTY[difficulty]} clue scroll in your inventory.' users.update_inventory(userid, [scrollid], remove=True) length = math.floor(calc_length(userid, difficulty) / 60) clue = adv.format_line(4, userid, adv.get_finish_time(length * 60), difficulty, length) adv.write(clue) out += f'You are now doing a {DIFFICULTY[difficulty]} clue scroll for {length} minutes.' else: out = adv.print_adventure(userid) out += adv.print_on_adventure_error('clue scroll') return out
def sell(userid, item, number): """Sells (a given amount) of an item from a user's inventory.""" try: itemid = find_by_name(item) number = int(number) except KeyError: return f'Error: {item} is not an item.' except ValueError: return f'Error: {number} is not a number.' item_name = get_attr(itemid) if users.item_in_inventory(userid, itemid, number=number): value = get_attr(itemid, key=VALUE_KEY) users.update_inventory(userid, [itemid] * number, remove=True) ac.update_account(userid, number * value) value_formatted = '{:,}'.format(value * number) return f'{number} {item_name} sold for G${value_formatted}!' else: return f'Error: {item_name} not in inventory or you do not have at least {number} in your inventory.'
def print_recipe(userid, recipe): """Prints details related to a particular recipe.""" try: recipeid = find_by_name(recipe) except KeyError: return f'Error: cannot find recipe that crafts {recipe}.' out = f'{CRAFT_HEADER}'\ f'**Name**: {items.get_attr(recipeid).title()}\n'\ f'**Artisan Requirement**: {get_attr(recipeid, key=ARTISAN_REQ_KEY)}\n\n'\ f'**Inputs**:\n' inputs = get_attr(recipeid, key=INPUTS_KEY) for inputid in list(inputs.keys()): if users.item_in_inventory(userid, inputid, inputs[inputid]): out += f'~~{items.add_plural(inputs[inputid], inputid)}~~\n' else: out += f'{items.add_plural(inputs[inputid], inputid)}\n' return out
def drink(userid, name): try: itemid = find_by_name(name) except KeyError: return f'Error: {name} does not exist.' item_name = get_attr(itemid) is_pot = get_attr(itemid, key=POT_KEY) if is_pot: if users.item_in_inventory(userid, itemid): users.update_inventory(userid, [itemid], remove=True) equipment = users.read_user(userid, key=users.EQUIPMENT_KEY) equipment['15'] = str(itemid) users.update_user(userid, equipment, key=users.EQUIPMENT_KEY) else: return f"You do not have any {add_plural(0, itemid)} in your inventory." else: return f"{item_name} isn't a potion!" out = f'You drank the {item_name}! Your stats will be increased for your next adventure.' return out
def print_details(userid, questid): """Prints the details of a quest.""" try: name = get_attr(questid) except KeyError: return f'Error: There is no quest with number {questid}.' out = f'{QUEST_HEADER}' out += f'**{questid}: {name}**\n' out += f'*{get_attr(questid, key=DESCRIPTION_KEY)}*\n\n' out += f'**Base Time**: {get_attr(questid, key=TIME_KEY)} minutes.\n' if int(questid) in users.get_completed_quests(userid): out += f'\n*{get_attr(questid, key=SUCCESS_KEY)}*\n' quest_reqs = get_attr(questid, key=QUEST_REQ_KEY) if len(quest_reqs) > 0: user_quests = set(users.get_completed_quests(userid)) out += f'\n**Quest Requirements**:\n' for quest_req in quest_reqs: if quest_req in user_quests: out += f'~~{quest_req}. {get_attr(quest_req)}~~\n' else: out += f'{quest_req}. {get_attr(quest_req)}\n' item_reqs = get_attr(questid, key=ITEM_REQ_KEY) if len(item_reqs) > 0: out += f'\n**Item Requirements**:\n' for itemid in item_reqs: if users.item_in_inventory(userid, itemid, item_reqs[itemid]): out += f'~~{items.add_plural(item_reqs[itemid], itemid)}~~\n' else: out += f'{items.add_plural(item_reqs[itemid], itemid)}\n' out += f'\nIf you would like to do this quest, type `~quest start {questid}`.' return out
async def deathmatch(self, ctx, opponent='rand', bet=None): """Allows users to duke it out in a 1v1 match.""" if ctx.channel.id == DUEL_CHANNEL or ctx.channel.id in GENERAL_CHANNELS: author_name = get_display_name(ctx.author) if bet is not None: if users.read_user(ctx.author.id, key=users.IRONMAN_KEY): await ctx.send('Ironmen cannot start staked deathmatches.') return try: bet = users.parse_int(bet) except ValueError: await ctx.send(f'{bet} does not represent a valid number.') bet_formatted = '{:,}'.format(bet) if not users.item_in_inventory(ctx.author.id, '0', bet): await ctx.send(f'You do not have {bet_formatted} coins.') return try: opponent_member = parse_name(ctx.message.guild, opponent) except NameError: await ctx.send(f'{opponent} not found in server.') return except AmbiguousInputError as members: await ctx.send( f'Input {opponent} can refer to multiple people ({members})' ) return if opponent_member.id == ctx.author.id: await ctx.send('You cannot fight yourself.') return if users.read_user(opponent_member.id, key=users.IRONMAN_KEY): await ctx.send( 'You cannot start a staked deathmatch with an ironman.' ) return bet_formatted = '{:,}'.format(bet) opponent_name = get_display_name(opponent_member) if not users.item_in_inventory(opponent_member.id, '0', bet): await ctx.send( f'{opponent_name} does not have {bet_formatted} coins.' ) return users.update_inventory(ctx.author.id, bet * ['0'], remove=True) out = f'Deathmatch set up between {author_name} and {opponent_member.mention} with bet ' \ f'{bet_formatted} coins! To confirm this match, {opponent_name} must react to ' \ f'this message with a :thumbsup: in the next minute. If a minute passes or if the ' \ f'challenger reacts to this message, the deathmatch will be cancelled and the deposit ' \ f'refunded.' msg = await ctx.send(out) await msg.add_reaction('\N{THUMBS UP SIGN}') while True: try: reaction, user = await self.bot.wait_for( 'reaction_add', timeout=60) if str(reaction.emoji ) == '👍' and user == opponent_member: users.update_inventory(opponent_member.id, bet * ['0'], remove=True) deathmatch_messages = dm.do_deathmatch( ctx.author, opponent_member, bet=bet_formatted) for message in deathmatch_messages[:-1]: await msg.edit(content=message) await asyncio.sleep(1) users.update_inventory(deathmatch_messages[-1], 2 * bet * ['0']) return elif user == ctx.author: users.update_inventory(ctx.author.id, bet * ['0']) await msg.edit( content= f'{author_name} has declined their challenge and ' f'the deposit of {bet_formatted} coins has been returned.' ) return except asyncio.TimeoutError: users.update_inventory(ctx.author.id, bet * ['0']) await msg.edit( content= f'One minute has passed and the deathmatch has been cancelled. ' f'The deposit of {bet_formatted} coins has been returned.' ) return else: try: opponent_member = parse_name(ctx.message.guild, opponent) except NameError: await ctx.send(f'{opponent} not found in server.') return except AmbiguousInputError as members: await ctx.send( f'Input {opponent} can refer to multiple people ({members})' ) return msg = await ctx.send(dm.DEATHMATCH_HEADER) deathmatch_messages = dm.do_deathmatch(ctx.author, opponent_member) for message in deathmatch_messages[:-1]: await msg.edit(content=message) await asyncio.sleep(1)
async def trade(self, ctx, *args): """Trades to a person a number of a given object for a given price.""" if ctx.channel.id == SHOP_CHANNEL or ctx.channel.id in GENERAL_CHANNELS: if users.read_user(ctx.author.id, key=users.IRONMAN_KEY): await ctx.send('Ironmen cannot trade.') return if len(args) < 4: await ctx.send( 'Arguments missing. Syntax is `~trade [name] [number] [item] [offer]`.' ) return name = args[0] for member in ctx.guild.members: if name.lower() in member.name.lower(): name_member = member break else: await ctx.send(f'{name} not found in server.') return if users.read_user(name_member.id, key=users.IRONMAN_KEY): await ctx.send('You cannot trade with an ironman.') return try: number = int(args[1]) except ValueError: await ctx.send(f'{args[1]} is not a valid number.') return try: offer = users.parse_int(args[-1]) itemid = items.find_by_name(' '.join(args[2:-1])) except ValueError: await ctx.send(f'{args[-1]} is not a valid offer.') return except KeyError: await ctx.send(f"{' '.join(args[2:-1])} is not a valid item.") return if not users.item_in_inventory(ctx.author.id, itemid, number): await ctx.send( f'You do not have {items.add_plural(number, itemid)} in your inventory.' ) return if not items.is_tradable(itemid): await ctx.send( f'You can not trade this item. ({items.get_attr(itemid)})') return if not users.item_in_inventory(name_member.id, "0", offer): await ctx.send( f'{get_display_name(name_member)} does not have enough gold to buy this many items.' ) return name = get_display_name(ctx.author) offer_formatted = '{:,}'.format(offer) out = f'{items.SHOP_HEADER}{name.title()} wants to sell {name_member.mention} ' \ f'{items.add_plural(number, itemid)} for {offer_formatted} coins. To accept this offer, reply ' \ f'to this post with a :thumbsup:. Otherwise, this offer will expire in one minute.' msg = await ctx.send(out) await msg.add_reaction('\N{THUMBS UP SIGN}') while True: try: reaction, user = await self.bot.wait_for('reaction_add', timeout=60) if str( reaction.emoji ) == '👍' and user == name_member and reaction.message.id == msg.id: price = offer * ["0"] users.update_inventory(name_member.id, price, remove=True) users.update_inventory(ctx.author.id, price) loot = number * [itemid] users.update_inventory(ctx.author.id, loot, remove=True) users.update_inventory(name_member.id, loot) buyer_name = get_display_name(name_member) await ctx.send( f'{items.SHOP_HEADER}{name.title()} successfully sold ' f'{items.add_plural(number, itemid)} to {buyer_name} for ' f'{offer_formatted} coins!') return except asyncio.TimeoutError: await msg.edit( content= f'One minute has passed and your offer has been cancelled.' ) return