Fixes and fail saves (#44)

* remove old discord, add offset_left, fix coordinate clamping

* proper bot thread
This commit is contained in:
aeon0
2021-11-17 21:12:29 +01:00
committed by GitHub
parent 89bea55100
commit 8f772daf86
13 changed files with 59 additions and 48 deletions
+2 -2
View File
@@ -50,7 +50,8 @@ run_shenk=0
--------------------------------|---------------------------------------------
monitor | Select on which monitor D2R is running in case multiple are available
res | Resolution settings can be any of [1920_1080, 1280_720]
offset_top | Your D2R window should always be in the top left corner. If you are on windowed mode you might have a window bar on top. Measure the pixel height of the bar and set the offset here.
offset_top | Your D2R windows offset from top of the screen (including the window bar). For fullscreen leave at 0.
offset_left | Your D2R window offset from left of screen. For fullscreen leave at 0.
min_game_length_s | Games must have at least this length, will wait in hero selection for if game is too quick (to avoid server connection issues)
exit_key | Pressing this key (anywhere), will force botty to shut down
resume_key | After starting the exe botty will wait for this keypress to atually start botting away
@@ -58,7 +59,6 @@ color_checker_key | Pressing this key will start a debug mode to check if the co
logger_lvl | Can be any of [info, debug] and determines how much output you see on the command line
randomize_runs | If 0, the order will always be pindle -> eldritch/shenk. If 1 the order will be random.
difficulty | Set to `normal` `nightmare` or `hell` for game difficulty
send_drops_to_discord | If 1 sends your drops to the discord channel "drop-log"
custom_discord_hook | Add your own discord hook here to get messages about drops and in case botty got stuck and can not resume
info_screenshots | If 1, the bot takes a screenshot with timestamp on every stuck / chicken / timeout / inventory full event. This is 1 by Default, so remember to clean up the folder every once in a while
loot_screenshots | If 1, the bot takes a screenshot with timestamp everytime he presses show_items button and saves it to loot_screenshots folder. Remember to clear them once in a while...
+1 -1
View File
@@ -2,6 +2,7 @@
monitor=0
res=1920_1080
offset_top=0
offset_left=0
min_game_length_s=65
resume_key=f11
exit_key=f12
@@ -10,7 +11,6 @@ color_checker_key=f10
logg_lvl=info
randomize_runs=0
difficulty=hell
send_drops_to_discord=1
custom_discord_hook=
info_screenshots=1
loot_screenshots=0
+9 -1
View File
@@ -81,6 +81,11 @@ class Bot:
def start(self):
self.trigger('create_game')
def current_game_length(self):
if self._timer is None:
return 0
return time.time() - self._timer
def shuffle_runs(self):
tmp = list(self._do_runs.items())
random.shuffle(tmp)
@@ -97,7 +102,10 @@ class Bot:
def _shut_down(self):
Logger.error("Something went wrong here, bot is unsure about current location. Closing down bot.")
if self._config.general["custom_discord_hook"] != "":
send_discord_thread = threading.Thread(target=send_discord, args=("Botty got stuck and can not resume", self._config.general["custom_discord_hook"]))
send_discord_thread = threading.Thread(
target=send_discord,
args=("Botty got stuck and can not resume", self._config.general["custom_discord_hook"])
)
send_discord_thread.daemon = True
send_discord_thread.start()
self._ui_manager.save_and_exit()
+2 -1
View File
@@ -69,7 +69,8 @@ class IChar:
keyboard.send(self._char_config["tp"])
wait(0.05, 0.1)
mouse.click(button="right")
mouse.move(120, 450, randomize=150, delay_factor=[0.8, 1.4])
# TODO: Add hardcoded coordinates to ini file
mouse.move(int(120 * self._config.scale), int(450 * self._config.scale), randomize=150, delay_factor=[0.8, 1.4])
wait(0.8, 1.3) # takes quite a while for tp to be visible
roi = self._config.ui_roi["tp_search"]
start = time.time()
+18 -19
View File
@@ -37,23 +37,25 @@ class Sorceress(IChar):
if self._char_config["cta_available"]:
self._pre_buff_cta()
def _left_attack(self, cast_pos: Tuple[float, float], delay: float, spray: int = 10):
def _left_attack(self, cast_pos_abs: Tuple[float, float], delay: float, spray: int = 10):
keyboard.send(self._char_config["stand_still"], do_release=False)
mouse.move(cast_pos[0], cast_pos[1])
keyboard.send(self._skill_hotkeys["skill_left"])
if self._skill_hotkeys["skill_left"]:
keyboard.send(self._skill_hotkeys["skill_left"])
for _ in range(6):
x = cast_pos[0] + (random.random() * 2*spray - spray)
y = cast_pos[1] + (random.random() * 2*spray - spray)
mouse.move(x, y)
x = cast_pos_abs[0] + (random.random() * 2*spray - spray)
y = cast_pos_abs[1] + (random.random() * 2*spray - spray)
cast_pos_monitor = self._screen.convert_abs_to_monitor((x, y))
mouse.move(*cast_pos_monitor)
mouse.click(button="left")
wait(delay[0], delay[1])
keyboard.send(self._char_config["stand_still"], do_press=False)
def _main_attack(self, cast_pos: Tuple[float, float], delay: float, spray: float = 10):
def _main_attack(self, cast_pos_abs: Tuple[float, float], delay: float, spray: float = 10):
keyboard.send(self._skill_hotkeys["skill_right"])
x = cast_pos[0] + (random.random() * 2*spray - spray)
y = cast_pos[1] + (random.random() * 2*spray - spray)
mouse.move(x, y)
x = cast_pos_abs[0] + (random.random() * 2*spray - spray)
y = cast_pos_abs[1] + (random.random() * 2*spray - spray)
cast_pos_monitor = self._screen.convert_abs_to_monitor((x, y))
mouse.move(*cast_pos_monitor)
mouse.click(button="right")
wait(delay[0], delay[1])
@@ -65,10 +67,9 @@ class Sorceress(IChar):
pindle_pos_abs = self._pather.find_abs_node_pos(104, self._screen.grab())
if pindle_pos_abs is not None:
cast_pos_abs = [pindle_pos_abs[0] * 0.9, pindle_pos_abs[1] * 0.9]
cast_pos_monitor = self._screen.convert_abs_to_monitor(cast_pos_abs)
for _ in range(int(self._char_config["atk_len_pindle"])):
self._main_attack(cast_pos_monitor, delay, 15)
self._left_attack(cast_pos_monitor, delay, 15)
self._main_attack(cast_pos_abs, delay, 15)
self._left_attack(cast_pos_abs, delay, 15)
wait(0.1, 0.15)
# Move to items
if self._config.char["static_path_pindle"]:
@@ -83,10 +84,9 @@ class Sorceress(IChar):
pos_abs = self._pather.find_abs_node_pos(123, self._screen.grab())
if pos_abs is not None:
cast_pos_abs = [pos_abs[0] * 0.9, pos_abs[1] * 0.9]
cast_pos_monitor = self._screen.convert_abs_to_monitor(cast_pos_abs)
for _ in range(int(self._char_config["atk_len_eldritch"])):
self._main_attack(cast_pos_monitor, delay, 90)
self._left_attack(cast_pos_monitor, delay, 90)
self._main_attack(cast_pos_abs, delay, 90)
self._left_attack(cast_pos_abs, delay, 90)
wait(0.2, 0.3)
# Move to items
if self._config.char["static_path_eldritch"]:
@@ -100,10 +100,9 @@ class Sorceress(IChar):
delay = [0.2, 0.3]
pos_abs = self._pather.find_abs_node_pos(149, self._screen.grab())
cast_pos_abs = [pos_abs[0] * 0.9, pos_abs[1] * 0.9]
cast_pos_monitor = self._screen.convert_abs_to_monitor(cast_pos_abs)
for _ in range(int(self._char_config["atk_len_shenk"])):
self._main_attack(cast_pos_monitor, delay, 90)
self._left_attack(cast_pos_monitor, delay, 90)
self._main_attack(cast_pos_abs, delay, 90)
self._left_attack(cast_pos_abs, delay, 90)
wait(0.2, 0.3)
# Move to items
self._pather.traverse_nodes(Location.SHENK_SAVE_DIST, Location.SHENK_END, self, time_out=2.0, force_tp=True)
+1 -1
View File
@@ -28,6 +28,7 @@ class Config:
"monitor": int(self._select_val("general", "monitor")),
"res": self._select_val("general", "res"),
"offset_top": int(self._select_val("general", "offset_top")),
"offset_left": int(self._select_val("general", "offset_left")),
"min_game_length_s": float(self._select_val("general", "min_game_length_s")),
"exit_key": self._select_val("general", "exit_key"),
"resume_key": self._select_val("general", "resume_key"),
@@ -36,7 +37,6 @@ class Config:
"logg_lvl": self._select_val("general", "logg_lvl"),
"randomize_runs": bool(int(self._select_val("general", "randomize_runs"))),
"difficulty": self._select_val("general", "difficulty"),
"send_drops_to_discord": bool(int(self._select_val("general", "send_drops_to_discord"))),
"custom_discord_hook": self._select_val("general", "custom_discord_hook"),
"info_screenshots": bool(int(self._select_val("general", "info_screenshots"))),
"loot_screenshots": bool(int(self._select_val("general", "loot_screenshots"))),
+3 -2
View File
@@ -3,7 +3,7 @@ from ui_manager import UiManager
import cv2
import time
import keyboard
from utils.misc import kill_thread, cut_roi, color_filter
from utils.misc import kill_thread, cut_roi, color_filter, wait
from logger import Logger
from screen import Screen
import numpy as np
@@ -78,15 +78,16 @@ class HealthManager:
return merc_health_percentage
def _do_chicken(self, img, run_thread):
kill_thread(run_thread)
if self._config.general["info_screenshots"]:
cv2.imwrite("./info_screenshots/info_debug_chicken_" + time.strftime("%Y%m%d_%H%M%S") + ".png", img)
# clean up key presses that might be pressed in the run_thread
keyboard.release(self._config.char["stand_still"])
wait(0.02, 0.05)
keyboard.release(self._config.char["show_items"])
time.sleep(0.01)
self._ui_manager.save_and_exit(does_chicken=True)
self._did_chicken = True
kill_thread(run_thread)
self._do_monitor = False
def start_monitor(self, run_thread: Thread):
+4 -5
View File
@@ -71,12 +71,11 @@ class PickIt:
else:
# send log to discord
if found_items and self._config.items[closest_item.name] == 2 and closest_item.name not in picked_up_items:
if self._config.general["send_drops_to_discord"]:
send_discord_thread = threading.Thread(target=send_discord, args=(f"Botty just found: {closest_item.name}",))
send_discord_thread.daemon = True
send_discord_thread.start()
if self._config.general["custom_discord_hook"] != "":
send_discord_thread = threading.Thread(target=send_discord, args=(f"Botty just found: {closest_item.name}", self._config.general["custom_discord_hook"]))
send_discord_thread = threading.Thread(
target=send_discord,
args=(f"Botty just found: {closest_item.name}", self._config.general["custom_discord_hook"])
)
send_discord_thread.daemon = True
send_discord_thread.start()
picked_up_items.append(closest_item.name)
+5 -4
View File
@@ -12,9 +12,8 @@ import time
import logging
def start_bot():
def start_bot(bot):
try:
bot = Bot()
bot.start()
except KeyboardInterrupt:
Logger.info('Exit (ctrl+c)') or exit()
@@ -45,8 +44,9 @@ if __name__ == "__main__":
while 1:
if keyboard.is_pressed(config.general['resume_key']):
start_bot()
break
bot = Bot()
bot_thread = threading.Thread(target=start_bot, args=(bot,))
bot_thread.start()
if keyboard.is_pressed(config.general['auto_settings_key']):
adjust_settings()
elif keyboard.is_pressed(config.general['color_checker_key']):
@@ -54,5 +54,6 @@ if __name__ == "__main__":
break
time.sleep(0.02)
bot_thread.join()
print("Press Enter to exit ...")
input()
+3 -2
View File
@@ -24,10 +24,11 @@ class Screen:
self._monitor_roi = self._sct.monitors[monitor_idx]
# For windowed screens it is expected to always have them at the top left edge and adjust offset_top then
self._monitor_roi["top"] += config.general["offset_top"]
self._monitor_roi["left"] += config.general["offset_left"]
self._monitor_roi["width"] = config.ui_pos["screen_width"]
self._monitor_roi["height"] = config.ui_pos["screen_height"]
self._monitor_x_range = (self._monitor_roi["left"] + 2, self._monitor_roi["left"] + self._monitor_roi["width"] - 2)
self._monitor_y_range = (self._monitor_roi["top"] + 2, self._monitor_roi["top"] + self._monitor_roi["height"] - 2)
self._monitor_x_range = (self._monitor_roi["left"] + 10, self._monitor_roi["left"] + self._monitor_roi["width"] - 10)
self._monitor_y_range = (self._monitor_roi["top"] + 10, self._monitor_roi["top"] + self._monitor_roi["height"] - 10)
def convert_monitor_to_screen(self, screen_coord: Tuple[float, float]) -> Tuple[float, float]:
return (screen_coord[0] - self._monitor_roi["left"], screen_coord[1] - self._monitor_roi["top"])
+7 -6
View File
@@ -10,7 +10,7 @@ import itertools
import os
import numpy as np
from logger import Logger
from utils.misc import wait, cut_roi, color_filter, send_discord
from utils.misc import wait, cut_roi, color_filter
from config import Config
class UiManager():
@@ -135,13 +135,14 @@ class UiManager():
wait(0.1)
exit_btn_pos = (self._config.ui_pos["save_and_exit_x"], self._config.ui_pos["save_and_exit_y"])
x_m, y_m = self._screen.convert_screen_to_monitor(exit_btn_pos)
away_x_m, away_y_m = self._screen.convert_screen_to_monitor((200, 450))
# TODO: Add hardcoded coordinates to ini file
away_x_m, away_y_m = self._screen.convert_screen_to_monitor((int(170 * self._config.scale), int(400 * self._config.scale)))
templates = ["SAVE_AND_EXIT_NO_HIGHLIGHT","SAVE_AND_EXIT_HIGHLIGHT"]
while self._template_finder.search_and_wait(templates, roi=self._config.ui_roi["save_and_exit"], time_out=1.5, take_ss=False)[0]:
delay = [0.9, 1.1]
if does_chicken:
delay = [0.3, 0.4]
mouse.move(x_m, y_m, randomize=[60, 10], delay_factor=delay)
mouse.move(x_m, y_m, randomize=[38, 7], delay_factor=delay)
wait(0.03, 0.06)
mouse.click(button="left")
if does_chicken:
@@ -149,7 +150,7 @@ class UiManager():
wait(0.05, 0.08)
mouse.click(button="left")
wait(0.1, 0.2)
mouse.move(away_x_m, away_y_m, randomize=100, delay_factor=[0.6, 0.9])
mouse.move(away_x_m, away_y_m, randomize=60, delay_factor=[0.6, 0.9])
wait(0.1, 0.5)
return True
return False
@@ -177,7 +178,7 @@ class UiManager():
Logger.debug(f"Found Play Btn ({mode_info}) -> clicking it")
if mode_info == "online":
Logger.warning("You are creating a game in online mode!")
mouse.move(x, y, randomize=[50, 15], delay_factor=[1.0, 1.8])
mouse.move(x, y, randomize=[50, 9], delay_factor=[1.0, 1.8])
wait(0.1, 0.15)
mouse.click(button="left")
break
@@ -200,7 +201,7 @@ class UiManager():
x, y = self._screen.convert_screen_to_monitor((self._config.ui_pos[f"{difficulty}_x"], self._config.ui_pos[f"{difficulty}_y"]))
Logger.debug(f"Found {difficulty} Btn -> clicking it")
mouse.move(x, y, randomize=[50, 15], delay_factor=[1.0, 1.8])
mouse.move(x, y, randomize=[50, 9], delay_factor=[1.0, 1.8])
wait(0.15, 0.2)
mouse.click(button="left")
break
+3 -3
View File
@@ -9,9 +9,9 @@ import subprocess
from version import __version__
def send_discord(msg, url:str = None):
if url is None:
url = "https://discord.com/api/webhooks/908071105372250213/puaS6gIYqYxTE-TBLAIs6_Qb6ZUwuygSeQfTQkpuXrSag5DPeV2gk0SctOjPy5qMHGeh"
def send_discord(msg, url: str):
if not url:
return
data = {"content": f"{msg} (v{__version__})"}
requests.post(url, json=data)
+1 -1
View File
@@ -1 +1 @@
__version__ = '0.2.2'
__version__ = '0.2.2-dev'