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:
@@ -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.
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
BIN
assets/docs/sample_graphics.png
Normal file
BIN
assets/docs/sample_graphics.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.1 MiB |
BIN
assets/docs/sample_graphics_2.png
Normal file
BIN
assets/docs/sample_graphics_2.png
Normal file
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 |
BIN
assets/templates/shenk/shenk_17.png
Normal file
BIN
assets/templates/shenk/shenk_17.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 102 KiB |
BIN
assets/templates_1280_720/shenk/shenk_17.png
Normal file
BIN
assets/templates_1280_720/shenk/shenk_17.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 62 KiB |
12
params.ini
12
params.ini
@@ -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=====
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 = {}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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__":
|
||||
|
||||
Reference in New Issue
Block a user