def start_quest(guildid, channelid, user: User, questid): """Assigns a user a slayer task provided they are not in the middle of another adventure.""" from miniscape import adventures as adv if adv.is_on_adventure(user.id): out = adv.print_adventure(user.id) out += adv.print_on_adventure_error('quest') return out try: quest = Quest.objects.get(id=questid) except ObjectDoesNotExist: return f"Error: quest number {questid} does not refer to any quest." if quest in user.completed_quests_list: return "Error: you have already done this quest." if not user.has_quest_req_for_quest(quest): return "Error: you have not completed the required quests to do this quest." if not user.has_items_for_quest(quest): return "Error: you do not have all the required items to start this quest." out = QUEST_HEADER chance = calc_chance(user, quest) quest_length = calc_length(user, quest) quest_adv = adv.format_line(2, user.id, adv.get_finish_time(quest_length), guildid, channelid, questid, chance) adv.write(quest_adv) out += print_quest(quest, quest_length, chance) return out
class MiniscapeBotContext(commands.Context): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) try: self.user_object = User.objects.get(id=self.author.id) except ObjectDoesNotExist: self.user_object = User( id=self.author.id, name=self.author.name + '#' + self.author.discriminator, nick=self.author.nick if self.author.nick is not None else '') self.user_object.save() return # Update our user's name/nick if they differ or don't exist # (if they don't exist they == '') # TODO: Fix this, it always triggers reeeeee name_match = self.user_object.name == (self.author.name + '#' + self.author.discriminator) nick_match = self.user_object.nick == self.author.nick and self.author.nick if not name_match or not nick_match: self.user_object.nick = self.author.nick if self.author.nick is not None else '' self.user_object.name = self.author.name + '#' + self.author.discriminator self.user_object.save()
def cook(user: User, food, n=1): """Cooks (a given number of) an item.""" item: Item = Item.find_by_name_or_nick(food) if not item: return f'Cannot find food called {food} that can be cooked.' name = item.name cooking_level = user.cook_level cooking_req = item.level if cooking_level < cooking_req: return f'{name} has a cooking requirement ({cooking_req}) higher than your cooking level ({cooking_level}).' rr = RecipeRequirement.objects.filter(recipe__creates=item) if not rr: return "You cannot cook {name}." rr = rr[0] if user.has_item_amount_by_item(rr.item, rr.amount*n): negative_loot = {rr.item: rr.amount*n} else: return f'You do not have enough items to make {rr.item.pluralize(n)} ' \ f'({rr.item.pluralize(rr.amount * n)}).' burn_chance = calc_burn(user, item) num_cooked = 0 bonus = 0 if burn_chance == 0: num_cooked = n else: for _ in range(n): if random.randint(1, 100) > burn_chance: num_cooked += 1 if cooking_level == 99: bonus = random.randint(0, round(n / 20)) user.update_inventory(negative_loot, remove=True) user.update_inventory({item: num_cooked}) user.update_inventory({BURNT_FOOD: n - num_cooked}) xp = XP_FACTOR * num_cooked * item.xp user.cook_xp += xp user.save() level_after = user.cook_level xp_formatted = '{:,}'.format(xp) out = f'After cooking {item.pluralize(n)}, you successfully cook ' \ f'{num_cooked} and burn {n - num_cooked}! ' if bonus > 0: out += f'Due to your cooking perk, you have also cooked an additional {item.pluralize(n)}! ' out += 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 unequip_item(author: User, item: str): """Takes an item out of a user's equipment and places it into their inventory.""" found_item = Item.find_by_name_or_nick(item) if not found_item: return f'Error: {item} does not exist.' item_name = found_item.name equipment = author.all_armour if found_item not in author.equipment_slots: return f'You do not have {item_name} equipped.' slot = found_item.slot - 1 slot_name = author.equipment_slot_strs[slot] curr_equip = author.equipment_slots[slot] # Set the equipment slot setattr(author, slot_name, None) author.update_inventory(curr_equip) author.save() return f'{found_item.name} unequipped from {SLOTS[str(slot+1)]}!'
def bury(author: User, itemname: str, number: int): """Buries (a given amount) of an item and gives the user prayer xp.""" item: Item = Item.find_by_name_or_nick(itemname) if not item: return f'Error: {itemname} is not an item.' if not item.is_buryable: return f"You cannot bury {item.name}." user_items = author.get_item_by_item(item) if not author.has_item_amount_by_item(item, number) or not user_items: # TODO: Pluralize this return f'You do not have enough {item.name} in your inventory.' user_item: UserInventory = user_items[0] xp_difference = item.xp * number pre_bury_level = author.prayer_level author.prayer_xp += xp_difference author.save() if user_item.amount == number: user_item.delete() else: user_item.amount -= number user_item.save() post_bury_level = author.prayer_level level_difference = post_bury_level - pre_bury_level prayer_xp_formatted = '{:,}'.format(xp_difference) out = PRAYER_HEADER out += f'You get {prayer_xp_formatted} prayer xp from your {item.pluralize(number)}! ' if level_difference: out += f'You have also gained {level_difference} prayer levels!' return out
def craft(user: User, recipe, n=1): """Crafts (a given number of) an item.""" recipeitem: Item = Item.find_by_name_or_nick(recipe) if not recipeitem: return f'Cannot find item {recipe}.' recipe: Recipe = Recipe.objects.filter(creates=recipeitem) if not recipe: return f'Cannot find recipe that crafts {recipeitem.name}.' recipe = recipe[0] name = recipeitem.name artisan_level = user.artisan_level artisan_req = recipe.level_requirement if artisan_level < artisan_req: return f'Error: {name} has a artisan requirement ({artisan_req}) ' \ f'higher than your artisan level ({artisan_level}).' inputs = recipe.get_requirements() negative_loot = {} for rr in list(inputs): if user.has_item_amount_by_item(rr.item, rr.amount*n): negative_loot[rr.item] = rr.amount * n else: return f'Error: you do not have enough items to make {recipe.creates.pluralize(n)} ' \ f'({rr.item.pluralize(rr.amount * n)}).' bonus = random.randint(1, math.floor(n/20)) if artisan_level == 99 and n >=20 else 0 goldsmith_bonus = 1 if recipe == GOLD_BAR_RECIPE and GOLD_GAUNTLETS in user.equipment_slots: goldsmith_bonus = 2 user.update_inventory(negative_loot, remove=True) user.update_inventory({recipe.creates: n + bonus}) xp = XP_FACTOR * goldsmith_bonus * n * recipe.creates.xp user.artisan_xp += xp user.save() level_after = user.artisan_level xp_formatted = '{:,}'.format(xp) out = f'Successfully crafted {recipe.creates.pluralize(n)}! 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 {recipe.creates.pluralize(bonus)}! ' if level_after > artisan_level: out += f'You have also gained {level_after - artisan_level} artisan levels!' return out
def equip_item(author: User, item: str): """Takes an item out of a user's inventory and places it into their equipment.""" found_item = Item.find_by_name_or_nick(item) if found_item is None: return f'Error: {item} does not exist.' item_level = found_item.level user_cb_level = author.combat_level # Error checking/verification if user_cb_level < item_level: return f'Error: Insufficient level to equip item ({found_item.level}). \ Your current combat level is {user_cb_level}.' if not author.has_item_by_item(found_item): return f'Error: {found_item.name} not in inventory.' if not found_item.is_equippable: return f'Error: {item} cannot be equipped.' if found_item.is_max_only and not author.is_maxed: return f"You cannot equip this item since you do not have {author.max_possible_level} skill total." slot = found_item.slot - 1 # I blame coiz for starting this at slot 1 :ANGERY: curr_equip = author.equipment_slots[slot] # if found_item == curr_equip: # return f"You already have {found_item.name} equipped!" item_name = found_item.name slot_name = author.equipment_slot_strs[slot] # Set the equipment slot setattr(author, slot_name, found_item) # Update the inventories author.update_inventory(curr_equip) author.update_inventory(found_item, remove=True) author.save() return f'{item_name} equipped to {SLOTS[str(slot+1)]}!'
def calc_chance(user: User, monster: Monster, number: int, remove_food=False): """Calculates the chance of success of a task.""" number = int(number) player_dam, player_acc, player_arm, dam_multiplier, monster_base, chance_bonus = calc_task_vars( user, monster) prayer_chance = 0 if user.prayer_slot is not None: if 10 <= user.prayer_slot.id <= 12: # if user is using protect from mage/range/melee monster_affinity = monster.affinity if monster_affinity == 0 and user.prayer_slot.id == 12\ or monster_affinity == 1 and user.prayer_slot.id == 11 \ or monster_affinity == 2 and user.prayer_slot.id == 10: prayer_chance = user.prayer_slot.chance chance = (200 * (1 + user.combat_level / 99) * player_arm) /\ (number / 50 * monster.damage * dam_multiplier + (1 + monster.level / 200)) + chance_bonus if user.equipment_slots[0] == SLAYER_HELMET: chance = min(round(chance * 1.125), 100) if user.is_eating: food_bonus = user.active_food.food_value if food_bonus > 0: num_food = user.get_item_by_item(user.active_food) if num_food: num_food = num_food[0].amount chance += food_bonus if num_food >= number else int( food_bonus * num_food / number) amount = number if num_food >= number else num_food if remove_food: user.update_inventory({user.active_food.id: amount}, remove=True) else: user.active_food = None user.save() chance += prayer_chance if chance > 100: chance = 100 if chance < 0: chance = 0 return round(chance)
def calc_task_vars(user: User, monster: Monster): """Calculates the variables required to calculate a task's length/number.""" user_prayer = user.prayer_slot equipment = user.equipment_slots player_dam, player_acc, player_arm, player_pray = user.equipment_stats monsters_fought = user.monster_kills(monster.name) if monsters_fought: monsters_fought = monsters_fought[0] monster_num_bonus = 1 - 0.2 * min(monsters_fought.amount / 5000, 1) else: monster_num_bonus = 1 if user_prayer is not None: player_dam, player_acc, player_arm = prayer.calc_pray_bonus(user) monster_base = calc_monster_base(user, monster) player_potion = equipment[14] if player_potion: player_potion = player_potion.id if player_potion == 427 or player_potion == 430: player_acc = player_acc * 1.1 + 3 if player_potion == 428 or player_potion == 430: player_dam = player_dam * 1.1 + 3 if player_potion == 429 or player_potion == 430: player_arm = player_arm * 1.1 + 3 if player_potion == 431 or player_potion == 434: player_acc = player_acc * 1.15 + 5 if player_potion == 432 or player_potion == 434: player_dam = player_dam * 1.15 + 5 if player_potion == 433 or player_potion == 434: player_arm = player_arm * 1.15 + 5 dam_multiplier = 1 + player_acc / 200 return tuple((player_dam, player_acc, player_arm, dam_multiplier, monster_base, monster_num_bonus))
def claim(person: User, name, number, other_person=None): """Claims items/xp from an item and returns a message.""" item = Item.objects.filter(name__iexact=name)[0] if not item: return f"{name} is not an item." if not person.has_item_amount_by_name(name, number): return f'You do not have {item.pluralize(number)} in your inventory.' out = ':moneybag: __**CLAIM**__ :moneybag:\n' if item == GEM_ROCK: out += 'You have received:\n' gems = { SAPPHIRE: 4, EMERALD: 16, RUBY: 64, DIAMOND: 128, LAPIS_LAZULI: 256, QUARTZ: 512 } loot = Counter() for _ in range(number): while True: gem_type = random.sample(gems.keys(), 1)[0] if random.randint(1, gems[gem_type]) == 1: loot[gem_type] += 1 break person.update_inventory(loot) for gem in loot.keys(): out += f'{gem.pluralize(loot[gem])}\n' out += f'from your {GEM_ROCK.pluralize(number)}.' person.update_inventory({GEM_ROCK: number}, remove=True) elif item == ANCIENT_EFFIGY: skills = Counter() for _ in range(number): skill = random.sample(person.xp_fields_str, 1)[0] skills[skill] += 1 person.update_inventory({OPENED_ANCIENT_EFFIGY: number}) person.update_inventory({ANCIENT_EFFIGY: number}, remove=True) got_pet = False if not person.has_item_amount_by_item(EFFY, 1): for _ in range(number): if random.randint(1, 100) == 1: got_pet = True person.update_inventory({EFFY: 1}) break out += f"You have received the following xp from your {ANCIENT_EFFIGY.pluralize(number)}!\n" for skill in skills.keys(): xp_gained = skills[skill] * XP_PER_EFFIGY setattr(person, skill, getattr(person, skill) + xp_gained) xp_gained_formatted = '{:,}'.format(xp_gained) out += f"{xp_gained_formatted} {skill.replace('_', ' ')}\n" if got_pet: out += 'You have also recieved Effy, the Effigy Pet!' person.save() elif item == CHRISTMAS_CRACKER: if other_person: person.update_inventory({CHRISTMAS_CRACKER: number}, remove=True) loot = Counter( random.choices(Item.objects.filter(name__contains="partyhat"), k=number)) other_person_name = other_person.nick if other_person.nick else other_person.plain_name out += f'You and {other_person_name} pull the christmas cracker and ' if random.randint(0, 1): out += f'**you** get the bigger end. You have received:\n' person.update_inventory(loot) else: out += f'**{other_person_name}** gets the bigger end. They have received:\n' other_person.update_inventory(loot) for item, value in loot.items(): out += f'**{item.pluralize(value)}**\n' else: out += f'Please use the `~pull [number] [other_person]` command to claim the christmas crackers.' else: out += f'{item} is not claimable.' return out
def start_runecraft(guildid, channelid, user: User, item, number=1, pure=0): """Starts a runecrafting session.""" from miniscape import adventures as adv out = '' if not adv.is_on_adventure(user.id): item: Item = Item.find_by_name_or_nick(item) if not item: return f'{item} is not an item.' try: number = int(number) except ValueError: return f'{number} is not a valid number.' if not item.is_rune: return f'{items.get_attr(itemid)} is not a rune that can be crafted.' # Find out if user has the talisman rune_type = item.name.split(" ")[0] if not user.has_item_by_name(rune_type + " talisman"): return f'{items.get_attr(talismanid)} not found in inventory.' item_name = item.name runecrafting_level = user.rc_level runecraft_req = item.level player_potion = user.potion_slot.id if user.potion_slot else '0' if player_potion == 435: boosted_level = runecrafting_level + 3 elif player_potion == 436: boosted_level = runecrafting_level + 6 else: boosted_level = runecrafting_level if boosted_level < runecraft_req: return f'Error: {item_name} has a runecrafting requirement ({runecraft_req}) higher ' \ f'than your runecrafting level ({runecrafting_level})' if item.quest_req and not user.has_completed_quest(item.quest_req): return f'You do not have the required quest to craft this rune.' if not user.has_completed_quest(RUNE_MYSTERIES): return f'You do not know how to craft runes.' factor = 1 if user.has_completed_quest(ABYSS_QUEST) else 2 bonus = 0 for pouch in POUCHES: if user.has_item_by_item(pouch): bonus += pouch.pouch length = factor * math.ceil(number * 1.2 / (28.0 + bonus)) ess_to_check = PURE_ESSENCE if pure else RUNE_ESSENCE if not user.has_item_amount_by_item(ess_to_check, number): return f'You do not have enough essence to craft this many runes.' rc_session = adv.format_line(6, user.id, adv.get_finish_time(length * 60), guildid, channelid, item.id, item_name, number, length, pure) adv.write(rc_session) out += f'You are now crafting {item.pluralize(number)} for {length} minutes.' else: out = adv.print_adventure(user.id) out += adv.print_on_adventure_error('runecrafting session') return out