From 8f772daf869cd86b3a80fa26032f2d9854191e30 Mon Sep 17 00:00:00 2001 From: aeon0 Date: Wed, 17 Nov 2021 21:12:29 +0100 Subject: [PATCH] Fixes and fail saves (#44) * remove old discord, add offset_left, fix coordinate clamping * proper bot thread --- README.md | 4 ++-- params.ini | 2 +- src/bot.py | 10 +++++++++- src/char/i_char.py | 3 ++- src/char/sorceress.py | 37 ++++++++++++++++++------------------- src/config.py | 2 +- src/health_manager.py | 5 +++-- src/pickit.py | 9 ++++----- src/run.py | 9 +++++---- src/screen.py | 5 +++-- src/ui_manager.py | 13 +++++++------ src/utils/misc.py | 6 +++--- src/version.py | 2 +- 13 files changed, 59 insertions(+), 48 deletions(-) diff --git a/README.md b/README.md index 7a1c6c4..b2afbc4 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,8 @@ run_shenk=0 --------------------------------|--------------------------------------------- monitor | Select on which monitor D2R is running in case multiple are available res | Resolution settings can be any of [1920_1080, 1280_720] -offset_top | Your D2R window should always be in the top left corner. If you are on windowed mode you might have a window bar on top. Measure the pixel height of the bar and set the offset here. +offset_top | Your D2R windows offset from top of the screen (including the window bar). For fullscreen leave at 0. +offset_left | Your D2R window offset from left of screen. For fullscreen leave at 0. min_game_length_s | Games must have at least this length, will wait in hero selection for if game is too quick (to avoid server connection issues) exit_key | Pressing this key (anywhere), will force botty to shut down resume_key | After starting the exe botty will wait for this keypress to atually start botting away @@ -58,7 +59,6 @@ color_checker_key | Pressing this key will start a debug mode to check if the co logger_lvl | Can be any of [info, debug] and determines how much output you see on the command line randomize_runs | If 0, the order will always be pindle -> eldritch/shenk. If 1 the order will be random. difficulty | Set to `normal` `nightmare` or `hell` for game difficulty -send_drops_to_discord | If 1 sends your drops to the discord channel "drop-log" custom_discord_hook | Add your own discord hook here to get messages about drops and in case botty got stuck and can not resume info_screenshots | If 1, the bot takes a screenshot with timestamp on every stuck / chicken / timeout / inventory full event. This is 1 by Default, so remember to clean up the folder every once in a while loot_screenshots | If 1, the bot takes a screenshot with timestamp everytime he presses show_items button and saves it to loot_screenshots folder. Remember to clear them once in a while... diff --git a/params.ini b/params.ini index cfc0931..209e9e4 100644 --- a/params.ini +++ b/params.ini @@ -2,6 +2,7 @@ monitor=0 res=1920_1080 offset_top=0 +offset_left=0 min_game_length_s=65 resume_key=f11 exit_key=f12 @@ -10,7 +11,6 @@ color_checker_key=f10 logg_lvl=info randomize_runs=0 difficulty=hell -send_drops_to_discord=1 custom_discord_hook= info_screenshots=1 loot_screenshots=0 diff --git a/src/bot.py b/src/bot.py index 32ab6c1..0dc63f5 100644 --- a/src/bot.py +++ b/src/bot.py @@ -81,6 +81,11 @@ class Bot: def start(self): self.trigger('create_game') + def current_game_length(self): + if self._timer is None: + return 0 + return time.time() - self._timer + def shuffle_runs(self): tmp = list(self._do_runs.items()) random.shuffle(tmp) @@ -97,7 +102,10 @@ class Bot: def _shut_down(self): Logger.error("Something went wrong here, bot is unsure about current location. Closing down bot.") if self._config.general["custom_discord_hook"] != "": - send_discord_thread = threading.Thread(target=send_discord, args=("Botty got stuck and can not resume", self._config.general["custom_discord_hook"])) + send_discord_thread = threading.Thread( + target=send_discord, + args=("Botty got stuck and can not resume", self._config.general["custom_discord_hook"]) + ) send_discord_thread.daemon = True send_discord_thread.start() self._ui_manager.save_and_exit() diff --git a/src/char/i_char.py b/src/char/i_char.py index 4aedcba..d687284 100644 --- a/src/char/i_char.py +++ b/src/char/i_char.py @@ -69,7 +69,8 @@ class IChar: keyboard.send(self._char_config["tp"]) wait(0.05, 0.1) mouse.click(button="right") - mouse.move(120, 450, randomize=150, delay_factor=[0.8, 1.4]) + # TODO: Add hardcoded coordinates to ini file + mouse.move(int(120 * self._config.scale), int(450 * self._config.scale), randomize=150, delay_factor=[0.8, 1.4]) wait(0.8, 1.3) # takes quite a while for tp to be visible roi = self._config.ui_roi["tp_search"] start = time.time() diff --git a/src/char/sorceress.py b/src/char/sorceress.py index 41f519b..f22f5db 100644 --- a/src/char/sorceress.py +++ b/src/char/sorceress.py @@ -37,23 +37,25 @@ class Sorceress(IChar): if self._char_config["cta_available"]: self._pre_buff_cta() - def _left_attack(self, cast_pos: Tuple[float, float], delay: float, spray: int = 10): + def _left_attack(self, cast_pos_abs: Tuple[float, float], delay: float, spray: int = 10): keyboard.send(self._char_config["stand_still"], do_release=False) - mouse.move(cast_pos[0], cast_pos[1]) - keyboard.send(self._skill_hotkeys["skill_left"]) + if self._skill_hotkeys["skill_left"]: + keyboard.send(self._skill_hotkeys["skill_left"]) for _ in range(6): - x = cast_pos[0] + (random.random() * 2*spray - spray) - y = cast_pos[1] + (random.random() * 2*spray - spray) - mouse.move(x, y) + x = cast_pos_abs[0] + (random.random() * 2*spray - spray) + y = cast_pos_abs[1] + (random.random() * 2*spray - spray) + cast_pos_monitor = self._screen.convert_abs_to_monitor((x, y)) + mouse.move(*cast_pos_monitor) mouse.click(button="left") wait(delay[0], delay[1]) keyboard.send(self._char_config["stand_still"], do_press=False) - def _main_attack(self, cast_pos: Tuple[float, float], delay: float, spray: float = 10): + def _main_attack(self, cast_pos_abs: Tuple[float, float], delay: float, spray: float = 10): keyboard.send(self._skill_hotkeys["skill_right"]) - x = cast_pos[0] + (random.random() * 2*spray - spray) - y = cast_pos[1] + (random.random() * 2*spray - spray) - mouse.move(x, y) + x = cast_pos_abs[0] + (random.random() * 2*spray - spray) + y = cast_pos_abs[1] + (random.random() * 2*spray - spray) + cast_pos_monitor = self._screen.convert_abs_to_monitor((x, y)) + mouse.move(*cast_pos_monitor) mouse.click(button="right") wait(delay[0], delay[1]) @@ -65,10 +67,9 @@ class Sorceress(IChar): pindle_pos_abs = self._pather.find_abs_node_pos(104, self._screen.grab()) if pindle_pos_abs is not None: cast_pos_abs = [pindle_pos_abs[0] * 0.9, pindle_pos_abs[1] * 0.9] - cast_pos_monitor = self._screen.convert_abs_to_monitor(cast_pos_abs) for _ in range(int(self._char_config["atk_len_pindle"])): - self._main_attack(cast_pos_monitor, delay, 15) - self._left_attack(cast_pos_monitor, delay, 15) + self._main_attack(cast_pos_abs, delay, 15) + self._left_attack(cast_pos_abs, delay, 15) wait(0.1, 0.15) # Move to items if self._config.char["static_path_pindle"]: @@ -83,10 +84,9 @@ class Sorceress(IChar): pos_abs = self._pather.find_abs_node_pos(123, self._screen.grab()) if pos_abs is not None: cast_pos_abs = [pos_abs[0] * 0.9, pos_abs[1] * 0.9] - cast_pos_monitor = self._screen.convert_abs_to_monitor(cast_pos_abs) for _ in range(int(self._char_config["atk_len_eldritch"])): - self._main_attack(cast_pos_monitor, delay, 90) - self._left_attack(cast_pos_monitor, delay, 90) + self._main_attack(cast_pos_abs, delay, 90) + self._left_attack(cast_pos_abs, delay, 90) wait(0.2, 0.3) # Move to items if self._config.char["static_path_eldritch"]: @@ -100,10 +100,9 @@ class Sorceress(IChar): delay = [0.2, 0.3] pos_abs = self._pather.find_abs_node_pos(149, self._screen.grab()) cast_pos_abs = [pos_abs[0] * 0.9, pos_abs[1] * 0.9] - cast_pos_monitor = self._screen.convert_abs_to_monitor(cast_pos_abs) for _ in range(int(self._char_config["atk_len_shenk"])): - self._main_attack(cast_pos_monitor, delay, 90) - self._left_attack(cast_pos_monitor, delay, 90) + self._main_attack(cast_pos_abs, delay, 90) + self._left_attack(cast_pos_abs, delay, 90) wait(0.2, 0.3) # Move to items self._pather.traverse_nodes(Location.SHENK_SAVE_DIST, Location.SHENK_END, self, time_out=2.0, force_tp=True) diff --git a/src/config.py b/src/config.py index e94415e..29b3ad7 100644 --- a/src/config.py +++ b/src/config.py @@ -28,6 +28,7 @@ class Config: "monitor": int(self._select_val("general", "monitor")), "res": self._select_val("general", "res"), "offset_top": int(self._select_val("general", "offset_top")), + "offset_left": int(self._select_val("general", "offset_left")), "min_game_length_s": float(self._select_val("general", "min_game_length_s")), "exit_key": self._select_val("general", "exit_key"), "resume_key": self._select_val("general", "resume_key"), @@ -36,7 +37,6 @@ class Config: "logg_lvl": self._select_val("general", "logg_lvl"), "randomize_runs": bool(int(self._select_val("general", "randomize_runs"))), "difficulty": self._select_val("general", "difficulty"), - "send_drops_to_discord": bool(int(self._select_val("general", "send_drops_to_discord"))), "custom_discord_hook": self._select_val("general", "custom_discord_hook"), "info_screenshots": bool(int(self._select_val("general", "info_screenshots"))), "loot_screenshots": bool(int(self._select_val("general", "loot_screenshots"))), diff --git a/src/health_manager.py b/src/health_manager.py index 1dc7209..6d2e538 100644 --- a/src/health_manager.py +++ b/src/health_manager.py @@ -3,7 +3,7 @@ from ui_manager import UiManager import cv2 import time import keyboard -from utils.misc import kill_thread, cut_roi, color_filter +from utils.misc import kill_thread, cut_roi, color_filter, wait from logger import Logger from screen import Screen import numpy as np @@ -78,15 +78,16 @@ class HealthManager: return merc_health_percentage def _do_chicken(self, img, run_thread): + kill_thread(run_thread) if self._config.general["info_screenshots"]: cv2.imwrite("./info_screenshots/info_debug_chicken_" + time.strftime("%Y%m%d_%H%M%S") + ".png", img) # clean up key presses that might be pressed in the run_thread keyboard.release(self._config.char["stand_still"]) + wait(0.02, 0.05) keyboard.release(self._config.char["show_items"]) time.sleep(0.01) self._ui_manager.save_and_exit(does_chicken=True) self._did_chicken = True - kill_thread(run_thread) self._do_monitor = False def start_monitor(self, run_thread: Thread): diff --git a/src/pickit.py b/src/pickit.py index 770da9a..3a4f7b3 100644 --- a/src/pickit.py +++ b/src/pickit.py @@ -71,12 +71,11 @@ class PickIt: else: # send log to discord if found_items and self._config.items[closest_item.name] == 2 and closest_item.name not in picked_up_items: - if self._config.general["send_drops_to_discord"]: - send_discord_thread = threading.Thread(target=send_discord, args=(f"Botty just found: {closest_item.name}",)) - send_discord_thread.daemon = True - send_discord_thread.start() if self._config.general["custom_discord_hook"] != "": - send_discord_thread = threading.Thread(target=send_discord, args=(f"Botty just found: {closest_item.name}", self._config.general["custom_discord_hook"])) + send_discord_thread = threading.Thread( + target=send_discord, + args=(f"Botty just found: {closest_item.name}", self._config.general["custom_discord_hook"]) + ) send_discord_thread.daemon = True send_discord_thread.start() picked_up_items.append(closest_item.name) diff --git a/src/run.py b/src/run.py index d1de2b9..0e16007 100644 --- a/src/run.py +++ b/src/run.py @@ -12,9 +12,8 @@ import time import logging -def start_bot(): +def start_bot(bot): try: - bot = Bot() bot.start() except KeyboardInterrupt: Logger.info('Exit (ctrl+c)') or exit() @@ -45,8 +44,9 @@ if __name__ == "__main__": while 1: if keyboard.is_pressed(config.general['resume_key']): - start_bot() - break + bot = Bot() + bot_thread = threading.Thread(target=start_bot, args=(bot,)) + bot_thread.start() if keyboard.is_pressed(config.general['auto_settings_key']): adjust_settings() elif keyboard.is_pressed(config.general['color_checker_key']): @@ -54,5 +54,6 @@ if __name__ == "__main__": break time.sleep(0.02) + bot_thread.join() print("Press Enter to exit ...") input() diff --git a/src/screen.py b/src/screen.py index edc52a6..881b603 100644 --- a/src/screen.py +++ b/src/screen.py @@ -24,10 +24,11 @@ class Screen: self._monitor_roi = self._sct.monitors[monitor_idx] # For windowed screens it is expected to always have them at the top left edge and adjust offset_top then self._monitor_roi["top"] += config.general["offset_top"] + self._monitor_roi["left"] += config.general["offset_left"] self._monitor_roi["width"] = config.ui_pos["screen_width"] self._monitor_roi["height"] = config.ui_pos["screen_height"] - self._monitor_x_range = (self._monitor_roi["left"] + 2, self._monitor_roi["left"] + self._monitor_roi["width"] - 2) - self._monitor_y_range = (self._monitor_roi["top"] + 2, self._monitor_roi["top"] + self._monitor_roi["height"] - 2) + self._monitor_x_range = (self._monitor_roi["left"] + 10, self._monitor_roi["left"] + self._monitor_roi["width"] - 10) + self._monitor_y_range = (self._monitor_roi["top"] + 10, self._monitor_roi["top"] + self._monitor_roi["height"] - 10) def convert_monitor_to_screen(self, screen_coord: Tuple[float, float]) -> Tuple[float, float]: return (screen_coord[0] - self._monitor_roi["left"], screen_coord[1] - self._monitor_roi["top"]) diff --git a/src/ui_manager.py b/src/ui_manager.py index 567cdb2..3338f9a 100644 --- a/src/ui_manager.py +++ b/src/ui_manager.py @@ -10,7 +10,7 @@ import itertools import os import numpy as np from logger import Logger -from utils.misc import wait, cut_roi, color_filter, send_discord +from utils.misc import wait, cut_roi, color_filter from config import Config class UiManager(): @@ -135,13 +135,14 @@ class UiManager(): wait(0.1) exit_btn_pos = (self._config.ui_pos["save_and_exit_x"], self._config.ui_pos["save_and_exit_y"]) x_m, y_m = self._screen.convert_screen_to_monitor(exit_btn_pos) - away_x_m, away_y_m = self._screen.convert_screen_to_monitor((200, 450)) + # TODO: Add hardcoded coordinates to ini file + away_x_m, away_y_m = self._screen.convert_screen_to_monitor((int(170 * self._config.scale), int(400 * self._config.scale))) templates = ["SAVE_AND_EXIT_NO_HIGHLIGHT","SAVE_AND_EXIT_HIGHLIGHT"] while self._template_finder.search_and_wait(templates, roi=self._config.ui_roi["save_and_exit"], time_out=1.5, take_ss=False)[0]: delay = [0.9, 1.1] if does_chicken: delay = [0.3, 0.4] - mouse.move(x_m, y_m, randomize=[60, 10], delay_factor=delay) + mouse.move(x_m, y_m, randomize=[38, 7], delay_factor=delay) wait(0.03, 0.06) mouse.click(button="left") if does_chicken: @@ -149,7 +150,7 @@ class UiManager(): wait(0.05, 0.08) mouse.click(button="left") wait(0.1, 0.2) - mouse.move(away_x_m, away_y_m, randomize=100, delay_factor=[0.6, 0.9]) + mouse.move(away_x_m, away_y_m, randomize=60, delay_factor=[0.6, 0.9]) wait(0.1, 0.5) return True return False @@ -177,7 +178,7 @@ class UiManager(): Logger.debug(f"Found Play Btn ({mode_info}) -> clicking it") if mode_info == "online": Logger.warning("You are creating a game in online mode!") - mouse.move(x, y, randomize=[50, 15], delay_factor=[1.0, 1.8]) + mouse.move(x, y, randomize=[50, 9], delay_factor=[1.0, 1.8]) wait(0.1, 0.15) mouse.click(button="left") break @@ -200,7 +201,7 @@ class UiManager(): x, y = self._screen.convert_screen_to_monitor((self._config.ui_pos[f"{difficulty}_x"], self._config.ui_pos[f"{difficulty}_y"])) Logger.debug(f"Found {difficulty} Btn -> clicking it") - mouse.move(x, y, randomize=[50, 15], delay_factor=[1.0, 1.8]) + mouse.move(x, y, randomize=[50, 9], delay_factor=[1.0, 1.8]) wait(0.15, 0.2) mouse.click(button="left") break diff --git a/src/utils/misc.py b/src/utils/misc.py index 500880f..bc0fad6 100644 --- a/src/utils/misc.py +++ b/src/utils/misc.py @@ -9,9 +9,9 @@ import subprocess from version import __version__ -def send_discord(msg, url:str = None): - if url is None: - url = "https://discord.com/api/webhooks/908071105372250213/puaS6gIYqYxTE-TBLAIs6_Qb6ZUwuygSeQfTQkpuXrSag5DPeV2gk0SctOjPy5qMHGeh" +def send_discord(msg, url: str): + if not url: + return data = {"content": f"{msg} (v{__version__})"} requests.post(url, json=data) diff --git a/src/version.py b/src/version.py index 020ed73..e7b044d 100644 --- a/src/version.py +++ b/src/version.py @@ -1 +1 @@ -__version__ = '0.2.2' +__version__ = '0.2.2-dev'