diff --git a/README.md b/README.md index 538e507..ff35e54 100644 --- a/README.md +++ b/README.md @@ -12,8 +12,8 @@ And please. I urge you to actually read that README! It will make your life a lo Botty only supports English language! Botty is currently working in 1080p or 720p and will try to adjust the D2R settings accordingly depending on your monitor res and your botty setting for "res" in the [general] section. ### 1) Graphics and Gameplay Settings -All settings will automatically be set when you execute `run.exe` and press the hotkey for "Adjust D2R settings" (default f9). Note that D2R should not run during this process, or if it does you will have to restart afterwards. It is not a 100% thing, in rare cases you might still have to fiddle around with your brightness. I suggest using the "Graphic Debugger" to verify your settings. Also, there are sample screenshots of how graphics should look like: Example 1, Example 2
-**Note**: There have been issues reported with image sharpening being truned on via the graphic card settings itself outside of D2R. Try turning it off when running botty. +All settings will automatically be set when you execute `run.exe` and press the hotkey for "Adjust D2R settings" (default f9). Note that D2R should not run during this process, or if it does you will have to restart afterwards. It is not a 100% thing, in rare cases you might still have to fiddle around with your brightness. I suggest using the "Graphic Debugger" to verify your settings. +**Note**: There have been issues reported with image sharpening being truned on via the graphic card settings itself outside of D2R. Try turning it off when running botty. Also HDR should be truned off. ### 2) Supported builds @@ -115,11 +115,12 @@ thunder_storm | Optional Hotkey for thunder storm [hammerdin] | Descriptions --------------------------------|--------------------------------------------- -teleport | Required Hotkey for teleport +teleport | Optional Hotkey for teleport. If left empty hammerdin will run instead of teleport. concentration | Required Hotkey for Concentration -redemption | Optional Hotkey for redemption holy_shield | Hotkey for Holy Shield blessed_hammer | Hotkey for Blessed Hammer +redemption | Optional Hotkey for redemption +vigor | Optional Hotkey for vigor [advanced_options] | Descriptions --------------------------------|--------------------------------------------- diff --git a/assets/docs/sample_graphics.png b/assets/docs/sample_graphics.png deleted file mode 100644 index 184dffc..0000000 Binary files a/assets/docs/sample_graphics.png and /dev/null differ diff --git a/assets/docs/sample_graphics_2.png b/assets/docs/sample_graphics_2.png deleted file mode 100644 index fac4601..0000000 Binary files a/assets/docs/sample_graphics_2.png and /dev/null differ diff --git a/assets/templates/vigor.png b/assets/templates/vigor.png new file mode 100644 index 0000000..23f9d91 Binary files /dev/null and b/assets/templates/vigor.png differ diff --git a/assets/templates_1280_720/vigor.png b/assets/templates_1280_720/vigor.png new file mode 100644 index 0000000..3b93554 Binary files /dev/null and b/assets/templates_1280_720/vigor.png differ diff --git a/game.ini b/game.ini index d2fcc64..e22d6bd 100644 --- a/game.ini +++ b/game.ini @@ -91,6 +91,8 @@ item_height_min=9 item_width_min=30 ; pather reached_node_dist=150 +min_walk_dist=38 +max_walk_dist=190 [ui_roi_1920_1080] ; all rois are in [left, top, width, height] format diff --git a/params.ini b/params.ini index 7748e02..8e89841 100644 --- a/params.ini +++ b/params.ini @@ -69,9 +69,10 @@ telekinesis= [hammerdin] teleport=f1 concentration=f2 -redemption= holy_shield=f4 blessed_hammer=f5 +vigor= +redemption= [advanced_options] ;===== don't touch unless you know what you're doing ===== diff --git a/src/char/hammerdin.py b/src/char/hammerdin.py index 04058f5..3c5308a 100644 --- a/src/char/hammerdin.py +++ b/src/char/hammerdin.py @@ -16,6 +16,11 @@ class Hammerdin(IChar): Logger.info("Setting up Hammerdin") super().__init__(skill_hotkeys, char_config, 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 + # ass most likely we will click on some mobs and already cast hammers + if not self._skill_hotkeys["teleport"]: + self._do_pre_move = False def pre_buff(self): if self._char_config["cta_available"]: @@ -30,7 +35,8 @@ class Hammerdin(IChar): wait(0.05, 0.15) keyboard.send(self._char_config["stand_still"], do_release=False) wait(0.05, 0.15) - keyboard.send(self._skill_hotkeys["blessed_hammer"]) + if self._skill_hotkeys["blessed_hammer"]: + keyboard.send(self._skill_hotkeys["blessed_hammer"]) wait(0.05, 0.15) mouse.press(button="left") start = time.time() @@ -56,7 +62,10 @@ class Hammerdin(IChar): if self._config.char["static_path_pindle"]: self._pather.traverse_nodes_fixed("pindle_end", self) else: - self._pather.traverse_nodes(Location.PINDLE_SAVE_DIST, Location.PINDLE_END, self, time_out=0.5) + if not self._do_pre_move: + keyboard.send(self._skill_hotkeys["concentration"]) + wait(0.05, 0.15) + self._pather.traverse_nodes(Location.PINDLE_SAVE_DIST, Location.PINDLE_END, self, time_out=1.0, do_pre_move=self._do_pre_move) self._cast_hammers(1) # pindle sometimes knocks back, get back in self._pather.traverse_nodes(Location.PINDLE_SAVE_DIST, Location.PINDLE_END, self, time_out=0.1) @@ -69,7 +78,10 @@ class Hammerdin(IChar): if self._config.char["static_path_eldritch"]: self._pather.traverse_nodes_fixed("eldritch_end", self) else: - self._pather.traverse_nodes(Location.ELDRITCH_SAVE_DIST, Location.ELDRITCH_END, self, time_out=0.5) + if not self._do_pre_move: + keyboard.send(self._skill_hotkeys["concentration"]) + wait(0.05, 0.15) + self._pather.traverse_nodes(Location.ELDRITCH_SAVE_DIST, Location.ELDRITCH_END, self, time_out=1.0, do_pre_move=self._do_pre_move) wait(0.05, 0.1) self._cast_hammers(self._char_config["atk_len_eldritch"]) wait(0.05, 0.15) @@ -77,13 +89,26 @@ class Hammerdin(IChar): return True def kill_shenk(self): - self._pather.traverse_nodes(Location.SHENK_SAVE_DIST, Location.SHENK_END, self, time_out=0.2) + if not self._do_pre_move: + keyboard.send(self._skill_hotkeys["concentration"]) + wait(0.05, 0.15) + self._pather.traverse_nodes(Location.SHENK_SAVE_DIST, Location.SHENK_END, self, time_out=1.0, do_pre_move=self._do_pre_move) wait(0.05, 0.1) self._cast_hammers(self._char_config["atk_len_shenk"]) wait(0.05, 0.15) self._do_redemption() return True + def pre_move(self): + # select teleport if available + super().pre_move() + # in case teleport hotkey is not set or teleport can not be used, use vigor if set + should_cast_vigor = self._skill_hotkeys["vigor"] and not self._ui_manager.is_right_skill_selected(["VIGOR"]) + can_teleport = self._skill_hotkeys["teleport"] and self._ui_manager.is_right_skill_active() + if should_cast_vigor and not can_teleport: + keyboard.send(self._skill_hotkeys["vigor"]) + wait(0.15, 0.25) + if __name__ == "__main__": import os @@ -98,10 +123,6 @@ if __name__ == "__main__": pather = Pather(screen, t_finder) ui_manager = UiManager(screen, t_finder) char = Hammerdin(config.hammerdin, config.char, screen, t_finder, ui_manager, pather) - # for i in range(20): - # char.pre_buff() - # time.sleep(1.5) - # char.tp_town() - char.select_by_template("A5_WP") - wait(1.0) - ui_manager.use_wp(4, 1) + char.pre_buff() + pather.traverse_nodes(Location.ELDRITCH_START, Location.ELDRITCH_SAVE_DIST, char) + char.kill_eldritch() diff --git a/src/char/i_char.py b/src/char/i_char.py index 8649306..e5f03bd 100644 --- a/src/char/i_char.py +++ b/src/char/i_char.py @@ -11,6 +11,7 @@ from logger import Logger import time from typing import Dict, Tuple, Union, List from config import Config +import random def abstract(f): @@ -55,12 +56,15 @@ class IChar: return True return False - def move(self, pos_monitor: Tuple[float, float], force_tp: bool = False): - if force_tp or not self._ui_manager.is_teleport_selected(): + def pre_move(self): + # if teleport hotkey is set and if teleport is not already selected + if self._skill_hotkeys["teleport"] and not self._ui_manager.is_right_skill_selected(["TELE_ACTIVE", "TELE_INACTIVE"]): keyboard.send(self._skill_hotkeys["teleport"]) wait(0.15, 0.25) + + def move(self, pos_monitor: Tuple[float, float], force_tp: bool = False): factor = self._config.advanced_options["pathing_delay_factor"] - if force_tp or self._ui_manager.can_teleport(): + if self._skill_hotkeys["teleport"] and (force_tp or self._ui_manager.is_right_skill_active()): mouse.move(pos_monitor[0], pos_monitor[1], randomize=3, delay_factor=[factor*0.1, factor*0.14]) wait(0.012, 0.02) mouse.click(button="right") @@ -70,7 +74,12 @@ class IChar: pos_screen = self._screen.convert_monitor_to_screen(pos_monitor) pos_abs = self._screen.convert_screen_to_abs(pos_screen) dist = math.dist(pos_abs, (0, 0)) - adjust_factor = (dist - 50) / dist + min_wd = self._config.ui_pos["min_walk_dist"] + if self._config.char["slow_walk"]: + max_wd = dist + else: + max_wd = random.randint(int(self._config.ui_pos["max_walk_dist"] * 0.65), self._config.ui_pos["max_walk_dist"]) + adjust_factor = max(max_wd, min(min_wd, dist - 50)) / dist pos_abs = [int(pos_abs[0] * adjust_factor), int(pos_abs[1] * adjust_factor)] x, y = self._screen.convert_abs_to_monitor(pos_abs) mouse.move(x, y, randomize=5, delay_factor=[factor*0.1, factor*0.14]) diff --git a/src/config.py b/src/config.py index 8740a69..2b2d734 100644 --- a/src/config.py +++ b/src/config.py @@ -91,6 +91,9 @@ class Config: self.hammerdin = self._config["hammerdin"] if "hammerdin" in self._custom: self.hammerdin.update(self._custom["hammerdin"]) + if not self.hammerdin["teleport"]: + self.char["static_path_pindle"] = False + self.char["static_path_eldritch"] = False self.advanced_options = { "pathing_delay_factor": min(max(int(self._select_val("advanced_options", "pathing_delay_factor")),1),10), diff --git a/src/pather.py b/src/pather.py index 1f121da..9a304a6 100644 --- a/src/pather.py +++ b/src/pather.py @@ -77,7 +77,7 @@ class Pather: 120: {"ELDRITCH_0": (439, 36), "ELDRITCH_1": (-461, 114)}, 121: {"ELDRITCH_1": (-493, -155), "ELDRITCH_2": (616, 257), "ELDRITCH_3": (-137, 297)}, 122: {"ELDRITCH_2": (530, -218), "ELDRITCH_3": (-223, -178)}, - 123: {"ELDRITCH_3": (-148, -498), "ELDRITCH_2": (604, -538), "ELDRITCH_4": (-163, -283)}, + 123: {"ELDRITCH_3": (-148, -498+120), "ELDRITCH_2": (604, -538+120), "ELDRITCH_4": (-163+70, -283+120)}, # Shenk 140: {"SHENK_0": (-224, -340), "SHENK_17": (-750, 353), "SHENK_15": (120, 20), "SHENK_1": (667, -242)}, 141: {"SHENK_0": (-194, 66), "SHENK_17": (-780, 792), "SHENK_15": (116, 440), "SHENK_1": (696, 161), "SHENK_2": (-251, -51)}, @@ -88,8 +88,7 @@ class Pather: 146: {"SHENK_12": (408, 166), "SHENK_9": (-497, -216), "SHENK_8": (-108, 387)}, 147: {"SHENK_16": (290, -150), "SHENK_9": (-100, 208), "SHENK_10": (-646, 100)}, 148: {"SHENK_16": (967, 95), "SHENK_9": (451, 395), "SHENK_10": (-97, 282), "SHENK_11": (-459, 208)}, - 149: {"SHENK_11": (532, 602), "SHENK_10": (882, 682), "SHENK_13": (730, 36)}, - + 149: {"SHENK_11": (532-140, 642-50), "SHENK_10": (882-140, 682-50), "SHENK_13": (730-140, 36-50)}, } self._paths = { # A5 Town @@ -161,7 +160,8 @@ class Pather: def _convert_rel_to_abs(rel_loc: Tuple[float, float], pos_abs: Tuple[float, float]) -> Tuple[float, float]: return (rel_loc[0] + pos_abs[0], rel_loc[1] + pos_abs[1]) - def traverse_nodes_fixed(self, key: str, char): + def traverse_nodes_fixed(self, key: str, char: IChar): + char.pre_move() path = self._config.path[key] for pos in path: x_m, y_m = self._screen.convert_screen_to_monitor(pos) @@ -212,7 +212,7 @@ class Pather: return node_pos_abs return None - def traverse_nodes(self, start_location: Location, end_location: Location, char: IChar, time_out: float = 7, force_tp: bool = False) -> bool: + def traverse_nodes(self, start_location: Location, end_location: Location, char: IChar, time_out: float = 7, force_tp: bool = False, do_pre_move: bool = True) -> bool: """ Traverse from one location to another :param start_location: Location the char is starting at @@ -223,6 +223,8 @@ class Pather: """ Logger.debug(f"Traverse from {start_location} to {end_location}") path = self._paths[(start_location, end_location)] + if do_pre_move: + char.pre_move() for i, node_idx in enumerate(path): continue_to_next_node = False last_move = time.time() @@ -264,13 +266,14 @@ if __name__ == "__main__": keyboard.wait("f11") from config import Config from char.sorceress import Sorceress + from char.hammerdin import Hammerdin from ui_manager import UiManager config = Config() screen = Screen(config.general["monitor"]) t_finder = TemplateFinder(screen) pather = Pather(screen, t_finder) ui_manager = UiManager(screen, t_finder) - char = Sorceress(config.sorceress, config.char, screen, t_finder, ui_manager, pather) + char = Hammerdin(config.hammerdin, config.char, screen, t_finder, ui_manager, pather) # pather.traverse_nodes_fixed("pindle_save_dist", char) - # pather.traverse_nodes(Location.SHENK_START, Location.SHENK_SAVE_DIST, char) - pather._display_all_nodes_debug(filter="SHENK_17") + # pather.traverse_nodes(Location.A5_TOWN_START, Location.NIHLATHAK_PORTAL, char) + pather._display_all_nodes_debug(filter="SHENK") diff --git a/src/pickit.py b/src/pickit.py index b8a45e9..e0cbe00 100644 --- a/src/pickit.py +++ b/src/pickit.py @@ -40,6 +40,7 @@ class PickIt: start = prev_cast_start = time.time() time_out = False picked_up_items = [] + char.pre_move() while not time_out: if (time.time() - start) > 24: time_out = True diff --git a/src/template_finder.py b/src/template_finder.py index cf90dbd..f8e8d6e 100644 --- a/src/template_finder.py +++ b/src/template_finder.py @@ -92,6 +92,7 @@ class TemplateFinder: "MERC": [load_template(f"assets/templates{res_str}/merc.png", 1.0), 1.0], "TELE_ACTIVE": [load_template(f"assets/templates{res_str}/tele_active.png", 1.0), 1.0], "TELE_INACTIVE": [load_template(f"assets/templates{res_str}/tele_inactive.png", 1.0), 1.0], + "VIGOR": [load_template(f"assets/templates{res_str}/vigor.png", 1.0), 1.0], "REPAIR_BTN": [load_template(f"assets/templates{res_str}/repair_btn.png", 1.0), 1.0], "TP_TOMB": [load_template(f"assets/templates{res_str}/tp_tomb.png", 1.0), 1.0], "SUPER_HEALING_POTION": [load_template(f"assets/templates{res_str}/super_healing_potion.png", 1.0), 1.0], diff --git a/src/ui_manager.py b/src/ui_manager.py index 3b52f45..663c3e7 100644 --- a/src/ui_manager.py +++ b/src/ui_manager.py @@ -12,6 +12,7 @@ from logger import Logger from utils.misc import wait, cut_roi, color_filter, send_discord from config import Config from item_finder import ItemFinder +from typing import List class UiManager(): @@ -41,9 +42,9 @@ class UiManager(): wait(0.4, 0.5) mouse.click(button="left") - def can_teleport(self) -> bool: + def is_right_skill_active(self) -> bool: """ - :return: Bool if teleport is red/available or not. Teleport skill must be selected on right skill slot when calling the function. + :return: Bool if skill is red/available or not. Skill must be selected on right skill slot when calling the function. """ roi = [ self._config.ui_pos["skill_right_x"] - (self._config.ui_pos["skill_width"] // 2), @@ -55,9 +56,9 @@ class UiManager(): avg = np.average(img) return avg > 75.0 - def is_teleport_selected(self) -> bool: + def is_right_skill_selected(self, template_list: List[str]) -> bool: """ - :return: Bool if teleport is currently the selected skill on the right skill slot. + :return: Bool if skill is currently the selected skill on the right skill slot. """ roi = [ self._config.ui_pos["skill_right_x"] - (self._config.ui_pos["skill_width"] // 2), @@ -65,10 +66,9 @@ class UiManager(): self._config.ui_pos["skill_width"], self._config.ui_pos["skill_height"] ] - if self._template_finder.search("TELE_ACTIVE", self._screen.grab(), threshold=0.94, roi=roi)[0]: - return True - if self._template_finder.search("TELE_INACTIVE", self._screen.grab(), threshold=0.94, roi=roi)[0]: - return True + for template in template_list: + if self._template_finder.search(template, self._screen.grab(), threshold=0.94, roi=roi)[0]: + return True return False def is_overburdened(self) -> bool: