Support Hammerdin without Engima (#136)
This commit is contained in:
@@ -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: <a href="/assets/docs/sample_graphics.png">Example 1</a>, <a href="/assets/docs/sample_graphics_2.png">Example 2</a></br>
|
||||
**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
|
||||
--------------------------------|---------------------------------------------
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 2.1 MiB |
Binary file not shown.
|
Before Width: | Height: | Size: 1.5 MiB |
BIN
assets/templates/vigor.png
Normal file
BIN
assets/templates/vigor.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.2 KiB |
BIN
assets/templates_1280_720/vigor.png
Normal file
BIN
assets/templates_1280_720/vigor.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.9 KiB |
2
game.ini
2
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
|
||||
|
||||
@@ -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 =====
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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])
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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],
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user