diff --git a/src/bot.py b/src/bot.py index 676ad86..e905009 100644 --- a/src/bot.py +++ b/src/bot.py @@ -46,7 +46,7 @@ class Bot: self._messenger = Messenger() self._config = Config() self._template_finder = template_finder - self._item_finder = ItemFinder(self._config) + self._item_finder = ItemFinder() self._ui_manager = UiManager(self._screen, self._template_finder, self._game_stats) self._belt_manager = BeltManager(self._screen, self._template_finder) self._pather = Pather(self._screen, self._template_finder) @@ -54,23 +54,23 @@ class Bot: # Create Character if self._config.char["type"] in ["sorceress", "light_sorc"]: - self._char: IChar = LightSorc(self._config.light_sorc, self._config.char, self._screen, self._template_finder, self._ui_manager, self._pather) + self._char: IChar = LightSorc(self._config.light_sorc, self._screen, self._template_finder, self._ui_manager, self._pather) elif self._config.char["type"] == "blizz_sorc": - self._char: IChar = BlizzSorc(self._config.blizz_sorc, self._config.char, self._screen, self._template_finder, self._ui_manager, self._pather) + self._char: IChar = BlizzSorc(self._config.blizz_sorc, self._screen, self._template_finder, self._ui_manager, self._pather) elif self._config.char["type"] == "nova_sorc": - self._char: IChar = NovaSorc(self._config.nova_sorc, self._config.char, self._screen, self._template_finder, self._ui_manager, self._pather) + self._char: IChar = NovaSorc(self._config.nova_sorc, self._screen, self._template_finder, self._ui_manager, self._pather) elif self._config.char["type"] == "hammerdin": - self._char: IChar = Hammerdin(self._config.hammerdin, self._config.char, self._screen, self._template_finder, self._ui_manager, self._pather) + self._char: IChar = Hammerdin(self._config.hammerdin, self._screen, self._template_finder, self._ui_manager, self._pather) elif self._config.char["type"] == "trapsin": - self._char: IChar = Trapsin(self._config.trapsin, self._config.char, self._screen, self._template_finder, self._ui_manager, self._pather) + self._char: IChar = Trapsin(self._config.trapsin, self._screen, self._template_finder, self._ui_manager, self._pather) elif self._config.char["type"] == "barbarian": - self._char: IChar = Barbarian(self._config.barbarian, self._config.char, self._screen, self._template_finder, self._ui_manager, self._pather) + self._char: IChar = Barbarian(self._config.barbarian, self._screen, self._template_finder, self._ui_manager, self._pather) elif self._config.char["type"] == "necro": - self._char: IChar = Necro(self._config.necro, self._config.char, self._screen, self._template_finder, self._ui_manager, self._pather) + self._char: IChar = Necro(self._config.necro, self._screen, self._template_finder, self._ui_manager, self._pather) elif self._config.char["type"] == "basic": - self._char: IChar = Basic(self._config.basic, self._config.char, self._screen, self._template_finder, self._ui_manager, self._pather) + self._char: IChar = Basic(self._config.basic, self._screen, self._template_finder, self._ui_manager, self._pather) elif self._config.char["type"] == "basic_ranged": - self._char: IChar = Basic_Ranged(self._config.basic_ranged, self._config.char, self._screen, self._template_finder, self._ui_manager, self._pather) + self._char: IChar = Basic_Ranged(self._config.basic_ranged, self._screen, self._template_finder, self._ui_manager, self._pather) else: Logger.error(f'{self._config.char["type"]} is not supported! Closing down bot.') os._exit(1) @@ -228,7 +228,7 @@ class Bot: if self._pick_corpse: self._pick_corpse = False time.sleep(1.6) - DeathManager.pick_up_corpse(self._config, self._screen) + DeathManager.pick_up_corpse(self._screen) wait(1.2, 1.5) self._belt_manager.fill_up_belt_from_inventory(self._config.char["num_loot_columns"]) wait(0.5) @@ -238,7 +238,7 @@ class Bot: # Check if should need some healing img = self._screen.grab() buy_pots = self._belt_manager.should_buy_pots() - if HealthManager.get_health(self._config, img) < 0.6 or HealthManager.get_mana(self._config, img) < 0.2 or buy_pots: + if HealthManager.get_health(img) < 0.6 or HealthManager.get_mana(img) < 0.2 or buy_pots: if buy_pots: Logger.info("Buy pots at next possible Vendor") pot_needs = self._belt_manager.get_pot_needs() diff --git a/src/char/barbarian.py b/src/char/barbarian.py index d6bc9d6..f983888 100644 --- a/src/char/barbarian.py +++ b/src/char/barbarian.py @@ -12,9 +12,9 @@ from pather import Pather, Location class Barbarian(IChar): - def __init__(self, skill_hotkeys, char_config, screen: Screen, template_finder: TemplateFinder, ui_manager: UiManager, pather: Pather): + def __init__(self, skill_hotkeys: dict, screen: Screen, template_finder: TemplateFinder, ui_manager: UiManager, pather: Pather): Logger.info("Setting up Barbarian") - super().__init__(skill_hotkeys, char_config, screen, template_finder, ui_manager) + super().__init__(skill_hotkeys, screen, template_finder, ui_manager) self._pather = pather self._do_pre_move = True # offset shenk final position further to the right and bottom diff --git a/src/char/basic.py b/src/char/basic.py index e41665c..660ee6b 100644 --- a/src/char/basic.py +++ b/src/char/basic.py @@ -12,9 +12,9 @@ from pather import Pather, Location class Basic(IChar): - def __init__(self, skill_hotkeys, char_config, screen: Screen, template_finder: TemplateFinder, ui_manager: UiManager, pather: Pather): + def __init__(self, skill_hotkeys: dict, screen: Screen, template_finder: TemplateFinder, ui_manager: UiManager, pather: Pather): Logger.info("Setting up Basic Character") - super().__init__(skill_hotkeys, char_config, screen, template_finder, ui_manager) + super().__init__(skill_hotkeys, screen, template_finder, ui_manager) self._pather = pather self._do_pre_move = True # offset shenk final position further to the right and bottom diff --git a/src/char/basic_ranged.py b/src/char/basic_ranged.py index d236fc7..cd4f1be 100644 --- a/src/char/basic_ranged.py +++ b/src/char/basic_ranged.py @@ -14,9 +14,9 @@ from pather import Pather, Location class Basic_Ranged(IChar): - def __init__(self, skill_hotkeys, char_config, screen: Screen, template_finder: TemplateFinder, ui_manager: UiManager, pather: Pather): + def __init__(self, skill_hotkeys: dict, screen: Screen, template_finder: TemplateFinder, ui_manager: UiManager, pather: Pather): Logger.info("Setting up Basic Ranged Character") - super().__init__(skill_hotkeys, char_config, screen, template_finder, ui_manager) + super().__init__(skill_hotkeys, screen, template_finder, ui_manager) self._pather = pather self._do_pre_move = True diff --git a/src/char/hammerdin.py b/src/char/hammerdin.py index d649211..daf44fa 100644 --- a/src/char/hammerdin.py +++ b/src/char/hammerdin.py @@ -12,9 +12,9 @@ from pather import Pather, Location class Hammerdin(IChar): - def __init__(self, skill_hotkeys, char_config, screen: Screen, template_finder: TemplateFinder, ui_manager: UiManager, pather: Pather): + def __init__(self, skill_hotkeys: dict, screen: Screen, template_finder: TemplateFinder, ui_manager: UiManager, pather: Pather): Logger.info("Setting up Hammerdin") - super().__init__(skill_hotkeys, char_config, screen, template_finder, ui_manager) + super().__init__(skill_hotkeys, screen, template_finder, ui_manager) self._pather = pather self._do_pre_move = True # In case we have a running pala, we want to switch to concentration when moving to the boss diff --git a/src/char/i_char.py b/src/char/i_char.py index 1895f2f..399b0c5 100644 --- a/src/char/i_char.py +++ b/src/char/i_char.py @@ -16,9 +16,9 @@ from ui import UiManager class IChar: - def __init__(self, skill_hotkeys: Dict, char_config: Dict, screen: Screen, template_finder: TemplateFinder, ui_manager: UiManager): + def __init__(self, skill_hotkeys: Dict, screen: Screen, template_finder: TemplateFinder, ui_manager: UiManager): self._skill_hotkeys = skill_hotkeys - self._char_config = char_config + self._char_config = Config().char self._template_finder = template_finder self._ui_manager = ui_manager self._screen = screen diff --git a/src/char/necro.py b/src/char/necro.py index 7657a3b..d07fa95 100644 --- a/src/char/necro.py +++ b/src/char/necro.py @@ -15,9 +15,9 @@ import time from utils.misc import cut_roi, is_in_roi class Necro(IChar): - def __init__(self, skill_hotkeys, char_config, screen: Screen, template_finder: TemplateFinder, ui_manager: UiManager, pather: Pather): + def __init__(self, skill_hotkeys: dict, screen: Screen, template_finder: TemplateFinder, ui_manager: UiManager, pather: Pather): Logger.info('\033[94m'+"Setting up Necro"+'\033[0m') - super().__init__(skill_hotkeys, char_config, screen, template_finder, ui_manager) + super().__init__(skill_hotkeys, screen, template_finder, ui_manager) self._pather = pather #custom necro pathing for pindle self._pather.adapt_path((Location.A5_PINDLE_START, Location.A5_PINDLE_SAFE_DIST), [100,101]) diff --git a/src/char/sorceress/sorceress.py b/src/char/sorceress/sorceress.py index 2423eb0..347f99c 100644 --- a/src/char/sorceress/sorceress.py +++ b/src/char/sorceress/sorceress.py @@ -13,8 +13,8 @@ from pather import Pather class Sorceress(IChar): - def __init__(self, skill_hotkeys, char_config, screen: Screen, template_finder: TemplateFinder, ui_manager: UiManager, pather: Pather): - super().__init__(skill_hotkeys, char_config, screen, template_finder, ui_manager) + def __init__(self, skill_hotkeys: dict, screen: Screen, template_finder: TemplateFinder, ui_manager: UiManager, pather: Pather): + super().__init__(skill_hotkeys, screen, template_finder, ui_manager) self._pather = pather def pick_up_item(self, pos: Tuple[float, float], item_name: str = None, prev_cast_start: float = 0): diff --git a/src/char/trapsin.py b/src/char/trapsin.py index 888816c..7186a70 100644 --- a/src/char/trapsin.py +++ b/src/char/trapsin.py @@ -14,9 +14,9 @@ import numpy as np class Trapsin(IChar): - def __init__(self, skill_hotkeys, char_config, screen: Screen, template_finder: TemplateFinder, ui_manager: UiManager, pather: Pather): + def __init__(self, skill_hotkeys: dict, screen: Screen, template_finder: TemplateFinder, ui_manager: UiManager, pather: Pather): Logger.info("Setting up Trapsin") - super().__init__(skill_hotkeys, char_config, screen, template_finder, ui_manager) + super().__init__(skill_hotkeys, screen, template_finder, ui_manager) self._pather = pather def pre_buff(self): diff --git a/src/config.py b/src/config.py index a4dfe3d..4de67d9 100644 --- a/src/config.py +++ b/src/config.py @@ -1,10 +1,14 @@ import configparser +import threading import numpy as np import os import re from dataclasses import dataclass from logger import Logger +config_lock = threading.Lock() + + @dataclass class ItemProps: pickit_type: int = 0 @@ -14,223 +18,39 @@ class ItemProps: exclude_type: str = "OR" class Config: - def _select_val(self, section: str, key: str = None): - if section in self._custom and key in self._custom[section]: - return self._custom[section][key] - elif section in self._config: - return self._config[section][key] - elif section in self._pickit_config: - return self._pickit_config[section][key] - elif section in self._shop_config: - return self._shop_config[section][key] + data_loaded = False + # all configparser objects + _config = None + _game_config = None + _pickit_config = None + _shop_config = None + _custom = None + + + def __init__(self): + with config_lock: + if not Config.data_loaded: + Config.data_loaded = True + Config.load_data() + + @staticmethod + def _select_val(section: str, key: str = None): + if section in Config._custom and key in Config._custom[section]: + return Config._custom[section][key] + elif section in Config._config: + return Config._config[section][key] + elif section in Config._pickit_config: + return Config._pickit_config[section][key] + elif section in Config._shop_config: + return Config._shop_config[section][key] else: - return self._game_config[section][key] + return Config._game_config[section][key] - def __init__(self, print_warnings: bool = False): - # print_warnings, what a hack... here it is, not making the effort - # passing a single config instance through bites me in the ass - self._print_warnings = print_warnings - self._config = configparser.ConfigParser() - self._config.read('config/params.ini') - self._game_config = configparser.ConfigParser() - self._game_config.read('config/game.ini') - self._pickit_config = configparser.ConfigParser() - self._pickit_config.read('config/pickit.ini') - self._shop_config = configparser.ConfigParser() - self._shop_config.read('config/shop.ini') - self._custom = configparser.ConfigParser() - if os.environ.get('RUN_ENV') != "test" and os.path.exists('config/custom.ini'): - self._custom.read('config/custom.ini') - - self.general = { - "saved_games_folder": self._select_val("general", "saved_games_folder"), - "name": self._select_val("general", "name"), - "monitor": int(self._select_val("general", "monitor")), - "max_game_length_s": float(self._select_val("general", "max_game_length_s")), - "exit_key": self._select_val("general", "exit_key"), - "resume_key": self._select_val("general", "resume_key"), - "auto_settings_key": self._select_val("general", "auto_settings_key"), - "restore_settings_from_backup_key": self._select_val("general", "restore_settings_from_backup_key"), - "settings_backup_key": self._select_val("general", "settings_backup_key"), - "graphic_debugger_key": self._select_val("general", "graphic_debugger_key"), - "logg_lvl": self._select_val("general", "logg_lvl"), - "randomize_runs": bool(int(self._select_val("general", "randomize_runs"))), - "difficulty": self._select_val("general", "difficulty"), - "message_api_type": self._select_val("general", "message_api_type"), - "custom_message_hook": self._select_val("general", "custom_message_hook"), - "discord_status_count": False if not self._select_val("general", "discord_status_count") else int(self._select_val("general", "discord_status_count")), - "discord_status_condensed": bool(int(self._select_val("general", "discord_status_condensed"))), - "info_screenshots": bool(int(self._select_val("general", "info_screenshots"))), - "loot_screenshots": bool(int(self._select_val("general", "loot_screenshots"))), - "d2r_path": self._select_val("general", "d2r_path"), - "restart_d2r_when_stuck": bool(int(self._select_val("general", "restart_d2r_when_stuck"))) - } - - # Added for dclone ip hunting - self.dclone = { - "region_ips": self._select_val("dclone", "region_ips"), - "dclone_hotip": self._select_val("dclone", "dclone_hotip"), - } - - self.routes = {} - order_str = self._select_val("routes", "order").replace("run_eldritch", "run_shenk") - self.routes_order = [x.strip() for x in order_str.split(",")] - del self._config["routes"]["order"] - for key in self._config["routes"]: - self.routes[key] = bool(int(self._select_val("routes", key))) - - self.char = { - "type": self._select_val("char", "type"), - "show_items": self._select_val("char", "show_items"), - "inventory_screen": self._select_val("char", "inventory_screen"), - "stand_still": self._select_val("char", "stand_still"), - "force_move": self._select_val("char", "force_move"), - "num_loot_columns": int(self._select_val("char", "num_loot_columns")), - "take_health_potion": float(self._select_val("char", "take_health_potion")), - "take_mana_potion": float(self._select_val("char", "take_mana_potion")), - "take_rejuv_potion_health": float(self._select_val("char", "take_rejuv_potion_health")), - "take_rejuv_potion_mana": float(self._select_val("char", "take_rejuv_potion_mana")), - "heal_merc": float(self._select_val("char", "heal_merc")), - "heal_rejuv_merc": float(self._select_val("char", "heal_rejuv_merc")), - "chicken": float(self._select_val("char", "chicken")), - "merc_chicken": float(self._select_val("char", "merc_chicken")), - "tp": self._select_val("char", "tp"), - "belt_rows": int(self._select_val("char", "belt_rows")), - "show_belt": self._select_val("char", "show_belt"), - "potion1": self._select_val("char", "potion1"), - "potion2": self._select_val("char", "potion2"), - "potion3": self._select_val("char", "potion3"), - "potion4": self._select_val("char", "potion4"), - "belt_rejuv_columns": int(self._select_val("char", "belt_rejuv_columns")), - "belt_hp_columns": int(self._select_val("char", "belt_hp_columns")), - "belt_mp_columns": int(self._select_val("char", "belt_mp_columns")), - "stash_gold": bool(int(self._select_val("char", "stash_gold"))), - "gold_trav_only": bool(int(self._select_val("char", "gold_trav_only"))), - "use_merc": bool(int(self._select_val("char", "use_merc"))), - "id_items": bool(int(self._select_val("char", "id_items"))), - "open_chests": bool(int(self._select_val("char", "open_chests"))), - "pre_buff_every_run": bool(int(self._select_val("char", "pre_buff_every_run"))), - "cta_available": bool(int(self._select_val("char", "cta_available"))), - "weapon_switch": self._select_val("char", "weapon_switch"), - "battle_orders": self._select_val("char", "battle_orders"), - "battle_command": self._select_val("char", "battle_command"), - "casting_frames": int(self._select_val("char", "casting_frames")), - "atk_len_arc": float(self._select_val("char", "atk_len_arc")), - "atk_len_trav": float(self._select_val("char", "atk_len_trav")), - "atk_len_pindle": float(self._select_val("char", "atk_len_pindle")), - "atk_len_eldritch": float(self._select_val("char", "atk_len_eldritch")), - "atk_len_shenk": float(self._select_val("char", "atk_len_shenk")), - "atk_len_nihlatak": float(self._select_val("char", "atk_len_nihlatak")), - "atk_len_diablo_vizier": float(self._select_val("char", "atk_len_diablo_vizier")), - "atk_len_diablo_deseis": float(self._select_val("char", "atk_len_diablo_deseis")), - "atk_len_diablo_infector": float(self._select_val("char", "atk_len_diablo_infector")), - "atk_len_diablo": float(self._select_val("char", "atk_len_diablo")), - "atk_len_cs_trashmobs": float(self._select_val("char", "atk_len_cs_trashmobs")), - "kill_cs_trash": float(self._select_val("char", "kill_cs_trash")), - "always_repair": bool(int(self._select_val("char", "always_repair"))), - } - - # Sorc base config - sorc_base_cfg = dict(self._config["sorceress"]) - if "sorceress" in self._custom: - sorc_base_cfg.update(dict(self._custom["sorceress"])) - # blizz sorc - self.blizz_sorc = dict(self._config["blizz_sorc"]) - if "blizz_sorc" in self._custom: - self.blizz_sorc.update(dict(self._custom["blizz_sorc"])) - self.blizz_sorc.update(sorc_base_cfg) - # light sorc - self.light_sorc = dict(self._config["light_sorc"]) - if "light_sorc" in self._custom: - self.light_sorc.update(dict(self._custom["light_sorc"])) - self.light_sorc.update(sorc_base_cfg) - # nova sorc - self.nova_sorc = dict(self._config["nova_sorc"]) - if "nova_sorc" in self._custom: - self.nova_sorc.update(dict(self._custom["nova_sorc"])) - self.nova_sorc.update(sorc_base_cfg) - - # Palandin config - self.hammerdin = self._config["hammerdin"] - if "hammerdin" in self._custom: - self.hammerdin.update(self._custom["hammerdin"]) - - # Assasin config - self.trapsin = self._config["trapsin"] - if "trapsin" in self._custom: - self.trapsin.update(self._custom["trapsin"]) - - # Barbarian config - self.barbarian = self._config["barbarian"] - if "barbarian" in self._custom: - self.barbarian.update(self._custom["barbarian"]) - self.barbarian = dict(self.barbarian) - self.barbarian["cry_frequency"] = float(self.barbarian["cry_frequency"]) - - # Basic config - self.basic = self._config["basic"] - if "basic" in self._custom: - self.basic.update(self._custom["basic"]) - - # Basic Ranged config - self.basic_ranged = self._config["basic_ranged"] - if "basic_ranged" in self._custom: - self.basic_ranged.update(self._custom["basic_ranged"]) - - # Necro config - self.necro = self._config["necro"] - if "necro" in self._custom: - self.necro.update(self._custom["necro"]) - - self.advanced_options = { - "pathing_delay_factor": min(max(int(self._select_val("advanced_options", "pathing_delay_factor")), 1), 10), - "message_headers": self._select_val("advanced_options", "message_headers"), - "message_body_template": self._select_val("advanced_options", "message_body_template"), - "message_highlight": bool(int(self._select_val("advanced_options", "message_highlight"))), - "d2r_windows_always_on_top": bool(int(self._select_val("advanced_options", "d2r_windows_always_on_top"))), - "graphic_debugger_layer_creator": bool(int(self._select_val("advanced_options", "graphic_debugger_layer_creator"))), - } - - self.items = {} - for key in self._pickit_config["items"]: - try: - self.items[key] = self.parse_item_config_string(key) - if self.items[key].pickit_type and not os.path.exists(f"./assets/items/{key}.png") and self._print_warnings: - print(f"Warning: You activated {key} in pickit, but there is no img available in assets/items") - except ValueError as e: - if self._print_warnings: - print(f"Error with pickit config: {key} ({e})") - - self.colors = {} - for key in self._game_config["colors"]: - self.colors[key] = np.split(np.array([int(x) for x in self._select_val("colors", key).split(",")]), 2) - - self.ui_pos = {} - for key in self._game_config["ui_pos"]: - self.ui_pos[key] = int(self._select_val("ui_pos", key)) - - self.ui_roi = {} - for key in self._game_config["ui_roi"]: - self.ui_roi[key] = np.array([int(x) for x in self._select_val("ui_roi", key).split(",")]) - - self.path = {} - for key in self._game_config["path"]: - self.path[key] = np.reshape(np.array([int(x) for x in self._select_val("path", key).split(",")]), (-1, 2)) - - self.shop = { - "shop_trap_claws": bool(int(self._select_val("claws", "shop_trap_claws"))), - "shop_melee_claws": bool(int(self._select_val("claws", "shop_melee_claws"))), - "shop_3_skills_ias_gloves": bool(int(self._select_val("gloves", "shop_3_skills_ias_gloves"))), - "shop_2_skills_ias_gloves": bool(int(self._select_val("gloves", "shop_2_skills_ias_gloves"))), - "trap_min_score": int(self._select_val("claws", "trap_min_score")), - "melee_min_score": int(self._select_val("claws", "melee_min_score")), - "shop_hammerdin_scepters": bool(int(self._select_val("scepters", "shop_hammerdin_scepters"))), - } - - def parse_item_config_string(self, key: str = None) -> ItemProps: + @staticmethod + def parse_item_config_string(key: str = None) -> ItemProps: item_props = ItemProps() # split string by commas NOT contained within parentheses - item_string_as_list = re.split(r',\s*(?![^()]*\))', self._select_val("items", key).upper()) + item_string_as_list = re.split(r',\s*(?![^()]*\))', Config._select_val("items", key).upper()) trim_strs=["AND(", "OR(", "(", ")", " "] clean_string = [re.sub(r'|'.join(map(re.escape, trim_strs)), '', x).strip() for x in item_string_as_list] item_props.pickit_type = int(clean_string[0]) @@ -250,8 +70,215 @@ class Config: Logger.error(f"Item parsing error: {exception}") return item_props + @staticmethod + def turn_off_goldpickup(): + with config_lock: + Config.char["stash_gold"] = False + Config.items["misc_gold"].pickit_type = 0 + + @staticmethod + def load_data(): + Logger.info("Loading Config Data") + Config._config = configparser.ConfigParser() + Config._config.read('config/params.ini') + Config._game_config = configparser.ConfigParser() + Config._game_config.read('config/game.ini') + Config._pickit_config = configparser.ConfigParser() + Config._pickit_config.read('config/pickit.ini') + Config._shop_config = configparser.ConfigParser() + Config._shop_config.read('config/shop.ini') + Config._custom = configparser.ConfigParser() + if os.environ.get('RUN_ENV') != "test" and os.path.exists('config/custom.ini'): + Config._custom.read('config/custom.ini') + + Config.general = { + "saved_games_folder": Config._select_val("general", "saved_games_folder"), + "name": Config._select_val("general", "name"), + "monitor": int(Config._select_val("general", "monitor")), + "max_game_length_s": float(Config._select_val("general", "max_game_length_s")), + "exit_key": Config._select_val("general", "exit_key"), + "resume_key": Config._select_val("general", "resume_key"), + "auto_settings_key": Config._select_val("general", "auto_settings_key"), + "restore_settings_from_backup_key": Config._select_val("general", "restore_settings_from_backup_key"), + "settings_backup_key": Config._select_val("general", "settings_backup_key"), + "graphic_debugger_key": Config._select_val("general", "graphic_debugger_key"), + "logg_lvl": Config._select_val("general", "logg_lvl"), + "randomize_runs": bool(int(Config._select_val("general", "randomize_runs"))), + "difficulty": Config._select_val("general", "difficulty"), + "message_api_type": Config._select_val("general", "message_api_type"), + "custom_message_hook": Config._select_val("general", "custom_message_hook"), + "discord_status_count": False if not Config._select_val("general", "discord_status_count") else int(Config._select_val("general", "discord_status_count")), + "discord_status_condensed": bool(int(Config._select_val("general", "discord_status_condensed"))), + "info_screenshots": bool(int(Config._select_val("general", "info_screenshots"))), + "loot_screenshots": bool(int(Config._select_val("general", "loot_screenshots"))), + "d2r_path": Config._select_val("general", "d2r_path"), + "restart_d2r_when_stuck": bool(int(Config._select_val("general", "restart_d2r_when_stuck"))) + } + + # Added for dclone ip hunting + Config.dclone = { + "region_ips": Config._select_val("dclone", "region_ips"), + "dclone_hotip": Config._select_val("dclone", "dclone_hotip"), + } + + Config.routes = {} + order_str = Config._select_val("routes", "order").replace("run_eldritch", "run_shenk") + Config.routes_order = [x.strip() for x in order_str.split(",")] + del Config._config["routes"]["order"] + for key in Config._config["routes"]: + Config.routes[key] = bool(int(Config._select_val("routes", key))) + + Config.char = { + "type": Config._select_val("char", "type"), + "show_items": Config._select_val("char", "show_items"), + "inventory_screen": Config._select_val("char", "inventory_screen"), + "stand_still": Config._select_val("char", "stand_still"), + "force_move": Config._select_val("char", "force_move"), + "num_loot_columns": int(Config._select_val("char", "num_loot_columns")), + "take_health_potion": float(Config._select_val("char", "take_health_potion")), + "take_mana_potion": float(Config._select_val("char", "take_mana_potion")), + "take_rejuv_potion_health": float(Config._select_val("char", "take_rejuv_potion_health")), + "take_rejuv_potion_mana": float(Config._select_val("char", "take_rejuv_potion_mana")), + "heal_merc": float(Config._select_val("char", "heal_merc")), + "heal_rejuv_merc": float(Config._select_val("char", "heal_rejuv_merc")), + "chicken": float(Config._select_val("char", "chicken")), + "merc_chicken": float(Config._select_val("char", "merc_chicken")), + "tp": Config._select_val("char", "tp"), + "belt_rows": int(Config._select_val("char", "belt_rows")), + "show_belt": Config._select_val("char", "show_belt"), + "potion1": Config._select_val("char", "potion1"), + "potion2": Config._select_val("char", "potion2"), + "potion3": Config._select_val("char", "potion3"), + "potion4": Config._select_val("char", "potion4"), + "belt_rejuv_columns": int(Config._select_val("char", "belt_rejuv_columns")), + "belt_hp_columns": int(Config._select_val("char", "belt_hp_columns")), + "belt_mp_columns": int(Config._select_val("char", "belt_mp_columns")), + "stash_gold": bool(int(Config._select_val("char", "stash_gold"))), + "gold_trav_only": bool(int(Config._select_val("char", "gold_trav_only"))), + "use_merc": bool(int(Config._select_val("char", "use_merc"))), + "id_items": bool(int(Config._select_val("char", "id_items"))), + "open_chests": bool(int(Config._select_val("char", "open_chests"))), + "pre_buff_every_run": bool(int(Config._select_val("char", "pre_buff_every_run"))), + "cta_available": bool(int(Config._select_val("char", "cta_available"))), + "weapon_switch": Config._select_val("char", "weapon_switch"), + "battle_orders": Config._select_val("char", "battle_orders"), + "battle_command": Config._select_val("char", "battle_command"), + "casting_frames": int(Config._select_val("char", "casting_frames")), + "atk_len_arc": float(Config._select_val("char", "atk_len_arc")), + "atk_len_trav": float(Config._select_val("char", "atk_len_trav")), + "atk_len_pindle": float(Config._select_val("char", "atk_len_pindle")), + "atk_len_eldritch": float(Config._select_val("char", "atk_len_eldritch")), + "atk_len_shenk": float(Config._select_val("char", "atk_len_shenk")), + "atk_len_nihlatak": float(Config._select_val("char", "atk_len_nihlatak")), + "atk_len_diablo_vizier": float(Config._select_val("char", "atk_len_diablo_vizier")), + "atk_len_diablo_deseis": float(Config._select_val("char", "atk_len_diablo_deseis")), + "atk_len_diablo_infector": float(Config._select_val("char", "atk_len_diablo_infector")), + "atk_len_diablo": float(Config._select_val("char", "atk_len_diablo")), + "atk_len_cs_trashmobs": float(Config._select_val("char", "atk_len_cs_trashmobs")), + "kill_cs_trash": float(Config._select_val("char", "kill_cs_trash")), + "always_repair": bool(int(Config._select_val("char", "always_repair"))), + } + + # Sorc base config + sorc_base_cfg = dict(Config._config["sorceress"]) + if "sorceress" in Config._custom: + sorc_base_cfg.update(dict(Config._custom["sorceress"])) + # blizz sorc + Config.blizz_sorc = dict(Config._config["blizz_sorc"]) + if "blizz_sorc" in Config._custom: + Config.blizz_sorc.update(dict(Config._custom["blizz_sorc"])) + Config.blizz_sorc.update(sorc_base_cfg) + # light sorc + Config.light_sorc = dict(Config._config["light_sorc"]) + if "light_sorc" in Config._custom: + Config.light_sorc.update(dict(Config._custom["light_sorc"])) + Config.light_sorc.update(sorc_base_cfg) + # nova sorc + Config.nova_sorc = dict(Config._config["nova_sorc"]) + if "nova_sorc" in Config._custom: + Config.nova_sorc.update(dict(Config._custom["nova_sorc"])) + Config.nova_sorc.update(sorc_base_cfg) + + # Palandin config + Config.hammerdin = Config._config["hammerdin"] + if "hammerdin" in Config._custom: + Config.hammerdin.update(Config._custom["hammerdin"]) + + # Assasin config + Config.trapsin = Config._config["trapsin"] + if "trapsin" in Config._custom: + Config.trapsin.update(Config._custom["trapsin"]) + + # Barbarian config + Config.barbarian = Config._config["barbarian"] + if "barbarian" in Config._custom: + Config.barbarian.update(Config._custom["barbarian"]) + Config.barbarian = dict(Config.barbarian) + Config.barbarian["cry_frequency"] = float(Config.barbarian["cry_frequency"]) + + # Basic config + Config.basic = Config._config["basic"] + if "basic" in Config._custom: + Config.basic.update(Config._custom["basic"]) + + # Basic Ranged config + Config.basic_ranged = Config._config["basic_ranged"] + if "basic_ranged" in Config._custom: + Config.basic_ranged.update(Config._custom["basic_ranged"]) + + # Necro config + Config.necro = Config._config["necro"] + if "necro" in Config._custom: + Config.necro.update(Config._custom["necro"]) + + Config.advanced_options = { + "pathing_delay_factor": min(max(int(Config._select_val("advanced_options", "pathing_delay_factor")), 1), 10), + "message_headers": Config._select_val("advanced_options", "message_headers"), + "message_body_template": Config._select_val("advanced_options", "message_body_template"), + "message_highlight": bool(int(Config._select_val("advanced_options", "message_highlight"))), + "d2r_windows_always_on_top": bool(int(Config._select_val("advanced_options", "d2r_windows_always_on_top"))), + "graphic_debugger_layer_creator": bool(int(Config._select_val("advanced_options", "graphic_debugger_layer_creator"))), + } + + Config.items = {} + for key in Config._pickit_config["items"]: + try: + Config.items[key] = Config.parse_item_config_string(key) + if Config.items[key].pickit_type and not os.path.exists(f"./assets/items/{key}.png"): + print(f"Warning: You activated {key} in pickit, but there is no img available in assets/items") + except ValueError as e: + print(f"Error with pickit config: {key} ({e})") + + Config.colors = {} + for key in Config._game_config["colors"]: + Config.colors[key] = np.split(np.array([int(x) for x in Config._select_val("colors", key).split(",")]), 2) + + Config.ui_pos = {} + for key in Config._game_config["ui_pos"]: + Config.ui_pos[key] = int(Config._select_val("ui_pos", key)) + + Config.ui_roi = {} + for key in Config._game_config["ui_roi"]: + Config.ui_roi[key] = np.array([int(x) for x in Config._select_val("ui_roi", key).split(",")]) + + Config.path = {} + for key in Config._game_config["path"]: + Config.path[key] = np.reshape(np.array([int(x) for x in Config._select_val("path", key).split(",")]), (-1, 2)) + + Config.shop = { + "shop_trap_claws": bool(int(Config._select_val("claws", "shop_trap_claws"))), + "shop_melee_claws": bool(int(Config._select_val("claws", "shop_melee_claws"))), + "shop_3_skills_ias_gloves": bool(int(Config._select_val("gloves", "shop_3_skills_ias_gloves"))), + "shop_2_skills_ias_gloves": bool(int(Config._select_val("gloves", "shop_2_skills_ias_gloves"))), + "trap_min_score": int(Config._select_val("claws", "trap_min_score")), + "melee_min_score": int(Config._select_val("claws", "melee_min_score")), + "shop_hammerdin_scepters": bool(int(Config._select_val("scepters", "shop_hammerdin_scepters"))), + } + + if __name__ == "__main__": - config = Config(print_warnings=True) + from copy import deepcopy + config = Config() # Check if any added items miss templates for k in config.items: diff --git a/src/death_manager.py b/src/death_manager.py index 37f021a..7e81ffc 100644 --- a/src/death_manager.py +++ b/src/death_manager.py @@ -36,8 +36,9 @@ class DeathManager: return self._died @staticmethod - def pick_up_corpse(config: Config, screen: Screen): + def pick_up_corpse(screen: Screen): Logger.debug("Pick up corpse") + config = Config() x, y = screen.convert_screen_to_monitor((config.ui_pos["corpse_x"], config.ui_pos["corpse_y"])) mouse.move(x, y) mouse.click(button="left") @@ -90,4 +91,4 @@ if __name__ == "__main__": config = Config() screen = Screen(config.general["monitor"]) manager = DeathManager(screen) - manager.pick_up_corpse(config, screen) + manager.pick_up_corpse(screen) diff --git a/src/game_controller.py b/src/game_controller.py index 6ea726e..bfccb79 100644 --- a/src/game_controller.py +++ b/src/game_controller.py @@ -23,8 +23,8 @@ from utils.misc import kill_thread, set_d2r_always_on_top, restore_d2r_window_vi class GameController: is_running = False - def __init__(self, config: Config): - self._config = config + def __init__(self): + self._config = Config() self.screen = None self.template_finder = None self.health_monitor_thread = None @@ -120,7 +120,7 @@ class GameController: self.start_death_manager_thread() self.game_recovery = GameRecovery(self.screen, self.death_manager, self.template_finder) self.game_stats = GameStats() - self.char_selector = CharSelector(self.screen, self._config, self.template_finder) + self.char_selector = CharSelector(self.screen, self.template_finder) self.start_game_controller_thread() GameController.is_running = True diff --git a/src/health_manager.py b/src/health_manager.py index a9a3203..32da648 100644 --- a/src/health_manager.py +++ b/src/health_manager.py @@ -57,7 +57,8 @@ class HealthManager: Logger.info(f"Health Manager is now {debug_str}") @staticmethod - def get_health(config: Config, img: np.ndarray) -> float: + def get_health(img: np.ndarray) -> float: + config = Config() health_rec = [config.ui_pos["health_left"], config.ui_pos["health_top"], config.ui_pos["health_width"], config.ui_pos["health_height"]] health_img = cut_roi(img, health_rec) # red mask @@ -71,7 +72,8 @@ class HealthManager: return max(health_percentage, health_percentage_green) @staticmethod - def get_mana(config: Config, img: np.ndarray) -> float: + def get_mana(img: np.ndarray) -> float: + config = Config() mana_rec = [config.ui_pos["mana_left"], config.ui_pos["mana_top"], config.ui_pos["mana_width"], config.ui_pos["mana_height"]] mana_img = cut_roi(img, mana_rec) mask, _ = color_filter(mana_img, [np.array([117, 120, 20]), np.array([121, 255, 255])]) @@ -79,7 +81,8 @@ class HealthManager: return mana_percentage @staticmethod - def get_merc_health(config: Config, img: np.ndarray) -> float: + def get_merc_health(img: np.ndarray) -> float: + config = Config() health_rec = [config.ui_pos["merc_health_left"], config.ui_pos["merc_health_top"], config.ui_pos["merc_health_width"], config.ui_pos["merc_health_height"]] merc_health_img = cut_roi(img, health_rec) merc_health_img = cv2.cvtColor(merc_health_img, cv2.COLOR_BGR2GRAY) @@ -120,8 +123,8 @@ class HealthManager: # TODO: Check if in town or not! Otherwise risk endless chicken loop ingame_template_match = self._template_finder.search("WINDOW_INGAME_OFFSET_REFERENCE", img, roi=self._config.ui_roi["window_ingame_ref"], threshold=0.9) if ingame_template_match.valid: - health_percentage = self.get_health(self._config, img) - mana_percentage = self.get_mana(self._config, img) + health_percentage = self.get_health(img) + mana_percentage = self.get_mana(img) # check rejuv success_drink_rejuv = False last_drink = time.time() - self._last_rejuv @@ -148,7 +151,7 @@ class HealthManager: # check merc merc_alive = self._template_finder.search(["MERC_A2","MERC_A1","MERC_A5","MERC_A3"], img, roi=self._config.ui_roi["merc_icon"]).valid if merc_alive: - merc_health_percentage = self.get_merc_health(self._config, img) + merc_health_percentage = self.get_merc_health(img) last_drink = time.time() - self._last_merc_healh if merc_health_percentage < self._config.char["merc_chicken"]: Logger.warning(f"Trying to chicken, merc HP {(merc_health_percentage*100):.1f}%!") diff --git a/src/item/item_finder.py b/src/item/item_finder.py index 27c7f0b..5945a4a 100644 --- a/src/item/item_finder.py +++ b/src/item/item_finder.py @@ -25,7 +25,7 @@ class Item: roi: list[int] = None class ItemFinder: - def __init__(self, config: Config): + def __init__(self): self._item_cropper = ItemCropper() # color range for each type of item # hsv ranges in opencv h: [0-180], s: [0-255], v: [0, 255] @@ -38,7 +38,7 @@ class ItemFinder: "unique": [np.array([23, 80, 140]), np.array([23, 89, 216])], "runes": [np.array([21, 251, 190]), np.array([22, 255, 255])] } - + config = Config() self._items_to_pick = config.items self._folder_name = "items" self._min_score = 0.86 @@ -68,9 +68,6 @@ class ItemFinder: template.blacklist = True self._templates[item_name] = template - def update_items_to_pick(self, config: Config): - self._items_to_pick = config.items - def search(self, inp_img: np.ndarray) -> list[Item]: img = inp_img[:,:,:] start = time.time() @@ -133,7 +130,7 @@ if __name__ == "__main__": from config import Config config = Config() screen = Screen(config.general["monitor"]) - item_finder = ItemFinder(config) + item_finder = ItemFinder() while 1: # img = cv2.imread("") img = screen.grab().copy() diff --git a/src/item/pickit.py b/src/item/pickit.py index 043ffa5..b175b97 100644 --- a/src/item/pickit.py +++ b/src/item/pickit.py @@ -161,7 +161,7 @@ if __name__ == "__main__": belt_manager = BeltManager(screen, t_finder) belt_manager._pot_needs = {"rejuv": 0, "health": 2, "mana": 2} pather = Pather(screen, t_finder) - item_finder = ItemFinder(config) + item_finder = ItemFinder() char = Hammerdin(config.hammerdin, config.char, screen, t_finder, ui_manager, pather) pickit = PickIt(screen, item_finder, ui_manager, belt_manager) print(pickit.pick_up_items(char)) diff --git a/src/main.py b/src/main.py index 014967a..0260305 100644 --- a/src/main.py +++ b/src/main.py @@ -15,7 +15,6 @@ from logger import Logger # Will be initialized in the __main__() function -config = None game_controller = None debugger_controller = None @@ -49,6 +48,7 @@ def on_exit(config: Config): def main(): + config = Config() if config.general["logg_lvl"] == "info": Logger.init(logging.INFO) elif config.general["logg_lvl"] == "debug": @@ -89,9 +89,8 @@ def main(): if __name__ == "__main__": # To avoid cmd just closing down, except any errors and add a input() to the end try: - config = Config(print_warnings=True) - game_controller = GameController(config) - debugger_controller = GraphicDebuggerController(config) + game_controller = GameController() + debugger_controller = GraphicDebuggerController() main() except: traceback.print_exc() diff --git a/src/npc_manager.py b/src/npc_manager.py index 02b66b7..795d0dd 100644 --- a/src/npc_manager.py +++ b/src/npc_manager.py @@ -19,7 +19,7 @@ class Npc: #A2 FARA = "fara" DROGNAN = "droganan" - LYSANDER = "lysander" + LYSANDER = "lysander" #A3 ORMUS = "ormus" #A4 diff --git a/src/shopper.py b/src/shopper.py index 60c483d..1f3de00 100644 --- a/src/shopper.py +++ b/src/shopper.py @@ -12,7 +12,7 @@ from version import __version__ def main(): - config = Config(print_warnings=True) + config = Config() if config.general["logg_lvl"] == "info": Logger.init(logging.INFO) elif config.general["logg_lvl"] == "debug": diff --git a/src/town/town_manager.py b/src/town/town_manager.py index 90d34d9..180b33d 100644 --- a/src/town/town_manager.py +++ b/src/town/town_manager.py @@ -184,7 +184,7 @@ if __name__ == "__main__": npc_manager = NpcManager(screen, template_finder) pather = Pather(screen, template_finder) ui_manager = UiManager(screen, template_finder) - item_finder = ItemFinder(config) + item_finder = ItemFinder() char = Hammerdin(config.hammerdin, config.char, screen, template_finder, ui_manager, pather) a5 = A5(screen, template_finder, pather, char, npc_manager) a4 = A4(screen, template_finder, pather, char, npc_manager) diff --git a/src/ui/char_selector.py b/src/ui/char_selector.py index 9345de3..d00423d 100644 --- a/src/ui/char_selector.py +++ b/src/ui/char_selector.py @@ -6,8 +6,8 @@ from template_finder import TemplateFinder class CharSelector: - def __init__(self, screen: Screen, config: Config, template_finder: TemplateFinder): - self._config = config + def __init__(self, screen: Screen, template_finder: TemplateFinder): + self._config = Config() self._char_template = None self._screen = screen self._template_finder = template_finder @@ -56,7 +56,7 @@ if __name__ == "__main__": config = Config() screen = Screen(config.general["monitor"]) tf = TemplateFinder(screen) - selector = CharSelector(screen, config, tf) + selector = CharSelector(screen, tf) if not selector.has_char_template_saved(): selector.save_char_template() selector.select_char() diff --git a/src/ui/ui_manager.py b/src/ui/ui_manager.py index c646941..ffea83b 100644 --- a/src/ui/ui_manager.py +++ b/src/ui/ui_manager.py @@ -10,7 +10,7 @@ from utils.custom_mouse import mouse from utils.misc import wait, cut_roi, color_filter from logger import Logger -from config import Config +from config import Config, ItemProps from screen import Screen from item import ItemFinder from template_finder import TemplateFinder @@ -387,9 +387,7 @@ class UiManager(): cv2.imwrite("./info_screenshots/info_gold_stash_full_" + time.strftime("%Y%m%d_%H%M%S") + ".png", self._screen.grab()) if self._curr_stash["gold"] > 3: # turn off gold pickup - self._config.char["stash_gold"] = False - self._config.items["misc_gold"].pickit_type = 0 - item_finder.update_items_to_pick(self._config) + self._config.turn_off_goldpickup() # inform user about it Logger.info("All stash tabs and character are full of gold, turn of gold pickup") if self._config.general["custom_message_hook"]: @@ -635,6 +633,6 @@ if __name__ == "__main__": game_stats = GameStats() screen = Screen(config.general["monitor"]) template_finder = TemplateFinder(screen) - item_finder = ItemFinder(config) + item_finder = ItemFinder() ui_manager = UiManager(screen, template_finder, game_stats) ui_manager.stash_all_items(5, item_finder) diff --git a/src/utils/graphic_debugger.py b/src/utils/graphic_debugger.py index 7384304..30095d7 100644 --- a/src/utils/graphic_debugger.py +++ b/src/utils/graphic_debugger.py @@ -26,8 +26,8 @@ class GraphicDebuggerController: window_name_trackbars = "Graphic Debugger - Trackbars" window_name_images = "Graphic Debugger - Images" - def __init__(self, config: Config): - self._config = config + def __init__(self): + self._config = Config() self.screen = None self.item_finder = None self.template_finder = None @@ -43,7 +43,7 @@ class GraphicDebuggerController: def start(self): self.screen = Screen(self._config.general["monitor"]) - self.item_finder = ItemFinder(self._config) + self.item_finder = ItemFinder() self.template_finder = TemplateFinder(self.screen) if self._config.advanced_options['graphic_debugger_layer_creator']: self.debugger_thread = threading.Thread(target=self.run_debugger_processor, daemon=False, name="Debugger-processor") diff --git a/src/utils/item_extractor.py b/src/utils/item_extractor.py index 4a3be89..b0cdb12 100644 --- a/src/utils/item_extractor.py +++ b/src/utils/item_extractor.py @@ -5,7 +5,6 @@ import argparse import os import cv2 import numpy as np -from config import Config from utils.misc import color_filter from item.item_cropper import ItemCropper import time diff --git a/test/item/item_finder_test.py b/test/item/item_finder_test.py index 7285f07..1a8a6a8 100644 --- a/test/item/item_finder_test.py +++ b/test/item/item_finder_test.py @@ -18,7 +18,7 @@ class TestItemFinder: config.items["misc_super_healing_potion"] = ItemProps(pickit_type=1) config.items["magic_small_charm"] = ItemProps(pickit_type=1) config.items["rare_stag_bow"] = ItemProps(pickit_type=1) - self.item_finder = ItemFinder(config) + self.item_finder = ItemFinder() @pytest.mark.parametrize("img_path, expected", [ ("test/assets/item_finder.png", 6),