Change default settings (#105)

* Change default settings

* Add another shenk tempalte at start

* fix path

* actually make screenshot with item hovered
This commit is contained in:
aeon0
2021-11-24 18:44:23 +01:00
committed by GitHub
parent 6cf3b4c236
commit 6bea13545a
18 changed files with 61 additions and 44 deletions

View File

@@ -12,7 +12,7 @@ And please. I urge you to actually read that README! It will make your life a lo
Botty only supports English language and screen resolutions 1920x1080 and 1280x720. If your monitor has a higher resolution, you need to either reduce it to one of the two resolutions or set D2R to windowed mode and adjust offset_left and offset_top.
### 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 "Color Test Mode" to verify your settings.</br>
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.
### 2) Supported builds
@@ -30,7 +30,7 @@ Open up D2R and wait till you are at the hero selection screen. Make sure the ch
Download the a prebuilt release [here](https://github.com/aeon0/botty/releases). Start `run.exe` in the botty folder. Switch (ALT+TAB) to your D2R window and press the start key (default f11). You can always force stop botty with f12.
## Color Test Mode
## Graphic Debugger
To check if you graphic settings are good and if the bot would pick up items there is a **Graphic Debugger Mode** built in. Start botty and press F10 (Default key). This will open up a (mostly black) window. Start a game in D2R and go to A5. You should see some templates with blue circles detected and scores printed out to the console. E.g. for 720p you should see scores higher 0.8 for the templates. To check item finding, throw some items of different types on the ground. The debug window should show the item names with black background. If you throw an item on the ground that should be picked up, it will have a red circle. The console will print out the scores for each item that would be picked up. Scores should be well above 0.9 for these items.</br>
<img src="assets/docs/graphic_debugger.png" width="900">
@@ -70,6 +70,7 @@ loot_screenshots | If 1, the bot takes a screenshot with timestamp everytime he
[routes] | Descriptions
--------------------------------|---------------------------------------------
run_pindle | Run Pindle in each new game. Select "1" to run it "0" to leave it out.
run_eldritch | Run Eldritch in each new game. Select "1" to run it "0" to leave it out.
run_shenk | Run shenk in each new game. Select "1" to run it "0" to leave it out.
[char] | Descriptions
@@ -125,10 +126,11 @@ blessed_hammer | Hotkey for Blessed Hammer
[advanced_options] | Descriptions
--------------------------------|---------------------------------------------
pathing_delay_factor | A linear scaling factor, between 1 and 10, applied to pathing delays.
template_threshold | Threshold to find templates used during pathing.
[items] | Descriptions
--------------------------------|---------------------------------------------
item_type | 0: Item will not be picked up. 1: Item will be picked up. 2: Item will be picked up and a discord message will be sent.
## Support this project
This project is free. Support it by contributing in any technical way, giving feedback, bug reports or submitting PRs.
Support it by contributing in any technical way, giving feedback, bug reports or submitting PRs.

View File

@@ -5,7 +5,7 @@
"MaxLuminance": 600,
"HDRContrast": 200,
"Contrast": 100,
"Screen Resolution (Windowed)": "1920x1051",
"Screen Resolution (Windowed)": "1280x720",
"Resolution Scale": 100,
"Sharpening": 6,
"Game Resolution": 1,
@@ -15,7 +15,7 @@
"VSync": 1,
"Framerate Cap": 60,
"Framerate Target": 0,
"Window Mode": 1,
"Window Mode": 0,
"Graphic Presets": 4,
"Texture Quality": 4,
"Texture Anisotropy": 0,
@@ -40,7 +40,7 @@
"Show Clock": 1,
"Auto Gold Enabled": 0,
"Chat Gem Mode": 1,
"Item Name Display": 0,
"Item Name Display": 1,
"Chat Background": 0,
"Always Run": 1
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 MiB

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

View File

@@ -1,15 +1,15 @@
; There is detailed documentation for each parameter in the README.md
[general]
name=Botty
monitor=0
res=1920_1080
offset_top=0
offset_left=0
res=1280_1080
offset_top=160
offset_left=320
max_game_length_s=320
resume_key=f11
exit_key=f12
auto_settings_key=f9
graphic_debugger_key=f10
template_threshold=0.68
logg_lvl=info
randomize_runs=0
difficulty=hell
@@ -45,7 +45,8 @@ inventory_screen=i
stand_still=capslock
tp=f9
belt_rows=4
show_belt=~
;note: this is different from the default hotkey as "~" is for many keyboards not reachable without also pressing altgr
show_belt=k
potion1=1
potion2=2
potion3=3
@@ -76,6 +77,7 @@ blessed_hammer=f5
[advanced_options]
;===== don't touch unless you know what you're doing =====
pathing_delay_factor=5
template_threshold=0.68
[items]
;=====MISC=====

View File

@@ -105,7 +105,7 @@ class BeltManager:
"health": self._config.char["belt_hp_columns"],
"mana": self._config.char["belt_mp_columns"],
}
center_m = self._screen.convert_abs_to_monitor((0, 0))
center_m = self._screen.convert_abs_to_monitor((-200, -120))
mouse.move(*center_m, randomize=100)
keyboard.send(self._config.char["show_belt"])
wait(0.5)

View File

@@ -35,7 +35,6 @@ class Config:
"resume_key": self._select_val("general", "resume_key"),
"auto_settings_key": self._select_val("general", "auto_settings_key"),
"graphic_debugger_key": self._select_val("general", "graphic_debugger_key"),
"template_threshold": float(self._select_val("general", "template_threshold")),
"logg_lvl": self._select_val("general", "logg_lvl"),
"randomize_runs": bool(int(self._select_val("general", "randomize_runs"))),
"difficulty": self._select_val("general", "difficulty"),
@@ -96,7 +95,8 @@ class Config:
self.hammerdin.update(self._custom["hammerdin"])
self.advanced_options = {
"pathing_delay_factor": min(max(int(self._select_val("advanced_options", "pathing_delay_factor")),1),10)
"pathing_delay_factor": min(max(int(self._select_val("advanced_options", "pathing_delay_factor")),1),10),
"template_threshold": float(self._select_val("advanced_options", "template_threshold")),
}
self.items = {}

View File

@@ -48,10 +48,10 @@ class DeathManager:
# first wait a bit to make sure health manager is done with its chicken stuff which obviously failed
if run_thread is not None:
kill_thread(run_thread)
# clean up key presses that might be pressed in the run_thread
keyboard.release(self._config.char["stand_still"])
wait(0.1, 0.2)
keyboard.release(self._config.char["show_items"])
# clean up key presses that might be pressed in the run_thread
keyboard.release(self._config.char["stand_still"])
wait(0.1, 0.2)
keyboard.release(self._config.char["show_items"])
time.sleep(6)
if self._template_finder.search("D2_LOGO_HS", self._screen.grab(), roi=self._config.ui_roi["hero_selection_logo"])[0]:
# in this case chicken executed and left the game, but we were still dead.

View File

@@ -19,6 +19,7 @@ class GameStats:
def _send_discord_thread(self, msg: str):
if self._config.general["custom_discord_hook"]:
msg = f"{self._config.general['name']}: {msg}"
send_discord_thread = threading.Thread(
target=send_discord,
args=(msg, self._config.general["custom_discord_hook"])
@@ -29,7 +30,7 @@ class GameStats:
def log_item_pickup(self, item_name: str, send_discord: bool):
self._picked_up_items.append(item_name)
if send_discord:
self._send_discord_thread(f"{self._config.general['name']} just found: {item_name}")
self._send_discord_thread(f"Found {item_name}")
def log_death(self):
self._death_counter += 1
@@ -37,13 +38,13 @@ class GameStats:
def log_chicken(self):
self._chicken_counter += 1
self._send_discord_thread(f"Winner winner chicken dinner")
self._send_discord_thread(f"You have chickened")
def log_start_game(self):
if self._game_counter > 0:
self._save_stats_to_file()
if self._game_counter % 15 == 0:
# every 10th game send a discord update about current status
if self._game_counter % 20 == 0:
# every 20th game send a discord update about current status
self._send_discord_status_update()
self._game_counter += 1
self._timer = time.time()
@@ -79,7 +80,7 @@ class GameStats:
return msg
def _send_discord_status_update(self):
msg = f"Status Report from {self._config.general['name']}:\n{self._create_msg()}\nVersion:"
msg = f"Status Report\n{self._create_msg()}\nVersion:"
self._send_discord_thread(msg)
def _save_stats_to_file(self):

View File

@@ -79,8 +79,8 @@ class Pather:
122: {"ELDRITCH_2": (530, -218), "ELDRITCH_3": (-223, -178)},
123: {"ELDRITCH_3": (-148, -498), "ELDRITCH_2": (604, -538), "ELDRITCH_4": (-163, -283)},
# Shenk
140: {"SHENK_0": (-224, -340), "SHENK_15": (120, 20), "SHENK_1": (667, -242)},
141: {"SHENK_0": (-194, 66), "SHENK_15": (116, 440), "SHENK_1": (696, 161), "SHENK_2": (-251, -51)},
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)},
142: {"SHENK_1": (876, 564), "SHENK_2": (-78, 352), "SHENK_3": (535, -194), "SHENK_4": (-665, -155)},
143: {"SHENK_2": (212, 758),"SHENK_3": (823, 209), "SHENK_4": (-377, 248), "SHENK_6": (-508, -103)},
144: {"SHENK_6": (-162, 185), "SHENK_7": (721, 226)},
@@ -130,21 +130,27 @@ class Pather:
while 1:
img = self._screen.grab()
display_img = img.copy()
template_map = {}
for node_idx in self._nodes:
for template_type in self._nodes[node_idx]:
if filter is None or filter in template_type:
success, ref_pos_screen = self._template_finder.search(template_type, img)
if template_type in template_map:
success = True
ref_pos_screen = template_map[template_type]
else:
success, ref_pos_screen = self._template_finder.search(template_type, img)
if success:
template_map[template_type] = ref_pos_screen
# Get reference position of template in abs coordinates
ref_pos_abs = self._screen.convert_screen_to_abs(ref_pos_screen)
# Calc the abs node position with the relative coordinates (relative to ref)
# node_pos_rel = self._get_scaled_node(node_idx, template_type)
# node_pos_abs = self._convert_rel_to_abs(node_pos_rel, ref_pos_abs)
# node_pos_abs = self._adjust_abs_range_to_screen(node_pos_abs)
# x, y = self._screen.convert_abs_to_screen(node_pos_abs)
# cv2.circle(display_img, (x, y), 5, (255, 0, 0), 3)
# cv2.putText(display_img, str(node_idx), (x, y), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 0), 2, cv2.LINE_AA)
x, y = self._screen.convert_abs_to_monitor(ref_pos_abs)
node_pos_rel = self._get_scaled_node(node_idx, template_type)
node_pos_abs = self._convert_rel_to_abs(node_pos_rel, ref_pos_abs)
node_pos_abs = self._adjust_abs_range_to_screen(node_pos_abs)
x, y = self._screen.convert_abs_to_screen(node_pos_abs)
cv2.circle(display_img, (x, y), 5, (255, 0, 0), 3)
cv2.putText(display_img, str(node_idx), (x, y), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 0), 2, cv2.LINE_AA)
x, y = self._screen.convert_abs_to_screen(ref_pos_abs)
cv2.circle(display_img, (x, y), 5, (0, 255, 0), 3)
cv2.putText(display_img, template_type, (x, y), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 0), 2, cv2.LINE_AA)
display_img = cv2.resize(display_img, None, fx=0.5, fy=0.5)
@@ -266,5 +272,5 @@ if __name__ == "__main__":
ui_manager = UiManager(screen, t_finder)
char = Sorceress(config.sorceress, 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")
# pather.traverse_nodes(Location.SHENK_START, Location.SHENK_SAVE_DIST, char)
pather._display_all_nodes_debug(filter="SHENK_17")

View File

@@ -29,7 +29,7 @@ class PickIt:
:return: Bool if any items were picked up or not. (Does not account for picking up scrolls and pots)
"""
found_items = False
keyboard.send(self._config.char["show_items"], do_press=True, do_release=False)
keyboard.send(self._config.char["show_items"])
time.sleep(1.0) # sleep needed here to give d2r time to display items on screen on keypress
#Creating a screenshot of the current loot
if self._config.general["loot_screenshots"]:
@@ -90,7 +90,7 @@ class PickIt:
else:
char.move((x_m, y_m))
time.sleep(0.1)
keyboard.send(self._config.char["show_items"], do_press=False, do_release=True)
keyboard.send(self._config.char["show_items"])
return found_items

View File

@@ -78,6 +78,7 @@ if __name__ == "__main__":
break
if keyboard.is_pressed(config.general['auto_settings_key']):
adjust_settings()
break
elif keyboard.is_pressed(config.general['graphic_debugger_key']):
run_graphic_debugger()
break

View File

@@ -1,5 +1,4 @@
import cv2
from enum import Enum
from screen import Screen
from typing import Tuple, Union, List
import numpy as np
@@ -75,6 +74,7 @@ class TemplateFinder:
"SHENK_13": [load_template(f"assets/templates{res_str}/shenk/shenk_13.png", self._scale_factor), self._scale_factor],
"SHENK_15": [load_template(f"assets/templates{res_str}/shenk/shenk_15.png", self._scale_factor), self._scale_factor],
"SHENK_16": [load_template(f"assets/templates{res_str}/shenk/shenk_16.png", self._scale_factor), self._scale_factor],
"SHENK_17": [load_template(f"assets/templates{res_str}/shenk/shenk_17.png", self._scale_factor), self._scale_factor],
# Template Selectables
"A5_STASH": [load_template(f"assets/templates{res_str}/a5_stash.png", self._scale_factor), self._scale_factor],
"A5_WP": [load_template(f"assets/templates{res_str}/a5_wp.png", self._scale_factor), self._scale_factor],
@@ -160,7 +160,7 @@ class TemplateFinder:
:param roi: Region of Interest of the inp_img to restrict search area. Format [left, top, width, height]
:return: Returns found flag and the position as [bool, [x, y]]. If not found, position will be None. Position in image space.
"""
threshold = self._config.general["template_threshold"] if threshold is None else threshold
threshold = self._config.advanced_options["template_threshold"] if threshold is None else threshold
if roi is None:
# if no roi is provided roi = full inp_img
roi = [0, 0, inp_img.shape[1], inp_img.shape[0]]
@@ -210,7 +210,7 @@ class TemplateFinder:
:param take_ss: Bool value to take screenshot on timeout or not (flag must still be set in params!)
Rest of params same as TemplateFinder.search()
"""
threshold = self._config.general["template_threshold"] if threshold is None else threshold
threshold = self._config.advanced_options["template_threshold"] if threshold is None else threshold
Logger.debug(f"Waiting for Template {ref}")
start = time.time()
while 1:

View File

@@ -309,8 +309,11 @@ class UiManager():
else:
# make sure there is actually an item
time.sleep(0.3)
slot_pos, slot_img = self.get_slot_pos_and_img(self._config, self._screen.grab(), column, row)
hovered_item = self._screen.grab()
slot_pos, slot_img = self.get_slot_pos_and_img(self._config, hovered_item, column, row)
if self._slot_has_item(slot_img):
if self._config.general["info_screenshots"]:
cv2.imwrite("./info_screenshots/info_discard_item_" + time.strftime("%Y%m%d_%H%M%S") + ".png", hovered_item)
mouse.press(button="left")
wait(0.2, 0.4)
mouse.release(button="left")

View File

@@ -2,8 +2,10 @@ import json
import os
def adjust_settings():
print("Warning: This will overwrite some of your graphics and gameplay settings. D2R must not be running during this action! Continue with Enter...")
input()
# You might not belive it, but there are cases where users press f9 and just dont read any console output and think everything is done
# so for now, removing this warning as it seems there has never been anybody actually backing up the original settings anyway...
# print("Warning: This will overwrite some of your graphics and gameplay settings. D2R must not be running during this action! Continue with Enter...")
# input()
# Get D2r folder
d2_saved_games = f"C:\\Users\\{os.getlogin()}\\Saved Games\\Diablo II Resurrected"
if not os.path.exists(d2_saved_games):
@@ -21,7 +23,7 @@ def adjust_settings():
# write back to settings.json
with open(d2_saved_games + "\\Settings.json", 'w') as outfile:
json.dump(curr_settings, outfile)
print("Changed settings succesfully. You can now start D2R to start the bot.")
print("Changed settings succesfully. You can now start D2R and botty.")
if __name__ == "__main__":