""" Python code to automatically generate a loot filter file for Path of Exile. """ # System Imports. import argparse, json, os, sys # User Imports. from resources import logging as init_logging from resources.parsers.accessories import AccessoryParser from resources.parsers.currency import CurrencyParser, PreEquipment_CurrencyParser, PostEquipment_CurrencyParser from resources.parsers.defense import DefenseParser from resources.parsers.flasks import FlaskParser from resources.parsers.gems import GemParser from resources.parsers.jewels import JewelParser from resources.parsers.maps import MapParser from resources.parsers.other import FinalParser, NotableGearParser, QuestItemParser, UniqueParser from resources.parsers.table_of_contents import TableOfContentsGenerator from resources.parsers.weapons import WeaponParser from resources.data.value_dictionary import filter_dict # Initialize Logger. logger = init_logging.get_logger(__name__) defense_choices = ['A', 'Ev', 'En', 'A/Ev', 'Ev/En', 'A/En'] weapon_choices = ['Bows', 'Quivers', 'Sceptres', 'Wands', 'Shields'] def run_filter_generation(): """ Start of program. """ # Define argument parsing. parser = define_argparse_args() # Attempt to parse passed args. args = parser.parse_args() # Determine if program should display help output or generate filter. amulet_help = get_amulet_help(args) belt_help = get_belt_help(args) ring_help = get_ring_help(args) if amulet_help or belt_help or ring_help: # At least one help arg passed. Display help and cancel filter generation. if amulet_help: display_amulet_help() if belt_help: display_belt_help() if ring_help: display_ring_help() logger.info('Cancelling filter generation.') else: # No help arg passed. Continue with actual filter generation. generate_filter(args) def generate_filter(args ,test_mode=False): """ Logic to actually generate filter file. :param args: Argparse args. :param test_mode: Debugging mode for testing specific sections of generation. """ # Read in all arg values from user. debug = get_debug(args) file_name = get_file_name(args) defenses = get_defenses(args) weapons = get_weapons(args) shield_types = get_shield_types(args) base_drop_level = get_base_drop_level(args) level_rarity_modifier = get_level_rarity_modifier(args) hybrid_flask_bool = get_hybrid_flask_bool(args) hidden_amulets = get_hidden_amulets(args) hidden_belts = get_hidden_belts(args) hidden_rings = get_hidden_rings(args) # Display args. logger.info('') logger.info('Creating filter:') logger.info(' Filter Name: "{0}"'.format(file_name)) logger.info(' Weapon Types: {0}'.format(weapons)) if 'Shields' in weapons: logger.info(' Shield Types: {0}'.format(shield_types)) logger.info(' Defense Types: {0}'.format(defenses)) logger.info('') logger.info(' Drop Level Modifiers (from item base drop level):') logger.info(' Base: +{0}'.format(base_drop_level)) logger.info(' Magic: +{0}'.format(base_drop_level + level_rarity_modifier)) logger.info(' Rare: +{0}'.format(base_drop_level + (level_rarity_modifier * 2))) logger.info('') if len(hidden_amulets) > 0 or len(hidden_belts) > 0 or len(hidden_rings) > 0: logger.info(' Note: For leveling purposes, some amulets/belts/rings will display') logger.info(' early on, even if set to ''"hidden".') if len(hidden_amulets) > 0: logger.info(' Hidden Amulets: {0}'.format(hidden_amulets)) if len(hidden_belts) > 0: logger.info(' Hidden Belts: {0}'.format(hidden_belts)) if len(hidden_rings) > 0: logger.info(' Hidden Rings: {0}'.format(hidden_rings)) logger.info('') # Create generation folder, if not present. try: os.mkdir('./generated_filters') except FileExistsError: pass # Folder already exists. This is fine. # Create filter. parse_num = 0 with open('generated_filters/{0}'.format(file_name), "w") as filter_file: filter_file.write('\n') filter_file.write('# ======================= #\n') filter_file.write('# === POE Loot Filter === #\n') filter_file.write('# ======================= #\n') filter_file.write('#\n#\n') filter_file.write('# Generated with:\n') filter_file.write('# Base Drop Level: {0}\n'.format(base_drop_level)) filter_file.write('# Level Rarity Modifier: {0}\n'.format(level_rarity_modifier)) filter_file.write('#\n') filter_file.write('# Weapons: {0}\n'.format(weapons)) if 'Shields' in weapons: filter_file.write('# Shields: {0}\n'.format(shield_types)) filter_file.write('# Defense: {0}\n'.format(defenses)) filter_file.write('# Hidden Amulets: {0}\n'.format(hidden_amulets)) filter_file.write('# Hidden Belts: {0}\n'.format(hidden_belts)) filter_file.write('# Hidden Rings: {0}\n'.format(hidden_rings)) filter_file.write('# Show Hybrid Flasks: {0}\n'.format(hybrid_flask_bool)) filter_file.write('#\n') filter_file.write('# Original Command:\n') filter_file.write('# python') orig_args = sys.argv for arg in orig_args: if 'main.py' in arg or arg[0] == '-': filter_file.write(' {0}'.format(arg)) else: filter_file.write(' "{0}"'.format(arg)) filter_file.write('\n#\n#\n') filter_file.write('# Sounds:\n') filter_file.write('# 1 - Unique\n') filter_file.write('# 2 - Quest Items\n') filter_file.write('# 4 - League/Special Item\n') filter_file.write('# 5 - Influenced Item\n') filter_file.write('# 6 - High Slot Item\n') filter_file.write('# 9 - Cards\n') filter_file.write('# 10 - Rare Currency\n') filter_file.write('# 13 - Map\n') filter_file.write('#\n#\n') if not test_mode: # Generate Table of Contents. parse_num += 1 TableOfContentsGenerator(filter_file, weapons, defenses, shield_types, hybrid_flask_bool, debug=debug) # Generate Quest Item Filtering. parse_num += 1 QuestItemParser(filter_file, parse_num, debug=debug) # Generate Unique Filtering. parse_num += 1 UniqueParser(filter_file, parse_num, debug=debug) # Generate Currency Filtering. parse_num += 1 CurrencyParser(filter_file, parse_num, debug=debug) # Generate Map Filtering. parse_num += 1 MapParser(filter_file, parse_num, debug=debug) # Generate Gem Filtering. parse_num += 1 GemParser(filter_file, parse_num, debug=debug) # Generate Jewel Filtering. parse_num += 1 JewelParser(filter_file, parse_num, debug=debug) # Generate Flask Filtering. parse_num += 1 FlaskParser(filter_file, parse_num, hybrid_flask_bool, debug=debug) # Generate Notable Gear Filtering. parse_num += 1 NotableGearParser(filter_file, parse_num, debug=debug) # Generate Pre-Equipment Currency Filtering. parse_num += 1 PreEquipment_CurrencyParser(filter_file, parse_num, debug=debug) # Generate Accessory Filtering. parse_num += 1 AccessoryParser( filter_file, parse_num, hidden_amulets, hidden_belts, hidden_rings, base_drop_level, level_rarity_modifier, debug=debug, ) # Generate Weapon Filtering. parse_num += 1 WeaponParser(filter_file, parse_num, weapons, shield_types, base_drop_level, level_rarity_modifier, debug=debug) # Generate Defense Filtering. parse_num += 1 DefenseParser(filter_file, parse_num, defenses, base_drop_level, level_rarity_modifier, debug=debug) # Generate Post-Equipment Currency Filtering. parse_num += 1 PostEquipment_CurrencyParser(filter_file, parse_num, debug=debug) # Generate End-of-Filter filtering. parse_num += 1 FinalParser(filter_file, parse_num, debug=debug) else: # Test mode. For debugging. # Hide all non-test items. FinalParser(filter_file, parse_num, debug=debug) logger.info('Created filter at "./generated_filters/{0}"'.format(file_name)) def define_argparse_args(): """ Defines and sets up argparse, to take in user-provided args. """ parser = argparse.ArgumentParser(description='Generates a loot filter file for path of exile.') parser.add_argument( '-n', '--name', nargs='+', help='Name for loot filter. ' 'Defaults to "path.filter" if not specified.', ) parser.add_argument( '-d', '--defense', nargs='+', choices=defense_choices, help='Use to define desired armor types to show. ' 'Default shows all types.', ) parser.add_argument( '-w', '--weapons', nargs='+', choices=weapon_choices, help='Use to define desired weapon types to show. ' 'Default shows all types.', ) parser.add_argument( '--shield_type', nargs='+', choices=defense_choices, help='Used to define desired shield types to show. ' 'Default shows all types.' ) parser.add_argument( '--base_drop_level', nargs=1, type=int, help='Defines how many levels a weapon/armor item drop should display for, from when it starts spawning. ' 'Defaults to 10.' ) parser.add_argument( '--level_rarity_modifier', nargs=1, type=int, help='Defines how many additional levels to display a weapon/armor item drop, based on rarity. ' 'Defaults to +5.' ) parser.add_argument( '--show_hybrid_flasks', action='store_true', help='Determines if hybrid flasks should display or not. ' 'Defaults to false.' ) parser.add_argument( '--hide_amulets', nargs='+', choices=get_amulet_list(), help='Hides all passed amulets from filtering. For list of amulets, use the "--amulet_help" arg.' ) parser.add_argument( '--hide_belts', nargs='+', choices=get_belt_list(), help='Hides all passed belts from filtering. For list of belts, use the "--belt_help" arg.' ) parser.add_argument( '--hide_rings', nargs='+', choices=get_ring_list(), help='Hides all passed rings from filtering. For list of rings, use the "--ring_help" arg.' ) parser.add_argument( '--debug', action='store_true', help='Runs program in debug mode. ' 'Defaults to false.', ) parser.add_argument( '--amulet_help', '--amulets_help', '--help_amulets', action='store_true', help='Displays available amulets to hide with the "--hide_amulets" command. ' 'By default, all amulets display at all times.' ) parser.add_argument( '--belt_help', '--belts_help', '--help_belts', action='store_true', help='Displays available belts to hide with the "--hide_belts" command. ' 'By default, all belts display at all times.' ) parser.add_argument( '--ring_help', '--rings_help', '--help_rings', action='store_true', help='Displays available rings to hide with the "--hide_rings" command. ' 'By default, all rings display at all times.' ) return parser def get_debug(args): """ Get program debug bool to determine if debug output is displayed for program. :param args: Argparse args. """ if args.debug: return True else: return False def get_file_name(args): """ Get file name for generated loot filter. :param args: Argparse args. """ if args.name is None: # Use default name. return 'path.filter' else: # Use user-provided name. file_name = '' for item in args.name: if file_name == '': file_name = item else: file_name += '_{0}'.format(item) # Ensure it ends with ".filter". if not file_name.endswith(".filter"): file_name += '.filter' return file_name def get_defenses(args): """ Get defense types to filter on. :param args: Argparse args. """ if args.defense is None: # Default to showing all defense types. return defense_choices else: # Show user-specified defense types. return args.defense def get_weapons(args): """ Get weapon types to filter on. :param args: Argparse args. """ if args.weapons is None: # Default to showing all weapon types. return weapon_choices else: # Show user-specified weapon types. return args.weapons def get_shield_types(args): """ Get shield types to filter on. :param args: Argparse args. """ if args.shield_type is None: # Default to showing all shield types. return defense_choices else: # Show user-specified shield types. return args.shield_type def get_base_drop_level(args): """ Get base drop level value. For all weapon and armor types set to display, this (plus the original item drop level) determines how many levels the item will show for. :param args: Argparse args. """ if args.base_drop_level is None: return filter_dict['base_drop_level'] else: return args.base_drop_level[0] def get_level_rarity_modifier(args): """ Get level rarity modifier value. This is combined with the above "base drop level" to determine how many additional levels to show uncommon and rare items for. Uncommon items use this value directly, while rare multiply it by 2. :param args: Argparse args. """ if args.level_rarity_modifier is None: return filter_dict['level_rarity_modifier'] else: return args.level_rarity_modifier[0] def get_hybrid_flask_bool(args): """ Check for hybrid flasks display bool. Determines if hybrid flasks will display in filter or not. :param args: Argparse args. """ if args.show_hybrid_flasks: return True else: return False def get_amulet_help(args): """ Checks for amulet help bool. If true, displays available belts to filter on for "--hide_amulets" arg and cancels filter creation. :param args: Argparse args. """ if args.amulet_help: return True else: return False def get_belt_help(args): """ Checks for belt help bool. If true, displays available belts to filter on for "--hide_belts" arg and cancels filter creation. :param args: Argparse args. """ if args.belt_help: return True else: return False def get_ring_help(args): """ Checks for ring help bool. If true, displays available belts to filter on for "--hide_rings" arg and cancels filter creation. :param args: Argparse args. """ if args.ring_help: return True else: return False def get_hidden_amulets(args): """ Get hidden amulet list. Anything in this list will not show up in filtering. :param args: Argparse args. """ if args.hide_amulets: return args.hide_amulets else: return [] def get_hidden_belts(args): """ Get hidden belt list. Anything in this list will not show up in filtering. :param args: Argparse args. """ if args.hide_belts: return args.hide_belts else: return [] def get_hidden_rings(args): """ Get hidden ring list. Anything in this list will not show up in filtering. :param args: Argparse args. """ if args.hide_rings: return args.hide_rings else: return [] def get_amulet_list(): """ Gets list of all amulets from json data. :return: List of all available amulets. """ item_list = [] with open('resources/data/accessories/amulets.json', 'r') as json_file: # Loop through all items in json. json_data = json.load(json_file) for item in json_data: item_list.append(item['Name']) return item_list def get_belt_list(): """ Gets list of all belts from json data. :return: List of all available belts. """ item_list = [] with open('resources/data/accessories/belts.json', 'r') as json_file: # Loop through all items in json. json_data = json.load(json_file) for item in json_data: item_list.append(item['Name']) return item_list def get_ring_list(): """ Gets list of all rings from json data. :return: List of all available rings. """ item_list = [] with open('resources/data/accessories/rings.json', 'r') as json_file: # Loop through all items in json. json_data = json.load(json_file) for item in json_data: item_list.append(item['Name']) return item_list def display_amulet_help(): """ Displays helper list of all available amulets. """ item_list = get_amulet_list() logger.info('Amulets:') for item in item_list: logger.info(' {0}'.format(item)) logger.info('') def display_belt_help(): """ Displays helper list of all available belts. """ item_list = get_belt_list() logger.info('Belts:') for item in item_list: logger.info(' {0}'.format(item)) logger.info('') def display_ring_help(): """ Displays helper list of all available rings. """ item_list = get_ring_list() logger.info('Rings:') for item in item_list: logger.info(' {0}'.format(item)) logger.info('') if __name__ == '__main__': logger.info('Starting program.') logger.info('') run_filter_generation() logger.info('') logger.info('Terminating program.')