Skip to content
This repository has been archived by the owner on Jul 1, 2022. It is now read-only.

Commit

Permalink
Support Hammerdin without Engima (#136)
Browse files Browse the repository at this point in the history
  • Loading branch information
aeon0 authored Nov 28, 2021
1 parent e8080c5 commit 7448c00
Show file tree
Hide file tree
Showing 14 changed files with 78 additions and 36 deletions.
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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
--------------------------------|---------------------------------------------
Expand Down
Binary file removed assets/docs/sample_graphics.png
Binary file not shown.
Binary file removed assets/docs/sample_graphics_2.png
Binary file not shown.
Binary file added assets/templates/vigor.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/templates_1280_720/vigor.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions game.ini
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion params.ini
Original file line number Diff line number Diff line change
Expand Up @@ -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 =====
Expand Down
43 changes: 32 additions & 11 deletions src/char/hammerdin.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ def __init__(self, skill_hotkeys, char_config, screen: Screen, template_finder:
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"]:
Expand All @@ -30,7 +35,8 @@ def _cast_hammers(self, time_in_s: float):
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()
Expand All @@ -56,7 +62,10 @@ def kill_pindle(self) -> bool:
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)
Expand All @@ -69,21 +78,37 @@ def kill_eldritch(self) -> bool:
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)
self._do_redemption()
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
Expand All @@ -98,10 +123,6 @@ def kill_shenk(self):
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()
17 changes: 13 additions & 4 deletions src/char/i_char.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import time
from typing import Dict, Tuple, Union, List
from config import Config
import random


def abstract(f):
Expand Down Expand Up @@ -55,12 +56,15 @@ def select_by_template(self, template_type: Union[str, List[str]], expect_loadi
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")
Expand All @@ -70,7 +74,12 @@ def move(self, pos_monitor: Tuple[float, float], force_tp: bool = False):
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])
Expand Down
3 changes: 3 additions & 0 deletions src/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ def __init__(self, print_warnings: bool = False):
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),
Expand Down
19 changes: 11 additions & 8 deletions src/pather.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ def __init__(self, screen: Screen, template_finder: TemplateFinder):
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)},
Expand All @@ -88,8 +88,7 @@ def __init__(self, screen: Screen, template_finder: TemplateFinder):
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
Expand Down Expand Up @@ -161,7 +160,8 @@ def _display_all_nodes_debug(self, filter: str = None):
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)
Expand Down Expand Up @@ -212,7 +212,7 @@ def find_abs_node_pos(self, node_idx: int, img: np.ndarray) -> Tuple[float, floa
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
Expand All @@ -223,6 +223,8 @@ def traverse_nodes(self, start_location: Location, end_location: Location, char:
"""
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()
Expand Down Expand Up @@ -264,13 +266,14 @@ def traverse_nodes(self, start_location: Location, end_location: Location, char:
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")
1 change: 1 addition & 0 deletions src/pickit.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ def pick_up_items(self, char: IChar) -> bool:
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
Expand Down
1 change: 1 addition & 0 deletions src/template_finder.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ def __init__(self, screen: Screen, scale_factor: float = None):
"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],
Expand Down
16 changes: 8 additions & 8 deletions src/ui_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
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():
Expand Down Expand Up @@ -41,9 +42,9 @@ def use_wp(self, act: int, idx: int):
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),
Expand All @@ -55,20 +56,19 @@ def can_teleport(self) -> bool:
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),
self._config.ui_pos["skill_y"] - (self._config.ui_pos["skill_height"] // 2),
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:
Expand Down

2 comments on commit 7448c00

@bobcho
Copy link
Contributor

@bobcho bobcho commented on 7448c00 Nov 28, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, are there any plans for support for Javazon? It is pretty much like Sorc but without teleport (walking sorc)

@nekolr
Copy link
Contributor

@nekolr nekolr commented on 7448c00 Dec 1, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well done!

Please sign in to comment.