Bugfix/enhancement: Optimize waits; implement wait_for_expiration(ScreenObject) function (#629)

* init

* simplify

* the

* init

* update

* init

* fix belt open

* rework waiting functions in ui_manager.py
This commit is contained in:
mgleed
2022-03-13 20:19:41 -04:00
committed by GitHub
parent 7564fea4b0
commit 2c09459bde
40 changed files with 271 additions and 260 deletions
+7 -16
View File
@@ -27,7 +27,7 @@ from char.barbarian import Barbarian
from char.necro import Necro
from char.basic import Basic
from char.basic_ranged import Basic_Ranged
from ui_manager import wait_for_screen_object, detect_screen_object, ScreenObjects
from ui_manager import wait_until_hidden, wait_until_visible, detect_screen_object, ScreenObjects, is_visible
from ui import meters, skills, view, character_select, main_menu
from inventory import personal, vendor, belt, common, consumables
@@ -105,7 +105,6 @@ class Bot:
self._diablo = Diablo(self._pather, self._town_manager, self._char, self._pickit)
# Create member variables
self._pick_corpse = False
self._picked_up_items = False
self._curr_loc: Union[bool, Location] = None
self._use_id_tome = True
@@ -218,8 +217,7 @@ class Bot:
def on_create_game(self):
# Start a game from hero selection
m = wait_for_screen_object(ScreenObjects.MainMenu)
if m.valid:
if (m := wait_until_visible(ScreenObjects.MainMenu)).valid:
if "DARK" in m.name:
keyboard.send("esc")
main_menu.start_game()
@@ -258,16 +256,13 @@ class Bot:
view.return_to_play()
# Handle picking up corpse in case of death
self._pick_corpse = detect_screen_object(ScreenObjects.Corpse).valid
if self._pick_corpse:
if (corpse_present := is_visible(ScreenObjects.Corpse)):
self._previous_run_failed = True
time.sleep(1.6)
view.pickup_corpse()
wait(1.2, 1.5)
wait_until_hidden(ScreenObjects.Corpse)
belt.fill_up_belt_from_inventory(Config().char["num_loot_columns"])
wait(0.5)
self._char.discover_capabilities()
if self._pick_corpse and self._char.capabilities.can_teleport_with_charges and not self._char.select_tp():
if corpse_present and self._char.capabilities.can_teleport_with_charges and not self._char.select_tp():
keybind = self._char._skill_hotkeys["teleport"]
Logger.info(f"Teleport keybind is lost upon death. Rebinding teleport to '{keybind}'")
self._char.remap_right_skill_hotkey("TELE_ACTIVE", self._char._skill_hotkeys["teleport"])
@@ -285,7 +280,7 @@ class Bot:
# Update TP, ID, key needs
if self._game_stats._game_counter == 1:
self._use_id_tome = common.tome_state(img, 'id')[0] is not None
self._use_keys = detect_screen_object(ScreenObjects.Key, img).valid
self._use_keys = is_visible(ScreenObjects.Key, img)
if (self._game_stats._run_counter - 1) % 4 == 0 or self._previous_run_failed:
consumables.update_tome_key_needs(img, item_type = 'tp')
if self._use_id_tome:
@@ -327,7 +322,6 @@ class Bot:
items = result_items
sell_items = any([item.sell for item in items]) if items else None
Logger.debug(f"Needs: {consumables.get_needs()}")
wait(0.5, 0.8)
elif meters.get_health(img) < 0.6 or meters.get_mana(img) < 0.2:
Logger.info("Healing at next possible Vendor")
self._curr_loc = self._town_manager.heal(self._curr_loc)
@@ -344,10 +338,9 @@ class Bot:
if not self._curr_loc:
return self.trigger_or_stop("end_game", failed=True)
self._picked_up_items = False
wait(1.0)
# Check if we are out of tps or need repairing
need_repair = detect_screen_object(ScreenObjects.NeedRepair).valid
need_repair = is_visible(ScreenObjects.NeedRepair)
need_routine_repair = False if not Config().char["runs_per_repair"] else self._game_stats._run_counter % Config().char["runs_per_repair"] == 0
need_refill_teleport = self._char.capabilities.can_teleport_with_charges and (not self._char.select_tp() or self._char.is_low_on_teleport_charges())
if need_repair or need_routine_repair or need_refill_teleport or sell_items:
@@ -364,7 +357,6 @@ class Bot:
items = result_items
if not self._curr_loc:
return self.trigger_or_stop("end_game", failed=True)
wait(1.0)
# Check if merc needs to be revived
match = detect_screen_object(ScreenObjects.MercIcon)
@@ -407,7 +399,6 @@ class Bot:
self._do_runs = copy(self._do_runs_reset)
if Config().general["randomize_runs"]:
self.shuffle_runs()
wait(0.2, 0.5)
self.trigger_or_stop("init")
def on_end_run(self):
+7 -7
View File
@@ -93,8 +93,8 @@ class Barbarian(IChar):
if not self._do_pre_move:
# keyboard.send(self._skill_hotkeys["concentration"])
# wait(0.05, 0.15)
self._pather.traverse_nodes((Location.A5_PINDLE_SAFE_DIST, Location.A5_PINDLE_END), self, time_out=1.0, do_pre_move=self._do_pre_move)
self._pather.traverse_nodes((Location.A5_PINDLE_SAFE_DIST, Location.A5_PINDLE_END), self, time_out=0.1)
self._pather.traverse_nodes((Location.A5_PINDLE_SAFE_DIST, Location.A5_PINDLE_END), self, timeout=1.0, do_pre_move=self._do_pre_move)
self._pather.traverse_nodes((Location.A5_PINDLE_SAFE_DIST, Location.A5_PINDLE_END), self, timeout=0.1)
self._cast_war_cry(Config().char["atk_len_pindle"])
wait(0.1, 0.15)
self._do_hork(4)
@@ -104,7 +104,7 @@ class Barbarian(IChar):
if self.capabilities.can_teleport_natively:
self._pather.traverse_nodes_fixed("eldritch_end", self)
else:
self._pather.traverse_nodes((Location.A5_ELDRITCH_SAFE_DIST, Location.A5_ELDRITCH_END), self, time_out=1.0, do_pre_move=self._do_pre_move)
self._pather.traverse_nodes((Location.A5_ELDRITCH_SAFE_DIST, Location.A5_ELDRITCH_END), self, timeout=1.0, do_pre_move=self._do_pre_move)
wait(0.05, 0.1)
self._cast_war_cry(Config().char["atk_len_eldritch"])
wait(0.1, 0.15)
@@ -112,7 +112,7 @@ class Barbarian(IChar):
return True
def kill_shenk(self):
self._pather.traverse_nodes((Location.A5_SHENK_SAFE_DIST, Location.A5_SHENK_END), self, time_out=1.0, do_pre_move=self._do_pre_move)
self._pather.traverse_nodes((Location.A5_SHENK_SAFE_DIST, Location.A5_SHENK_END), self, timeout=1.0, do_pre_move=self._do_pre_move)
wait(0.05, 0.1)
self._cast_war_cry(Config().char["atk_len_shenk"])
wait(0.1, 0.15)
@@ -123,14 +123,14 @@ class Barbarian(IChar):
# Check out the node screenshot in assets/templates/trav/nodes to see where each node is at
atk_len = Config().char["atk_len_trav"]
# Go inside and war cry a bit
self._pather.traverse_nodes([228, 229], self, time_out=2.5, force_tp=True)
self._pather.traverse_nodes([228, 229], self, timeout=2.5, force_tp=True)
self._cast_war_cry(atk_len)
# Move a bit back and another round
self._move_and_attack((40, 20), atk_len)
# Here we have two different attack sequences depending if tele is available or not
if self.capabilities.can_teleport_natively:
# Back to center stairs and more war cry
self._pather.traverse_nodes([226], self, time_out=2.5, force_tp=True)
self._pather.traverse_nodes([226], self, timeout=2.5, force_tp=True)
self._cast_war_cry(atk_len)
# move a bit to the top
self._move_and_attack((65, -30), atk_len)
@@ -143,7 +143,7 @@ class Barbarian(IChar):
def kill_nihlathak(self, end_nodes: list[int]) -> bool:
# Move close to nihlathak
self._pather.traverse_nodes(end_nodes, self, time_out=0.8, do_pre_move=False)
self._pather.traverse_nodes(end_nodes, self, timeout=0.8, do_pre_move=False)
# move mouse to center (leftover from hammerdin)
pos_m = convert_abs_to_monitor((0, 0))
mouse.move(*pos_m, randomize=80, delay_factor=[0.5, 0.7])
+7 -7
View File
@@ -75,8 +75,8 @@ class Basic(IChar):
if not self._do_pre_move:
# keyboard.send(self._skill_hotkeys["concentration"])
# wait(0.05, 0.15)
self._pather.traverse_nodes((Location.A5_PINDLE_SAFE_DIST, Location.A5_PINDLE_END), self, time_out=1.0, do_pre_move=self._do_pre_move)
self._pather.traverse_nodes((Location.A5_PINDLE_SAFE_DIST, Location.A5_PINDLE_END), self, time_out=0.1)
self._pather.traverse_nodes((Location.A5_PINDLE_SAFE_DIST, Location.A5_PINDLE_END), self, timeout=1.0, do_pre_move=self._do_pre_move)
self._pather.traverse_nodes((Location.A5_PINDLE_SAFE_DIST, Location.A5_PINDLE_END), self, timeout=0.1)
self._cast_attack_pattern(Config().char["atk_len_pindle"])
wait(0.1, 0.15)
return True
@@ -88,7 +88,7 @@ class Basic(IChar):
if not self._do_pre_move:
# keyboard.send(self._skill_hotkeys["concentration"])
# wait(0.05, 0.15)
self._pather.traverse_nodes((Location.A5_ELDRITCH_SAFE_DIST, Location.A5_ELDRITCH_END), self, time_out=1.0, do_pre_move=self._do_pre_move)
self._pather.traverse_nodes((Location.A5_ELDRITCH_SAFE_DIST, Location.A5_ELDRITCH_END), self, timeout=1.0, do_pre_move=self._do_pre_move)
wait(0.05, 0.1)
self._cast_attack_pattern(Config().char["atk_len_eldritch"])
return True
@@ -97,7 +97,7 @@ class Basic(IChar):
# if not self._do_pre_move:
# keyboard.send(self._skill_hotkeys["concentration"])
# wait(0.05, 0.15)
self._pather.traverse_nodes((Location.A5_SHENK_SAFE_DIST, Location.A5_SHENK_END), self, time_out=1.0, do_pre_move=self._do_pre_move)
self._pather.traverse_nodes((Location.A5_SHENK_SAFE_DIST, Location.A5_SHENK_END), self, timeout=1.0, do_pre_move=self._do_pre_move)
wait(0.05, 0.1)
self._cast_attack_pattern(Config().char["atk_len_shenk"])
wait(0.1, 0.15)
@@ -107,14 +107,14 @@ class Basic(IChar):
# Check out the node screenshot in assets/templates/trav/nodes to see where each node is at
atk_len = Config().char["atk_len_trav"]
# Go inside and war cry a bit
self._pather.traverse_nodes([228, 229], self, time_out=2.5, force_tp=True)
self._pather.traverse_nodes([228, 229], self, timeout=2.5, force_tp=True)
self._cast_attack_pattern(atk_len)
# Move a bit back and another round
self._move_and_attack((40, 20), atk_len)
# Here we have two different attack sequences depending if tele is available or not
if self.capabilities.can_teleport_natively:
# Back to center stairs and more war cry
self._pather.traverse_nodes([226], self, time_out=2.5, force_tp=True)
self._pather.traverse_nodes([226], self, timeout=2.5, force_tp=True)
self._cast_attack_pattern(atk_len)
# move a bit to the top
self._move_and_attack((65, -30), atk_len)
@@ -126,7 +126,7 @@ class Basic(IChar):
def kill_nihlathak(self, end_nodes: list[int]) -> bool:
# Move close to nihlathak
self._pather.traverse_nodes(end_nodes, self, time_out=0.8, do_pre_move=False)
self._pather.traverse_nodes(end_nodes, self, timeout=0.8, do_pre_move=False)
# move mouse to center (leftover from hammerdin)
pos_m = convert_abs_to_monitor((0, 0))
mouse.move(*pos_m, randomize=80, delay_factor=[0.5, 0.7])
+6 -6
View File
@@ -72,7 +72,7 @@ class Basic_Ranged(IChar):
keyboard.send(Config().char["stand_still"], do_press=False)
wait(self._cast_duration, self._cast_duration + 0.2)
# Move to items
self._pather.traverse_nodes((Location.A5_PINDLE_SAFE_DIST, Location.A5_PINDLE_END), self, time_out=1.4, force_tp=True)
self._pather.traverse_nodes((Location.A5_PINDLE_SAFE_DIST, Location.A5_PINDLE_END), self, timeout=1.4, force_tp=True)
return True
@@ -91,7 +91,7 @@ class Basic_Ranged(IChar):
keyboard.send(Config().char["stand_still"], do_press=False)
wait(self._cast_duration, self._cast_duration + 0.2)
# Move to items
self._pather.traverse_nodes((Location.A5_ELDRITCH_SAFE_DIST, Location.A5_ELDRITCH_END), self, time_out=1.4, force_tp=True)
self._pather.traverse_nodes((Location.A5_ELDRITCH_SAFE_DIST, Location.A5_ELDRITCH_END), self, timeout=1.4, force_tp=True)
return True
def kill_shenk(self) -> bool:
@@ -111,7 +111,7 @@ class Basic_Ranged(IChar):
keyboard.send(Config().char["stand_still"], do_press=False)
wait(self._cast_duration, self._cast_duration + 0.2)
# Move to items
self._pather.traverse_nodes((Location.A5_SHENK_SAFE_DIST, Location.A5_SHENK_END), self, time_out=1.4, force_tp=True)
self._pather.traverse_nodes((Location.A5_SHENK_SAFE_DIST, Location.A5_SHENK_END), self, timeout=1.4, force_tp=True)
return True
def kill_council(self) -> bool:
@@ -119,7 +119,7 @@ class Basic_Ranged(IChar):
# Check out the node screenshot in assets/templates/trav/nodes to see where each node is at
# Go inside cast stuff in general direction
self._pather.offset_node(229, [250, 130])
self._pather.traverse_nodes([228, 229], self, time_out=2.5, force_tp=True)
self._pather.traverse_nodes([228, 229], self, timeout=2.5, force_tp=True)
self._pather.offset_node(229, [-250, -130])
atk_pos_abs = self._pather.find_abs_node_pos(230, grab())
if atk_pos_abs is None:
@@ -149,7 +149,7 @@ class Basic_Ranged(IChar):
self._left_attack(cast_pos_abs, spray=11)
# Move outside
# Move a bit back and another round
self._pather.traverse_nodes([226], self, time_out=2.5, force_tp=True)
self._pather.traverse_nodes([226], self, timeout=2.5, force_tp=True)
cast_pos_abs = np.array([-300, -100])
for _ in range(atk_len_trav_2):
if skills.is_right_skill_active():
@@ -183,7 +183,7 @@ class Basic_Ranged(IChar):
self._left_attack(cast_pos_abs, spray=11)
# Move to items
wait(self._cast_duration, self._cast_duration + 0.2)
self._pather.traverse_nodes(end_nodes, self, time_out=0.8)
self._pather.traverse_nodes(end_nodes, self, timeout=0.8)
return True
+56 -56
View File
@@ -81,7 +81,7 @@ class Hammerdin(IChar):
if not self._do_pre_move:
keyboard.send(self._skill_hotkeys["concentration"])
wait(0.05, 0.15)
self._pather.traverse_nodes((Location.A5_PINDLE_SAFE_DIST, Location.A5_PINDLE_END), self, time_out=1.0, do_pre_move=self._do_pre_move)
self._pather.traverse_nodes((Location.A5_PINDLE_SAFE_DIST, Location.A5_PINDLE_END), self, timeout=1.0, do_pre_move=self._do_pre_move)
self._cast_hammers(Config().char["atk_len_pindle"])
wait(0.1, 0.15)
self._cast_hammers(1.6, "redemption")
@@ -95,7 +95,7 @@ class Hammerdin(IChar):
if not self._do_pre_move:
keyboard.send(self._skill_hotkeys["concentration"])
wait(0.05, 0.15)
self._pather.traverse_nodes((Location.A5_ELDRITCH_SAFE_DIST, Location.A5_ELDRITCH_END), self, time_out=1.0, do_pre_move=self._do_pre_move, force_tp=True, use_tp_charge=True)
self._pather.traverse_nodes((Location.A5_ELDRITCH_SAFE_DIST, Location.A5_ELDRITCH_END), self, timeout=1.0, do_pre_move=self._do_pre_move, force_tp=True, use_tp_charge=True)
wait(0.05, 0.1)
self._cast_hammers(Config().char["atk_len_eldritch"])
wait(0.1, 0.15)
@@ -106,7 +106,7 @@ class Hammerdin(IChar):
if not self._do_pre_move:
keyboard.send(self._skill_hotkeys["concentration"])
wait(0.05, 0.15)
self._pather.traverse_nodes((Location.A5_SHENK_SAFE_DIST, Location.A5_SHENK_END), self, time_out=1.0, do_pre_move=self._do_pre_move, force_tp=True, use_tp_charge=True)
self._pather.traverse_nodes((Location.A5_SHENK_SAFE_DIST, Location.A5_SHENK_END), self, timeout=1.0, do_pre_move=self._do_pre_move, force_tp=True, use_tp_charge=True)
wait(0.05, 0.1)
self._cast_hammers(Config().char["atk_len_shenk"])
wait(0.1, 0.15)
@@ -120,14 +120,14 @@ class Hammerdin(IChar):
# Check out the node screenshot in assets/templates/trav/nodes to see where each node is at
atk_len = Config().char["atk_len_trav"]
# Go inside and hammer a bit
self._pather.traverse_nodes([228, 229], self, time_out=2.5, force_tp=True, use_tp_charge=True)
self._pather.traverse_nodes([228, 229], self, timeout=2.5, force_tp=True, use_tp_charge=True)
self._cast_hammers(atk_len)
# Move a bit back and another round
self._move_and_attack((40, 20), atk_len)
# Here we have two different attack sequences depending if tele is available or not
if self.capabilities.can_teleport_natively or self.capabilities.can_teleport_with_charges:
# Back to center stairs and more hammers
self._pather.traverse_nodes([226], self, time_out=2.5, force_tp=True, use_tp_charge=True)
self._pather.traverse_nodes([226], self, timeout=2.5, force_tp=True, use_tp_charge=True)
self._cast_hammers(atk_len)
# move a bit to the top
self._move_and_attack((65, -30), atk_len)
@@ -140,7 +140,7 @@ class Hammerdin(IChar):
def kill_nihlathak(self, end_nodes: list[int]) -> bool:
# Move close to nihlathak
self._pather.traverse_nodes(end_nodes, self, time_out=0.8, do_pre_move=False)
self._pather.traverse_nodes(end_nodes, self, timeout=0.8, do_pre_move=False)
# move mouse to center, otherwise hammers sometimes dont fly, not sure why
pos_m = convert_abs_to_monitor((0, 0))
mouse.move(*pos_m, randomize=80, delay_factor=[0.5, 0.7])
@@ -200,7 +200,7 @@ class Hammerdin(IChar):
elif location == "rof_01": #node 603 - outside CS in ROF
### APPROACH ###
if not self._pather.traverse_nodes([603], self, time_out=3): return False #calibrate after static path
if not self._pather.traverse_nodes([603], self, timeout=3): return False #calibrate after static path
pos_m = convert_abs_to_monitor((0, 0))
### ATTACK ###
mouse.move(*pos_m, randomize=80, delay_factor=[0.5, 0.7])
@@ -221,7 +221,7 @@ class Hammerdin(IChar):
elif location == "rof_02": #node 604 - inside ROF
### APPROACH ###
if not self._pather.traverse_nodes([604], self, time_out=3): return False #threshold=0.8 (ex 601)
if not self._pather.traverse_nodes([604], self, timeout=3): return False #threshold=0.8 (ex 601)
### ATTACK ###
pos_m = convert_abs_to_monitor((0, 0))
mouse.move(*pos_m, randomize=80, delay_factor=[0.5, 0.7])
@@ -298,7 +298,7 @@ class Hammerdin(IChar):
wait(0.3, 0.6)
### LOOT ###
self._picked_up_items |= self._pickit.pick_up_items(self)
if not self._pather.traverse_nodes([673], self): return False # , time_out=3): # Re-adjust itself and continues to attack
if not self._pather.traverse_nodes([673], self): return False # , timeout=3): # Re-adjust itself and continues to attack
elif location == "entrance1_02": #node 673
### APPROACH ###
@@ -317,7 +317,7 @@ class Hammerdin(IChar):
### LOOT ###
self._picked_up_items |= self._pickit.pick_up_items(self)
self._pather.traverse_nodes_fixed("diablo_entrance_1_1", self) # Moves char to postion close to node 674 continues to attack
if not self._pather.traverse_nodes([674], self): return False#, time_out=3)
if not self._pather.traverse_nodes([674], self): return False#, timeout=3)
elif location == "entrance1_03": #node 674
### APPROACH ###
@@ -336,9 +336,9 @@ class Hammerdin(IChar):
### LOOT ###
self._picked_up_items |= self._pickit.pick_up_items(self)
self._picked_up_items |= self._pickit.pick_up_items(self)
if not self._pather.traverse_nodes([675], self): return False#, time_out=3) # Re-adjust itself
if not self._pather.traverse_nodes([675], self): return False#, timeout=3) # Re-adjust itself
self._pather.traverse_nodes_fixed("diablo_entrance_1_1", self) #static path to get to be able to spot 676
if not self._pather.traverse_nodes([676], self): return False#, time_out=3)
if not self._pather.traverse_nodes([676], self): return False#, timeout=3)
elif location == "entrance1_04": #node 676- Hall3
### APPROACH ###
@@ -379,10 +379,10 @@ class Hammerdin(IChar):
elif location == "entrance2_02": #node 682
### APPROACH ###
#if not self._pather.traverse_nodes([682], self): return False # , time_out=3):
#if not self._pather.traverse_nodes([682], self): return False # , timeout=3):
self._pather.traverse_nodes_fixed("diablo_trash_b_hall2_605_right", self) #pull mobs from the right
wait (0.2, 0.5)
if not self._pather.traverse_nodes([605], self): return False#, time_out=3)
if not self._pather.traverse_nodes([605], self): return False#, timeout=3)
### ATTACK ###
pos_m = convert_abs_to_monitor((0, 0))
mouse.move(*pos_m, randomize=80, delay_factor=[0.5, 0.7])
@@ -400,13 +400,13 @@ class Hammerdin(IChar):
elif location == "entrance2_03": #node 683
### APPROACH ###
#if not self._pather.traverse_nodes([682], self): return False # , time_out=3):
#if not self._pather.traverse_nodes([682], self): return False # , timeout=3):
#self._pather.traverse_nodes_fixed("diablo_entrance2_1", self)
#if not self._pather.traverse_nodes([683], self): return False # , time_out=3):
#if not self._pather.traverse_nodes([683], self): return False # , timeout=3):
self._pather.traverse_nodes_fixed("diablo_trash_b_hall2_605_top1", self) #pull mobs from top
wait (0.2, 0.5)
self._pather.traverse_nodes_fixed("diablo_trash_b_hall2_605_top2", self) #pull mobs from top
if not self._pather.traverse_nodes([605], self): return False#, time_out=3)
if not self._pather.traverse_nodes([605], self): return False#, timeout=3)
### ATTACK ###
pos_m = convert_abs_to_monitor((0, 0))
mouse.move(*pos_m, randomize=80, delay_factor=[0.5, 0.7])
@@ -424,14 +424,14 @@ class Hammerdin(IChar):
elif location == "entrance2_04": #node 686 - Hall3
### APPROACH ###
if not self._pather.traverse_nodes([605], self): return False#, time_out=3)
#if not self._pather.traverse_nodes([683,684], self): return False#, time_out=3)
if not self._pather.traverse_nodes([605], self): return False#, timeout=3)
#if not self._pather.traverse_nodes([683,684], self): return False#, timeout=3)
#self._pather.traverse_nodes_fixed("diablo_entrance2_2", self)
#if not self._pather.traverse_nodes([685,686], self): return False#, time_out=3)
#if not self._pather.traverse_nodes([685,686], self): return False#, timeout=3)
self._pather.traverse_nodes_fixed("diablo_trash_b_hall2_605_hall3", self)
if not self._pather.traverse_nodes([609], self): return False#, time_out=3)
if not self._pather.traverse_nodes([609], self): return False#, timeout=3)
self._pather.traverse_nodes_fixed("diablo_trash_b_hall3_pull_609", self)
if not self._pather.traverse_nodes([609], self): return False#, time_out=3)
if not self._pather.traverse_nodes([609], self): return False#, timeout=3)
### ATTACK ###
pos_m = convert_abs_to_monitor((0, 0))
mouse.move(*pos_m, randomize=80, delay_factor=[0.5, 0.7])
@@ -450,9 +450,9 @@ class Hammerdin(IChar):
### LOOT ###
self._picked_up_items |= self._pickit.pick_up_items(self)
self._picked_up_items |= self._pickit.pick_up_items(self)
if not self._pather.traverse_nodes([609], self): return False#, time_out=3)
if not self._pather.traverse_nodes([609], self): return False#, timeout=3)
self._picked_up_items |= self._pickit.pick_up_items(self)
if not self._pather.traverse_nodes([609], self): return False#, time_out=3)
if not self._pather.traverse_nodes([609], self): return False#, timeout=3)
####################
# PENT TRASH TO SEAL
@@ -605,7 +605,7 @@ class Hammerdin(IChar):
elif location == "A1-L_01": #node 611 seal layout A1-L: safe_dist
### APPROACH ###
if not self._pather.traverse_nodes([611], self): return False # , time_out=3):
if not self._pather.traverse_nodes([611], self): return False # , timeout=3):
### ATTACK ###
pos_m = convert_abs_to_monitor((0, 0))
mouse.move(*pos_m, randomize=80, delay_factor=[0.5, 0.7])
@@ -623,7 +623,7 @@ class Hammerdin(IChar):
elif location == "A1-L_02": #node 612 seal layout A1-L: center
### APPROACH ###
if not self._pather.traverse_nodes([612], self): return False # , time_out=3):
if not self._pather.traverse_nodes([612], self): return False # , timeout=3):
### ATTACK ###
if self._skill_hotkeys["redemption"]:
keyboard.send(self._skill_hotkeys["redemption"])
@@ -646,7 +646,7 @@ class Hammerdin(IChar):
elif location == "A1-L_03": #node 613 seal layout A1-L: fake_seal
### APPROACH ###
if not self._pather.traverse_nodes([613], self): return False # , time_out=3):
if not self._pather.traverse_nodes([613], self): return False # , timeout=3):
### ATTACK ###
pos_m = convert_abs_to_monitor((0, 0))
mouse.move(*pos_m, randomize=80, delay_factor=[0.5, 0.7])
@@ -677,7 +677,7 @@ class Hammerdin(IChar):
elif location == "A1-L_seal2": #node 614 seal layout A1-L: boss_seal
### APPROACH ###
if not self._pather.traverse_nodes([613, 615], self): return False # , time_out=3):
if not self._pather.traverse_nodes([613, 615], self): return False # , timeout=3):
### ATTACK ###
if self._skill_hotkeys["redemption"]:
keyboard.send(self._skill_hotkeys["redemption"])
@@ -693,7 +693,7 @@ class Hammerdin(IChar):
### APPROACH ###
if not self._pather.traverse_nodes_fixed("dia_a2y_hop_622", self): return False
Logger.info("A2-Y: Hop!")
#if not self._pather.traverse_nodes([622], self): return False # , time_out=3):
#if not self._pather.traverse_nodes([622], self): return False # , timeout=3):
if not self._pather.traverse_nodes([622], self): return False
if self._skill_hotkeys["redemption"]:
keyboard.send(self._skill_hotkeys["redemption"])
@@ -743,7 +743,7 @@ class Hammerdin(IChar):
### ATTACK ###
### LOOT ###
# we loot at boss
if not self._pather.traverse_nodes([625], self): return False # , time_out=3):
if not self._pather.traverse_nodes([625], self): return False # , timeout=3):
if self._skill_hotkeys["redemption"]:
keyboard.send(self._skill_hotkeys["redemption"])
wait(0.3, 0.6)
@@ -785,7 +785,7 @@ class Hammerdin(IChar):
elif location == "B1-S_seal2": #B only has 1 seal, which is the boss seal = seal2
### APPROACH ###
if not self._pather.traverse_nodes([634], self): return False # , time_out=3):
if not self._pather.traverse_nodes([634], self): return False # , timeout=3):
### ATTACK ###
if self._skill_hotkeys["redemption"]:
keyboard.send(self._skill_hotkeys["redemption"])
@@ -821,7 +821,7 @@ class Hammerdin(IChar):
elif location == "B2-U_seal2": #B only has 1 seal, which is the boss seal = seal2
### APPROACH ###
self._pather.traverse_nodes_fixed("dia_b2u_bold_seal", self)
if not self._pather.traverse_nodes([644], self): return False # , time_out=3):
if not self._pather.traverse_nodes([644], self): return False # , timeout=3):
### ATTACK ###
### LOOT ###
# we loot at boss
@@ -860,7 +860,7 @@ class Hammerdin(IChar):
wait(0.1,0.3)
self._pather.traverse_nodes_fixed("dia_c1f_hop_fakeseal", self)
wait(0.1,0.3)
if not self._pather.traverse_nodes([655], self): return False # , time_out=3):
if not self._pather.traverse_nodes([655], self): return False # , timeout=3):
### ATTACK ###
pos_m = convert_abs_to_monitor((0, 0))
mouse.move(*pos_m, randomize=80, delay_factor=[0.5, 0.7])
@@ -875,7 +875,7 @@ class Hammerdin(IChar):
wait(0.3, 0.6)
### LOOT ###
self._picked_up_items |= self._pickit.pick_up_items(self)
if not self._pather.traverse_nodes([655], self): return False # , time_out=3):
if not self._pather.traverse_nodes([655], self): return False # , timeout=3):
if self._skill_hotkeys["redemption"]:
keyboard.send(self._skill_hotkeys["redemption"])
wait(0.3, 0.6)
@@ -883,7 +883,7 @@ class Hammerdin(IChar):
elif location == "C1-F_seal2":
### APPROACH ###
self._pather.traverse_nodes_fixed("dia_c1f_654_651", self)
if not self._pather.traverse_nodes([652], self): return False # , time_out=3):
if not self._pather.traverse_nodes([652], self): return False # , timeout=3):
### ATTACK ###
pos_m = convert_abs_to_monitor((0, 0))
mouse.move(*pos_m, randomize=80, delay_factor=[0.5, 0.7])
@@ -898,7 +898,7 @@ class Hammerdin(IChar):
wait(0.3, 0.6)
### LOOT ###
self._picked_up_items |= self._pickit.pick_up_items(self)
if not self._pather.traverse_nodes([652], self): return False # , time_out=3):
if not self._pather.traverse_nodes([652], self): return False # , timeout=3):
if self._skill_hotkeys["redemption"]:
keyboard.send(self._skill_hotkeys["redemption"])
wait(0.3, 0.6)
@@ -930,7 +930,7 @@ class Hammerdin(IChar):
elif location == "C2-G_seal1":
### APPROACH ###
#if not self._pather.traverse_nodes([663, 662], self): return False # , time_out=3): #caused 7% failed runs, replaced by static path.
#if not self._pather.traverse_nodes([663, 662], self): return False # , timeout=3): #caused 7% failed runs, replaced by static path.
self._pather.traverse_nodes_fixed("dia_c2g_lc_661", self)
### ATTACK ###
### LOOT ###
@@ -976,7 +976,7 @@ class Hammerdin(IChar):
if self._skill_hotkeys["redemption"]:
keyboard.send(self._skill_hotkeys["redemption"])
wait(0.3, 0.6)
if not self._pather.traverse_nodes([664, 665], self): return False # , time_out=3):
if not self._pather.traverse_nodes([664, 665], self): return False # , timeout=3):
else:
### APPROACH ###
@@ -1002,7 +1002,7 @@ class Hammerdin(IChar):
def kill_vizier(self, seal_layout:str) -> bool:
if seal_layout == "A1-L":
### APPROACH ###
if not self._pather.traverse_nodes([612], self): return False # , time_out=3):
if not self._pather.traverse_nodes([612], self): return False # , timeout=3):
### ATTACK ###
Logger.debug(seal_layout + ": Attacking Vizier at position 1/2")
pos_m = convert_abs_to_monitor((0, 0))
@@ -1011,7 +1011,7 @@ class Hammerdin(IChar):
self._move_and_attack((-30, -15), Config().char["atk_len_diablo_vizier"] * 0.5)
self._cast_hammers(1, "redemption")
Logger.debug(seal_layout + ": Attacking Vizier at position 2/2")
self._pather.traverse_nodes([611], self, time_out=3)
self._pather.traverse_nodes([611], self, timeout=3)
self._move_and_attack((30, 15), Config().char["atk_len_diablo_vizier"] * 0.5)
self._move_and_attack((-30, -15), Config().char["atk_len_diablo_vizier"]) # no factor, so merc is not reset by teleport and he his some time to move & kill stray bosses
self._cast_hammers(1, "redemption")
@@ -1024,16 +1024,16 @@ class Hammerdin(IChar):
wait(0.3, 1.2)
### LOOT ###
self._picked_up_items |= self._pickit.pick_up_items(self)
if not self._pather.traverse_nodes([612], self): return False # , time_out=3):
if not self._pather.traverse_nodes([612], self): return False # , timeout=3):
if self._skill_hotkeys["redemption"]:
keyboard.send(self._skill_hotkeys["redemption"])
wait(0.3, 0.6)
self._picked_up_items |= self._pickit.pick_up_items(self)
if not self._pather.traverse_nodes([612], self): return False # , time_out=3): # recalibrate after loot
if not self._pather.traverse_nodes([612], self): return False # , timeout=3): # recalibrate after loot
elif seal_layout == "A2-Y":
### APPROACH ###
if not self._pather.traverse_nodes([627, 622], self): return False # , time_out=3):
if not self._pather.traverse_nodes([627, 622], self): return False # , timeout=3):
### ATTACK ###
Logger.debug(seal_layout + ": Attacking Vizier at position 1/2")
pos_m = convert_abs_to_monitor((0, 0))
@@ -1042,7 +1042,7 @@ class Hammerdin(IChar):
self._move_and_attack((-30, -15), Config().char["atk_len_diablo_vizier"] * 0.5)
self._cast_hammers(1, "redemption")
Logger.debug(seal_layout + ": Attacking Vizier at position 2/2")
self._pather.traverse_nodes([623], self, time_out=3)
self._pather.traverse_nodes([623], self, timeout=3)
self._move_and_attack((30, 15), Config().char["atk_len_diablo_vizier"] * 0.5)
self._move_and_attack((-30, -15), Config().char["atk_len_diablo_vizier"] * 0.5)
self._cast_hammers(1, "redemption")
@@ -1064,12 +1064,12 @@ class Hammerdin(IChar):
if self._skill_hotkeys["redemption"]:
keyboard.send(self._skill_hotkeys["redemption"])
wait(0.3, 0.6)
if not self._pather.traverse_nodes([622], self): return False #, time_out=3):
if not self._pather.traverse_nodes([622], self): return False #, timeout=3):
if self._skill_hotkeys["redemption"]:
keyboard.send(self._skill_hotkeys["redemption"])
wait(0.3, 0.6)
self._picked_up_items |= self._pickit.pick_up_items(self)
if not self._pather.traverse_nodes([622], self): return False # , time_out=3): #recalibrate after loot
if not self._pather.traverse_nodes([622], self): return False # , timeout=3): #recalibrate after loot
else:
Logger.debug(seal_layout + ": Invalid location for kill_deseis("+ seal_layout +"), should not happen.")
@@ -1093,16 +1093,16 @@ class Hammerdin(IChar):
self._move_and_attack((-30, -15), Config().char["atk_len_diablo_deseis"] * 0.2)
self._cast_hammers(1, "redemption")
Logger.debug(seal_layout + ": Attacking De Seis at position 2/4")
self._pather.traverse_nodes(nodes1, self, time_out=3)
self._pather.traverse_nodes(nodes1, self, timeout=3)
self._move_and_attack((30, 15), Config().char["atk_len_diablo_deseis"] * 0.2)
self._move_and_attack((-30, -15), Config().char["atk_len_diablo_deseis"] * 0.2)
self._cast_hammers(1, "redemption")
Logger.debug(seal_layout + ": Attacking De Seis at position 3/4")
self._pather.traverse_nodes(nodes2, self, time_out=3)
self._pather.traverse_nodes(nodes2, self, timeout=3)
self._move_and_attack((0, 0), Config().char["atk_len_diablo_deseis"] * 0.5)
self._cast_hammers(1, "redemption")
Logger.debug(seal_layout + ": Attacking De Seis at position 4/4")
self._pather.traverse_nodes(nodes3, self, time_out=3)
self._pather.traverse_nodes(nodes3, self, timeout=3)
self._move_and_attack((0, 0), Config().char["atk_len_diablo_deseis"]) # no factor, so merc is not reset by teleport and he his some time to move & kill stray bosses
wait(0.1, 0.2)
self._cast_hammers(2, "redemption")
@@ -1130,16 +1130,16 @@ class Hammerdin(IChar):
self._move_and_attack((-30, -15), Config().char["atk_len_diablo_deseis"] * 0.2)
self._cast_hammers(1, "redemption")
Logger.debug(seal_layout + ": Attacking De Seis at position 2/4")
self._pather.traverse_nodes(nodes1, self, time_out=3)
self._pather.traverse_nodes(nodes1, self, timeout=3)
self._move_and_attack((30, 15), Config().char["atk_len_diablo_deseis"] * 0.2)
self._move_and_attack((-30, -15), Config().char["atk_len_diablo_deseis"] * 0.2)
self._cast_hammers(1, "redemption")
Logger.debug(seal_layout + ": Attacking De Seis at position 3/4")
self._pather.traverse_nodes(nodes2, self, time_out=3)
self._pather.traverse_nodes(nodes2, self, timeout=3)
self._move_and_attack((0, 0), Config().char["atk_len_diablo_deseis"] * 0.5)
self._cast_hammers(1, "redemption")
Logger.debug(seal_layout + ": Attacking De Seis at position 4/4")
self._pather.traverse_nodes(nodes3, self, time_out=3)
self._pather.traverse_nodes(nodes3, self, timeout=3)
self._move_and_attack((0, 0), Config().char["atk_len_diablo_deseis"]) # no factor, so merc is not reset by teleport and he his some time to move & kill stray bosses
wait(0.1, 0.2)
self._cast_hammers(2, "redemption")
@@ -1150,11 +1150,11 @@ class Hammerdin(IChar):
#if Config().general["info_screenshots"]: cv2.imwrite(f"./info_screenshots/info_check_deseis_dead" + seal_layout + "_" + time.strftime("%Y%m%d_%H%M%S") + ".png", grab())
### LOOT ###
self._picked_up_items |= self._pickit.pick_up_items(self)
if not self._pather.traverse_nodes([641], self): return False # , time_out=3):
if not self._pather.traverse_nodes([646], self): return False # , time_out=3):
if not self._pather.traverse_nodes([641], self): return False # , timeout=3):
if not self._pather.traverse_nodes([646], self): return False # , timeout=3):
self._picked_up_items |= self._pickit.pick_up_items(self)
if not self._pather.traverse_nodes([646], self): return False # , time_out=3):
if not self._pather.traverse_nodes([640], self): return False # , time_out=3):
if not self._pather.traverse_nodes([646], self): return False # , timeout=3):
if not self._pather.traverse_nodes([640], self): return False # , timeout=3):
self._picked_up_items |= self._pickit.pick_up_items(self)
else:
+5 -5
View File
@@ -7,7 +7,7 @@ from inventory import consumables
import keyboard
import numpy as np
from char.capabilities import CharacterCapabilities
from ui_manager import wait_for_screen_object
from ui_manager import wait_until_visible
from ui import skills
from utils.custom_mouse import mouse
from utils.misc import wait, cut_roi, is_in_roi, color_filter
@@ -69,7 +69,7 @@ class IChar:
self,
template_type: Union[str, List[str]],
success_func: Callable = None,
time_out: float = 8,
timeout: float = 8,
threshold: float = 0.68,
telekinesis: bool = False
) -> bool:
@@ -77,7 +77,7 @@ class IChar:
Finds any template from the template finder and interacts with it
:param template_type: Strings or list of strings of the templates that should be searched for
:param success_func: Function that will return True if the interaction is successful e.g. return True when loading screen is reached, defaults to None
:param time_out: Timeout for the whole template selection, defaults to None
:param timeout: Timeout for the whole template selection, defaults to None
:param threshold: Threshold which determines if a template is found or not. None will use default form .ini files
:return: True if success. False otherwise
"""
@@ -87,7 +87,7 @@ class IChar:
if match.valid:
keyboard.send("esc")
start = time.time()
while time_out is None or (time.time() - start) < time_out:
while timeout is None or (time.time() - start) < timeout:
template_match = TemplateFinder().search(template_type, grab(), threshold=threshold, normalize_monitor=True)
if template_match.valid:
Logger.debug(f"Select {template_match.name} ({template_match.score*100:.1f}% confidence)")
@@ -211,7 +211,7 @@ class IChar:
mouse.move(*pos, randomize=6, delay_factor=[0.9, 1.1])
wait(0.08, 0.15)
mouse.click(button="left")
match = wait_for_screen_object(ScreenObjects.Loading, 2)
match = wait_until_visible(ScreenObjects.Loading, 2)
if match.valid:
return True
# move mouse away to not overlay with the town portal if mouse is in center
+14 -14
View File
@@ -13,7 +13,7 @@ from pather import Location, Pather
import numpy as np
import time
import os
from ui_manager import wait_for_screen_object, ScreenObjects
from ui_manager import wait_until_visible, ScreenObjects
class Necro(IChar):
def __init__(self, skill_hotkeys: dict, pather: Pather):
@@ -491,7 +491,7 @@ class Necro(IChar):
if self.capabilities.can_teleport_natively:
self._pather.traverse_nodes_fixed("eldritch_end", self)
else:
self._pather.traverse_nodes((Location.A5_ELDRITCH_SAFE_DIST, Location.A5_ELDRITCH_END), self, time_out=0.6, force_tp=True)
self._pather.traverse_nodes((Location.A5_ELDRITCH_SAFE_DIST, Location.A5_ELDRITCH_END), self, timeout=0.6, force_tp=True)
self.bone_armor()
#get some more summons out for elite packs
@@ -532,7 +532,7 @@ class Necro(IChar):
self._summon_stat()
#continue to shenk fight
self._pather.traverse_nodes(([ 146, 147, 148]), self, time_out=1.4, force_tp=True)
self._pather.traverse_nodes(([ 146, 147, 148]), self, timeout=1.4, force_tp=True)
shenk_pos_abs = self._pather.find_abs_node_pos(149, grab())
if shenk_pos_abs is None:
@@ -566,7 +566,7 @@ class Necro(IChar):
# Move to items
#wait(self._cast_duration, self._cast_duration + 0.2)
self._pather.traverse_nodes(([148,149]), self, time_out=3.4, force_tp=True)
self._pather.traverse_nodes(([148,149]), self, timeout=3.4, force_tp=True)
for _ in range(30):
self._summon_count()
if self._skeletons_count < 10:
@@ -678,7 +678,7 @@ class Necro(IChar):
wait(0.08, 0.15)
mouse.click(button="left")
Logger.debug("enter durance lv 1")
match = wait_for_screen_object(ScreenObjects.Loading, 2)
match = wait_until_visible(ScreenObjects.Loading, 2)
if match.valid:
return True
else:
@@ -719,7 +719,7 @@ class Necro(IChar):
wait(0.08, 0.15)
mouse.click(button="left")
Logger.debug("entering trav...")
match = wait_for_screen_object(ScreenObjects.Loading, 2)
match = wait_until_visible(ScreenObjects.Loading, 2)
if match.valid:
return True
return False
@@ -728,7 +728,7 @@ class Necro(IChar):
def kill_council(self) -> bool:
''' kill the council '''
result = self._pather.traverse_nodes((901,902,903,904,905,906,226,228,300), self, force_move=True,time_out = 2.5,threshold=.55)
result = self._pather.traverse_nodes((901,902,903,904,905,906,226,228,300), self, force_move=True,timeout = 2.5,threshold=.55)
#this is gross but the skeletons I think cause pathing issues
while result is False:
@@ -745,22 +745,22 @@ class Necro(IChar):
if self._pather.find_abs_node_pos(904, grab()):
#try again
result = self._pather.traverse_nodes((904,905,906,226,228,300), self, force_move=True,time_out = 2.5)
result = self._pather.traverse_nodes((904,905,906,226,228,300), self, force_move=True,timeout = 2.5)
elif self._pather.find_abs_node_pos(905, grab()):
#try again
result = self._pather.traverse_nodes((905,906,226,228,300), self, force_move=True,time_out = 2.5)
result = self._pather.traverse_nodes((905,906,226,228,300), self, force_move=True,timeout = 2.5)
elif self._pather.find_abs_node_pos(906, grab()):
#try again
result = self._pather.traverse_nodes((906,226,228,300), self, force_move=True,time_out = 2.5)
result = self._pather.traverse_nodes((906,226,228,300), self, force_move=True,timeout = 2.5)
elif self._pather.find_abs_node_pos(226, grab()):
#try again
result = self._pather.traverse_nodes((226,228,300), self, force_move=True,time_out = 2.5)
result = self._pather.traverse_nodes((226,228,300), self, force_move=True,timeout = 2.5)
elif self._pather.find_abs_node_pos(901, grab()):
#try again
result = self._pather.traverse_nodes((901,902,903,904,905,906,226,228,300), self, force_move=True,time_out = 2.5)
result = self._pather.traverse_nodes((901,902,903,904,905,906,226,228,300), self, force_move=True,timeout = 2.5)
elif self._pather.find_abs_node_pos(902, grab()):
#try again
result = self._pather.traverse_nodes((902,903,904,905,906,226,228,300), self, force_move=True,time_out = 2.5)
result = self._pather.traverse_nodes((902,903,904,905,906,226,228,300), self, force_move=True,timeout = 2.5)
self._cast_circle(cast_dir=[-1,1],cast_start_angle=0,cast_end_angle=360,cast_div=4,cast_v_div=3,cast_spell='amp_dmg',delay=3.0)
@@ -864,7 +864,7 @@ class Necro(IChar):
self._corpse_explosion(corpse_exp_pos, 240, cast_count=18)
#self._pather.traverse_nodes([230, 229 ,228], self, time_out=2.5, force_tp=True)
#self._pather.traverse_nodes([230, 229 ,228], self, timeout=2.5, force_tp=True)
Logger.info('\033[92m'+"atk cycle end"+'\033[0m')
+8 -8
View File
@@ -101,7 +101,7 @@ class BlizzSorc(Sorceress):
self.pre_move()
self.move(pos_m, force_move=True)
#lower left posistion
self._pather.traverse_nodes([151], self, time_out=2.5, force_tp=False)
self._pather.traverse_nodes([151], self, timeout=2.5, force_tp=False)
self._cast_static()
self._blizzard((-250, 100), spray=10)
self._ice_blast((60, 70), spray=60)
@@ -130,14 +130,14 @@ class BlizzSorc(Sorceress):
self._cast_static()
self._blizzard((100, -50), spray=10)
# Move to items
self._pather.traverse_nodes((Location.A5_SHENK_SAFE_DIST, Location.A5_SHENK_END), self, time_out=1.4, force_tp=True)
self._pather.traverse_nodes((Location.A5_SHENK_SAFE_DIST, Location.A5_SHENK_END), self, timeout=1.4, force_tp=True)
return True
def kill_council(self) -> bool:
# Move inside to the right
self._pather.traverse_nodes_fixed([(1110, 120)], self)
self._pather.offset_node(300, (80, -110))
self._pather.traverse_nodes([300], self, time_out=5.5, force_tp=True)
self._pather.traverse_nodes([300], self, timeout=5.5, force_tp=True)
self._pather.offset_node(300, (-80, 110))
# Attack to the left
self._blizzard((-150, 10), spray=80)
@@ -154,7 +154,7 @@ class BlizzSorc(Sorceress):
wait(0.5)
# Move to far left
self._pather.offset_node(301, (-80, -50))
self._pather.traverse_nodes([301], self, time_out=2.5, force_tp=True)
self._pather.traverse_nodes([301], self, timeout=2.5, force_tp=True)
self._pather.offset_node(301, (80, 50))
# Attack to RIGHT
self._blizzard((100, 150), spray=80)
@@ -167,7 +167,7 @@ class BlizzSorc(Sorceress):
for p in [(450, 100), (-190, 200)]:
pos_m = convert_abs_to_monitor(p)
self.move(pos_m, force_move=True)
self._pather.traverse_nodes([304], self, time_out=2.5, force_tp=True)
self._pather.traverse_nodes([304], self, timeout=2.5, force_tp=True)
# Attack to center of stairs
self._blizzard((-175, -200), spray=30)
self._ice_blast((30, -60), spray=30)
@@ -176,7 +176,7 @@ class BlizzSorc(Sorceress):
wait(1.0)
# Move back inside
self._pather.traverse_nodes_fixed([(1110, 15)], self)
self._pather.traverse_nodes([300], self, time_out=2.5, force_tp=False)
self._pather.traverse_nodes([300], self, timeout=2.5, force_tp=False)
# Attack to center
self._blizzard((-100, 0), spray=10)
self._cast_static()
@@ -208,7 +208,7 @@ class BlizzSorc(Sorceress):
self._cast_static()
self._ice_blast((-30, 50), spray=10)
# Move outside since the trav.py expects to start searching for items there if char can teleport
self._pather.traverse_nodes([226], self, time_out=2.5, force_tp=True)
self._pather.traverse_nodes([226], self, timeout=2.5, force_tp=True)
return True
def kill_nihlathak(self, end_nodes: list[int]) -> bool:
@@ -233,7 +233,7 @@ class BlizzSorc(Sorceress):
self._blizzard(cast_pos_abs, spray=15)
# Move to items
wait(1.3)
self._pather.traverse_nodes(end_nodes, self, time_out=0.8)
self._pather.traverse_nodes(end_nodes, self, timeout=0.8)
return True
def kill_summoner(self) -> bool:
+7 -7
View File
@@ -103,14 +103,14 @@ class LightSorc(Sorceress):
self.move(pos_m, force_move=True)
# Move to items
wait(self._cast_duration, self._cast_duration + 0.2)
self._pather.traverse_nodes((Location.A5_SHENK_SAFE_DIST, Location.A5_SHENK_END), self, time_out=1.4, force_tp=True)
self._pather.traverse_nodes((Location.A5_SHENK_SAFE_DIST, Location.A5_SHENK_END), self, timeout=1.4, force_tp=True)
return True
def kill_council(self) -> bool:
# Move inside to the right
self._pather.traverse_nodes_fixed([(1110, 120)], self)
self._pather.offset_node(300, (80, -110))
self._pather.traverse_nodes([300], self, time_out=1.0, force_tp=True)
self._pather.traverse_nodes([300], self, timeout=1.0, force_tp=True)
self._pather.offset_node(300, (-80, 110))
wait(0.5)
self._frozen_orb((-150, -10), spray=10)
@@ -126,7 +126,7 @@ class LightSorc(Sorceress):
self.move(pos_m, force_move=True)
wait(0.5)
self._pather.offset_node(226, (-80, 60))
self._pather.traverse_nodes([226], self, time_out=1.0, force_tp=True)
self._pather.traverse_nodes([226], self, timeout=1.0, force_tp=True)
self._pather.offset_node(226, (80, -60))
wait(0.5)
self._frozen_orb((-150, -130), spray=10)
@@ -134,7 +134,7 @@ class LightSorc(Sorceress):
self._chain_lightning((-170, -150), spray=20)
wait(0.5)
self._pather.traverse_nodes_fixed([(1110, 15)], self)
self._pather.traverse_nodes([300], self, time_out=1.0, force_tp=True)
self._pather.traverse_nodes([300], self, timeout=1.0, force_tp=True)
pos_m = convert_abs_to_monitor((300, 150))
self.pre_move()
self.move(pos_m, force_move=True)
@@ -149,7 +149,7 @@ class LightSorc(Sorceress):
self.pre_move()
self.move(pos_m, force_move=True)
self._pather.offset_node(304, (0, -80))
self._pather.traverse_nodes([304], self, time_out=1.0, force_tp=True)
self._pather.traverse_nodes([304], self, timeout=1.0, force_tp=True)
self._pather.offset_node(304, (0, 80))
wait(0.5)
self._frozen_orb((175, -170), spray=40)
@@ -184,7 +184,7 @@ class LightSorc(Sorceress):
self.pre_move()
self.move(pos_m, force_move=True)
# Move outside since the trav.py expects to start searching for items there if char can teleport
self._pather.traverse_nodes([226], self, time_out=2.5, force_tp=True)
self._pather.traverse_nodes([226], self, timeout=2.5, force_tp=True)
return True
def kill_nihlathak(self, end_nodes: list[int]) -> bool:
@@ -220,7 +220,7 @@ class LightSorc(Sorceress):
# Move to items
wait(self._cast_duration, self._cast_duration + 0.2)
self._pather.traverse_nodes(end_nodes, self, time_out=0.8)
self._pather.traverse_nodes(end_nodes, self, timeout=0.8)
return True
def kill_summoner(self) -> bool:
+4 -4
View File
@@ -48,7 +48,7 @@ class NovaSorc(Sorceress):
return True
def kill_shenk(self) -> bool:
self._pather.traverse_nodes((Location.A5_SHENK_SAFE_DIST, Location.A5_SHENK_END), self, time_out=1.0)
self._pather.traverse_nodes((Location.A5_SHENK_SAFE_DIST, Location.A5_SHENK_END), self, timeout=1.0)
self._cast_static(0.6)
self._nova(Config().char["atk_len_shenk"])
return True
@@ -61,13 +61,13 @@ class NovaSorc(Sorceress):
self._pather.offset_node(229, offset_229)
def clear_inside():
self._pather.traverse_nodes_fixed([(1110, 120)], self)
self._pather.traverse_nodes([229], self, time_out=0.8, force_tp=True)
self._pather.traverse_nodes([229], self, timeout=0.8, force_tp=True)
self._nova(atk_len)
self._move_and_attack((-40, -20), atk_len)
self._move_and_attack((40, 20), atk_len)
self._move_and_attack((40, 20), atk_len)
def clear_outside():
self._pather.traverse_nodes([226], self, time_out=0.8, force_tp=True)
self._pather.traverse_nodes([226], self, timeout=0.8, force_tp=True)
self._nova(atk_len)
self._move_and_attack((45, -20), atk_len)
self._move_and_attack((-45, 20), atk_len)
@@ -82,7 +82,7 @@ class NovaSorc(Sorceress):
def kill_nihlathak(self, end_nodes: list[int]) -> bool:
atk_len = Config().char["atk_len_nihlathak"] * 0.3
# Move close to nihlathak
self._pather.traverse_nodes(end_nodes, self, time_out=0.8, do_pre_move=False)
self._pather.traverse_nodes(end_nodes, self, timeout=0.8, do_pre_move=False)
# move mouse to center
pos_m = convert_abs_to_monitor((0, 0))
mouse.move(*pos_m, randomize=80, delay_factor=[0.5, 0.7])
+4 -4
View File
@@ -38,20 +38,20 @@ class Sorceress(IChar):
self,
template_type: Union[str, List[str]],
success_func: Callable = None,
time_out: float = 8,
timeout: float = 8,
threshold: float = 0.68,
telekinesis: bool = False
) -> bool:
# In case telekinesis is False or hotkey is not set, just call the base implementation
if not self._skill_hotkeys["telekinesis"] or not telekinesis:
return super().select_by_template(template_type, success_func, time_out, threshold)
return super().select_by_template(template_type, success_func, timeout, threshold)
if type(template_type) == list and "A5_STASH" in template_type:
# sometimes waypoint is opened and stash not found because of that, check for that
match = detect_screen_object(ScreenObjects.WaypointLabel)
if match.valid:
keyboard.send("esc")
start = time.time()
while time_out is None or (time.time() - start) < time_out:
while timeout is None or (time.time() - start) < timeout:
template_match = TemplateFinder().search(template_type, grab(), threshold=threshold, normalize_monitor=True)
if template_match.valid:
keyboard.send(self._skill_hotkeys["telekinesis"])
@@ -65,7 +65,7 @@ class Sorceress(IChar):
if success_func is None or success_func():
return True
# In case telekinesis fails, try again with the base implementation
return super().select_by_template(template_type, success_func, time_out, threshold)
return super().select_by_template(template_type, success_func, timeout, threshold)
def pre_buff(self):
if Config().char["cta_available"]:
+3 -3
View File
@@ -96,7 +96,7 @@ class Trapsin(IChar):
if self.capabilities.can_teleport_natively:
self._pather.traverse_nodes_fixed("eldritch_end", self)
else:
self._pather.traverse_nodes((Location.A5_ELDRITCH_SAFE_DIST, Location.A5_ELDRITCH_END), self, time_out=0.6, force_tp=True)
self._pather.traverse_nodes((Location.A5_ELDRITCH_SAFE_DIST, Location.A5_ELDRITCH_END), self, timeout=0.6, force_tp=True)
return True
def kill_shenk(self) -> bool:
@@ -110,7 +110,7 @@ class Trapsin(IChar):
self._left_attack(cast_pos_abs, 90)
# Move to items
wait(self._cast_duration, self._cast_duration + 0.2)
self._pather.traverse_nodes((Location.A5_SHENK_SAFE_DIST, Location.A5_SHENK_END), self, time_out=1.4, force_tp=True)
self._pather.traverse_nodes((Location.A5_SHENK_SAFE_DIST, Location.A5_SHENK_END), self, timeout=1.4, force_tp=True)
return True
def kill_nihlathak(self, end_nodes: list[int]) -> bool:
@@ -132,7 +132,7 @@ class Trapsin(IChar):
self.move(pos_m)
# Move to items
wait(self._cast_duration, self._cast_duration + 0.2)
self._pather.traverse_nodes(end_nodes, self, time_out=0.8)
self._pather.traverse_nodes(end_nodes, self, timeout=0.8)
return True
+2 -2
View File
@@ -23,12 +23,12 @@ class Chest:
chest = filename[:-4].upper()
self._templates.append(chest)
def open_up_chests(self, time_out: float = 8.0, threshold: float = 0.73) -> bool:
def open_up_chests(self, timeout: float = 8.0, threshold: float = 0.73) -> bool:
Logger.debug("Open chests")
templates = self._templates
found_chest = True
start = time.time()
while time.time() - start < time_out:
while time.time() - start < timeout:
template_match = TemplateFinder().search(templates, grab(), roi=Config().ui_roi["reduce_to_center"], threshold=threshold, use_grayscale=True, best_match=True, normalize_monitor=True)
# search for at least 1.5 second, if no chest found, break
if not template_match.valid:
+25 -13
View File
@@ -3,14 +3,34 @@ from logger import Logger
from typing import List
import numpy as np
from template_finder import TemplateFinder
from inventory import common, consumables
from inventory import common, consumables, personal
from inventory.consumables import item_consumables_map
from ui import view
from ui_manager import is_visible, wait_until_visible, ScreenObjects, wait_until_hidden
from utils.custom_mouse import mouse
from utils.misc import cut_roi, wait, color_filter
from config import Config
from screen import convert_abs_to_monitor, convert_monitor_to_screen, convert_screen_to_monitor, grab
import keyboard
def open(img: np.ndarray = None) -> np.ndarray:
img = grab() if img is None else img
if is_visible(ScreenObjects.BeltExpandable, img) and Config().char["belt_rows"] > 1:
keyboard.send(Config().char["show_belt"])
if not wait_until_hidden(ScreenObjects.BeltExpandable, 1):
return None
img = grab()
return img
def close(img: np.ndarray = None) -> np.ndarray:
img = grab() if img is None else img
if not is_visible(ScreenObjects.BeltExpandable, img):
keyboard.send("esc")
if not wait_until_visible(ScreenObjects.BeltExpandable, 2):
success = view.return_to_play()
if not success:
return None
return img
def _potion_type(img: np.ndarray) -> str:
"""
@@ -80,10 +100,8 @@ def update_pot_needs():
if screen_mouse_pos[1] > Config().ui_pos["screen_height"] * 0.72:
center_m = convert_abs_to_monitor((-200, -120))
mouse.move(*center_m, randomize=100)
keyboard.send(Config().char["show_belt"])
wait(0.5)
img = open()
# first clean up columns that might be too much
img = grab()
for column in range(4):
potion_type = _potion_type(_cut_potion_img(img, column, 0))
if potion_type != "empty":
@@ -112,21 +130,17 @@ def update_pot_needs():
break
elif current_column is not None and potion_type == "empty":
pot_needs[current_column] += 1
wait(0.2)
#Logger.debug(f"Will pickup: {pot_needs}")
keyboard.send(Config().char["show_belt"])
consumables.set_needs("health", pot_needs["health"])
consumables.set_needs("mana", pot_needs["mana"])
consumables.set_needs("rejuv", pot_needs["rejuv"])
close(img)
def fill_up_belt_from_inventory(num_loot_columns: int):
"""
Fill up your belt with pots from the inventory e.g. after death. It will open and close invetory by itself!
:param num_loot_columns: Number of columns used for loot from left
"""
keyboard.send(Config().char["inventory_screen"])
wait(0.7, 1.0)
img = grab()
img = personal.open()
pot_positions = []
for column, row in itertools.product(range(num_loot_columns), range(4)):
center_pos, slot_img = common.get_slot_pos_and_img(img, column, row)
@@ -141,6 +155,4 @@ def fill_up_belt_from_inventory(num_loot_columns: int):
mouse.click(button="left")
wait(0.3, 0.4)
keyboard.release("shift")
wait(0.2, 0.25)
keyboard.send(Config().char["inventory_screen"])
wait(0.5)
common.close(img)
+3 -3
View File
@@ -5,7 +5,7 @@ import keyboard
import time
from utils.custom_mouse import mouse
from template_finder import TemplateFinder
from ui_manager import detect_screen_object, ScreenObjects, center_mouse
from ui_manager import detect_screen_object, ScreenObjects, center_mouse, wait_until_hidden
from utils.misc import wait, trim_black, color_filter, cut_roi
from inventory import consumables, personal
from ui import view
@@ -113,7 +113,7 @@ def id_item_with_tome(item_location: list, id_tome_location: list):
wait(0.1)
mouse.click(button="left")
consumables.increment_need("id", 1)
wait(0.2, 0.4)
wait(0.1)
def transfer_items(items: list, action: str = "drop") -> list:
#requires open inventory / stash / vendor
@@ -219,7 +219,7 @@ def wait_for_left_inventory():
start=time.time()
while time.time() - start < 5:
if left_inventory_ready(grab()):
Logger.debug("Vendor/stash inventory fully loaded")
Logger.debug("Vendor/stash inventory open")
return True
wait(0.1)
Logger.error("wait_for_left_inventory: Vendor/stash inventory not detected")
+5 -6
View File
@@ -15,7 +15,7 @@ from utils.misc import wait, is_in_roi, mask_by_roi
from utils.custom_mouse import mouse
from inventory import stash, common, vendor
from ui import view
from ui_manager import detect_screen_object, wait_for_screen_object, ScreenObjects, center_mouse
from ui_manager import detect_screen_object, wait_until_visible, ScreenObjects, center_mouse
from item import ItemCropper
from messages import Messenger
@@ -62,13 +62,13 @@ def stash_all_items(items: list = None):
center_mouse()
# Wait till gold btn is found
Logger.debug("Searching for inventory gold btn...")
if not (gold_btn := wait_for_screen_object(ScreenObjects.GoldBtnInventory, time_out = 20)).valid:
if not (gold_btn := wait_until_visible(ScreenObjects.GoldBtnInventory, timeout = 20)).valid:
Logger.error("Could not determine to be in stash menu. Continue...")
return
Logger.debug("Found inventory gold btn")
# stash gold
if Config().char["stash_gold"]:
if wait_for_screen_object(ScreenObjects.GoldNone, 1).valid:
if wait_until_visible(ScreenObjects.GoldNone, 0.5).valid:
Logger.debug("No gold to stash")
else:
Logger.debug("Stashing gold")
@@ -142,7 +142,6 @@ def keep_item(item_box: ItemText = None, found_item: Item = None, do_logging: bo
:param do_logging: Bool value to turn on/off logging for items that are found and should be kept
:return: Bool if item should be kept
"""
wait(0.2, 0.3)
ymax = 50 if item_box.data.shape[0] < 50 else item_box.data.shape[0]
img = item_box.data[0:ymax,:]
@@ -259,11 +258,11 @@ def open(img: np.ndarray = None) -> np.ndarray:
img = grab() if img is None else img
if not detect_screen_object(ScreenObjects.RightPanel, img).valid:
keyboard.send(Config().char["inventory_screen"])
if not wait_for_screen_object(ScreenObjects.RightPanel, 1).valid:
if not wait_until_visible(ScreenObjects.RightPanel, 1).valid:
if not view.return_to_play():
return None
keyboard.send(Config().char["inventory_screen"])
if not wait_for_screen_object(ScreenObjects.RightPanel, 1).valid:
if not wait_until_visible(ScreenObjects.RightPanel, 1).valid:
return None
img = grab()
return img
+3 -3
View File
@@ -7,7 +7,7 @@ from utils.misc import wait
from screen import grab
from logger import Logger
from utils.custom_mouse import mouse
from ui_manager import detect_screen_object, wait_for_screen_object, ScreenObjects
from ui_manager import detect_screen_object, wait_until_visible, ScreenObjects
from inventory import personal, common
gamble_count = 0
@@ -39,7 +39,7 @@ def repair() -> bool:
Repair and fills up TP buy selling tome and buying. Vendor inventory needs to be open!
:return: Bool if success
"""
repair_btn = wait_for_screen_object(ScreenObjects.RepairBtn, time_out=4)
repair_btn = wait_until_visible(ScreenObjects.RepairBtn, timeout=4)
if not repair_btn.valid:
return False
mouse.move(*repair_btn.center, randomize=12, delay_factor=[1.0, 1.5])
@@ -53,7 +53,7 @@ def repair() -> bool:
return True
def gamble():
if (refresh_btn := TemplateFinder().search_and_wait("REFRESH", threshold=0.79, time_out=4, normalize_monitor=True)).valid:
if (refresh_btn := TemplateFinder().search_and_wait("REFRESH", threshold=0.79, timeout=4, normalize_monitor=True)).valid:
#Gambling window is open. Starting to spent some coins
max_gamble_count = floor(2000000/188000) # leave about 500k gold and assume buying coronets at ~188k
while get_gamble_status() and get_gamble_count() < max_gamble_count:
+3 -3
View File
@@ -35,7 +35,7 @@ class PickIt:
cv2.imwrite("./loot_screenshots/info_debug_drop_" + time.strftime("%Y%m%d_%H%M%S") + ".png", img)
Logger.debug("Took a screenshot of current loot")
start = prev_cast_start = time.time()
time_out = False
timeout = False
picked_up_items = []
skip_items = []
curr_item_to_pick: Item = None
@@ -43,9 +43,9 @@ class PickIt:
did_force_move = False
done_ocr=False
while not time_out:
while not timeout:
if (time.time() - start) > 28:
time_out = True
timeout = True
Logger.warning("Got stuck during pickit, skipping it this time...")
break
img = grab()
+3 -2
View File
@@ -5,7 +5,7 @@ import keyboard
from template_finder import TemplateFinder
from config import Config
from screen import grab
from ui_manager import detect_screen_object, ScreenObjects, center_mouse
from ui_manager import detect_screen_object, ScreenObjects, center_mouse, wait_until_hidden
from utils.misc import color_filter, wait
from logger import Logger
from utils.custom_mouse import mouse
@@ -222,7 +222,8 @@ npcs = {
def escape_dialogue(img) -> np.ndarray:
while detect_screen_object(ScreenObjects.NPCDialogue, img).valid:
keyboard.send("esc")
wait(0.2)
if wait_until_hidden(ScreenObjects.NPCDialogue, 0.5):
break
img = grab()
return img
+5 -5
View File
@@ -594,7 +594,7 @@ class Pather:
self,
path: Union[tuple[Location, Location], list[int]],
char: IChar,
time_out: float = 5,
timeout: float = 5,
force_tp: bool = False,
do_pre_move: bool = True,
force_move: bool = False,
@@ -604,7 +604,7 @@ class Pather:
"""Traverse from one location to another
:param path: Either a list of node indices or a tuple with (start_location, end_location)
:param char: Char that is traversing the nodes
:param time_out: Timeout in second. If no more move was found in that time it will cancel traverse
:param timeout: Timeout in second. If no more move was found in that time it will cancel traverse
:param force_move: Bool value if force move should be used for pathing
:return: Bool if traversed successful or False if it got stuck
"""
@@ -641,17 +641,17 @@ class Pather:
while not continue_to_next_node:
img = grab()
# Handle timeout
if (time.time() - last_move) > time_out:
if (time.time() - last_move) > timeout:
if detect_screen_object(ScreenObjects.WaypointLabel).valid:
# sometimes bot opens waypoint menu, close it to find templates again
Logger.debug("Opened wp, closing it again")
keyboard.send("esc")
last_move = time.time()
else:
# This is a bit hacky, but for moving into a boss location we set time_out usually quite low
# This is a bit hacky, but for moving into a boss location we set timeout usually quite low
# because of all the spells and monsters it often can not determine the final template
# Don't want to spam the log with errors in this case because it most likely worked out just fine
if time_out > 3.1:
if timeout > 3.1:
if Config().general["info_screenshots"]:
cv2.imwrite("./info_screenshots/info_pather_got_stuck_" + time.strftime("%Y%m%d_%H%M%S") + ".png", img)
Logger.error("Got stuck exit pather")
+5 -5
View File
@@ -41,19 +41,19 @@ class Arcane:
# Check if we arrived at platform
templates_platform = ["ARC_PLATFORM_1", "ARC_PLATFORM_2", "ARC_PLATFORM_3", "ARC_CENTER"]
tempaltes_summoner = ["ARC_ALTAR", "ARC_ALTAR3", "ARC_END_STAIRS", "ARC_END_STAIRS_2"]
match_platform = TemplateFinder().search_and_wait(templates_platform, threshold=0.55, time_out=0.5, use_grayscale=True, take_ss=False)
match_summoner = TemplateFinder().search_and_wait(tempaltes_summoner, threshold=0.79, time_out=0.5, use_grayscale=True, take_ss=False)
match_platform = TemplateFinder().search_and_wait(templates_platform, threshold=0.55, timeout=0.5, use_grayscale=True, take_ss=False)
match_summoner = TemplateFinder().search_and_wait(tempaltes_summoner, threshold=0.79, timeout=0.5, use_grayscale=True, take_ss=False)
if not match_platform.valid and not match_summoner.valid:
# We might have arrived at summoner, move up stairs with static traverse
self._pather.traverse_nodes_fixed(traverse_to_summoner, self._char)
# try to match summoner again
match_summoner = TemplateFinder().search_and_wait(tempaltes_summoner, threshold=0.79, time_out=1.0, use_grayscale=True, take_ss=False)
match_summoner = TemplateFinder().search_and_wait(tempaltes_summoner, threshold=0.79, timeout=1.0, use_grayscale=True, take_ss=False)
if match_summoner.valid:
if self._pather.traverse_nodes([461], self._char, time_out=2.2, force_tp=True):
if self._pather.traverse_nodes([461], self._char, timeout=2.2, force_tp=True):
return True
else:
# Traverse to center of platform
self._pather.traverse_nodes([462], self._char, time_out=1.3, force_tp=True)
self._pather.traverse_nodes([462], self._char, timeout=1.3, force_tp=True)
return False
def battle(self, do_pre_buff: bool) -> Union[bool, tuple[Location, bool]]:
+17 -17
View File
@@ -78,7 +78,7 @@ class Diablo:
self._curr_loc = self._town_manager.buy_pots(self._curr_loc, pot_needs["health"], pot_needs["mana"])
Logger.debug(location + ": Done in town, now going back to portal...")
# Move from Act 4 NPC Jamella towards WP where we can see the Blue Portal
if not self._pather.traverse_nodes([164, 163], self._char, time_out=2): return False
if not self._pather.traverse_nodes([164, 163], self._char, timeout=2): return False
wait(0.22, 0.28)
template_match = detect_screen_object(ScreenObjects.TownPortalReduced)
if template_match.valid:
@@ -111,9 +111,9 @@ class Diablo:
i = 0
while i < 4:
Logger.debug(seal_layout + ": trying to open (try #" + str(i+1)+")")
self._char.select_by_template(seal_closedtemplates, threshold=0.5, time_out=0.1, telekinesis=True)
self._char.select_by_template(seal_closedtemplates, threshold=0.5, timeout=0.1, telekinesis=True)
wait(i*0.5)
found = TemplateFinder().search_and_wait(seal_opentemplates, threshold=0.75, time_out=0.1, best_match=True, take_ss=False).valid
found = TemplateFinder().search_and_wait(seal_opentemplates, threshold=0.75, timeout=0.1, best_match=True, take_ss=False).valid
if found:
Logger.debug(seal_layout +": is open - "+'\033[92m'+" open"+'\033[0m')
break
@@ -143,7 +143,7 @@ class Diablo:
templates = ["DIA_NEW_PENT_TP", "DIA_NEW_PENT_0", "DIA_NEW_PENT_1", "DIA_NEW_PENT_2"]
start_time = time.time()
while not found and time.time() - start_time < 15:
found = TemplateFinder().search_and_wait(templates, threshold=0.83, time_out=0.1, best_match=True, take_ss=False).valid
found = TemplateFinder().search_and_wait(templates, threshold=0.83, timeout=0.1, best_match=True, take_ss=False).valid
if not found: self._pather.traverse_nodes_fixed(path, self._char)
if not found:
if Config().general["info_screenshots"]: cv2.imwrite(f"./info_screenshots/info_failed_loop_pentagram_" + path + "_" + time.strftime("%Y%m%d_%H%M%S") + ".png", grab())
@@ -161,10 +161,10 @@ class Diablo:
if not self._pather.traverse_nodes([605], self._char): return False
templates = ["DIABLO_ENTRANCE_53", "DIABLO_ENTRANCE_51","DIABLO_ENTRANCE_50", "DIABLO_ENTRANCE_52", "DIABLO_ENTRANCE_54", "DIABLO_ENTRANCE_55"]
if TemplateFinder().search_and_wait(templates, threshold=0.8, time_out=0.1, best_match=False, take_ss=False).valid:
if TemplateFinder().search_and_wait(templates, threshold=0.8, timeout=0.1, best_match=False, take_ss=False).valid:
Logger.debug("CS Trash (A): Layout_check step 1/2: Layout A templates found")
templates = ["DIABLO_ENTRANCE2_55", "DIABLO_ENTRANCE2_50", "DIABLO_ENTRANCE2_51", "DIABLO_ENTRANCE2_52","DIABLO_ENTRANCE2_53","DIABLO_ENTRANCE2_54","DIABLO_ENTRANCE2_15","DIABLO_ENTRANCE2_56"]
if not TemplateFinder().search_and_wait(templates, threshold=0.8, time_out=0.5, best_match=True, take_ss=False).valid:
if not TemplateFinder().search_and_wait(templates, threshold=0.8, timeout=0.5, best_match=True, take_ss=False).valid:
Logger.debug("CS Trash (A): Layout_check step 2/2: Layout B templates NOT found - "+'\033[95m'+"all fine, proceeding with Layout A"+'\033[0m')
entrance1_layout = "CS Trash (A):"
#if Config().general["info_screenshots"]: cv2.imwrite(f"./info_screenshots/info_" + entrance1_layout + "_" + time.strftime("%Y%m%d_%H%M%S") + ".png", grab())
@@ -185,7 +185,7 @@ class Diablo:
else:
Logger.debug("CS Trash (B): Layout_check step 1/2: Layout A templates NOT found")
templates = ["DIABLO_ENTRANCE2_55", "DIABLO_ENTRANCE2_50", "DIABLO_ENTRANCE2_51", "DIABLO_ENTRANCE2_52","DIABLO_ENTRANCE2_53","DIABLO_ENTRANCE2_54","DIABLO_ENTRANCE2_15","DIABLO_ENTRANCE2_56"]
if TemplateFinder().search_and_wait(templates, threshold=0.8, time_out=0.1, best_match=False, take_ss=False).valid:
if TemplateFinder().search_and_wait(templates, threshold=0.8, timeout=0.1, best_match=False, take_ss=False).valid:
Logger.debug("CS Trash (B): Layout_check step 2/2: Layout B templates found - "+'\033[96m'+"all fine, proceeding with Layout B"+'\033[0m')
entrance2_layout = "CS Trash (B):"
#if Config().general["info_screenshots"]: cv2.imwrite(f"./info_screenshots/info_" + entrance2_layout + "_" + time.strftime("%Y%m%d_%H%M%S") + ".png", grab())
@@ -214,7 +214,7 @@ class Diablo:
templates = ["DIA_NEW_PENT_0", "DIA_NEW_PENT_1", "DIA_NEW_PENT_2"]
start_time = time.time()
while not found and time.time() - start_time < 10:
found = TemplateFinder().search_and_wait(templates, threshold=0.8, time_out=0.1, best_match=True, take_ss=False).valid
found = TemplateFinder().search_and_wait(templates, threshold=0.8, timeout=0.1, best_match=True, take_ss=False).valid
if not found:
self._pather.traverse_nodes_fixed("diablo_wp_pentagram_loop", self._char)
if not found:
@@ -235,7 +235,7 @@ class Diablo:
templates = ["DIABLO_CS_ENTRANCE_0", "DIABLO_CS_ENTRANCE_2", "DIABLO_CS_ENTRANCE_3"]
start_time = time.time()
while not found and time.time() - start_time < 10:
found = TemplateFinder().search_and_wait(templates, threshold=0.8, time_out=0.1, best_match=True, take_ss=False).valid
found = TemplateFinder().search_and_wait(templates, threshold=0.8, timeout=0.1, best_match=True, take_ss=False).valid
if not found:
self._pather.traverse_nodes_fixed("diablo_wp_entrance_loop", self._char)
if not found:
@@ -251,7 +251,7 @@ class Diablo:
templates = ["DIA_NEW_PENT_TP", "DIA_NEW_PENT_0", "DIA_NEW_PENT_1", "DIA_NEW_PENT_2"]
start_time = time.time()
while not found and time.time() - start_time < 15:
found = TemplateFinder().search_and_wait(templates, threshold=0.83, time_out=0.1, best_match=True, take_ss=False).valid
found = TemplateFinder().search_and_wait(templates, threshold=0.83, timeout=0.1, best_match=True, take_ss=False).valid
if not found: self._pather.traverse_nodes_fixed("diablo_wp_pentagram_loop", self._char)
if not found:
if Config().general["info_screenshots"]: cv2.imwrite(f"./info_screenshots/info_failed_loop_pentagram_diablo_wp_pentagram_loop_" + time.strftime("%Y%m%d_%H%M%S") + ".png", grab())
@@ -277,23 +277,23 @@ class Diablo:
"""
#CLEAR TRASH BETWEEN PENTAGRAM & LAYOUT CHECK (clear_trash=1): NEW METHOD, BUT ONLY 50% EFFICACY
def _trash_seals(self, seal:str, path:str, node_calibration:str, loop_path:str, threshold:float) -> bool:
if not self._pather.traverse_nodes([602], self._char, time_out=2): return False
if not self._pather.traverse_nodes([602], self._char, timeout=2): return False
if not self._pather.traverse_nodes_fixed(path, self._char): return False
Logger.info("CS TRASH: " + seal + " Pent to LC")
self._char.kill_cs_trash(path)
if not self._pather.traverse_nodes(node_calibration, self._char, time_out=2, threshold=threshold): return False
if not self._pather.traverse_nodes(node_calibration, self._char, timeout=2, threshold=threshold): return False
Logger.info("CS TRASH: " + str(seal) + " looping to PENTAGRAM")
#if not self._loop_pentagram(loop_path): return False
found = False
templates = ["DIA_NEW_PENT_TP", "DIA_NEW_PENT_0", "DIA_NEW_PENT_1", "DIA_NEW_PENT_2"]
start_time = time.time()
while not found and time.time() - start_time < 15:
found = TemplateFinder().search_and_wait(templates, threshold=0.83, time_out=0.1, best_match=True, take_ss=False).valid
found = TemplateFinder().search_and_wait(templates, threshold=0.83, timeout=0.1, best_match=True, take_ss=False).valid
if not found: self._pather.traverse_nodes_fixed(loop_path, self._char)
if not found:
if Config().general["info_screenshots"]: cv2.imwrite(f"./info_screenshots/info_failed_loop_pentagram_" + path + "_" + time.strftime("%Y%m%d_%H%M%S") + ".png", grab())
return False
if not self._pather.traverse_nodes([602], self._char, time_out=2): return False
if not self._pather.traverse_nodes([602], self._char, timeout=2): return False
Logger.info("CS TRASH: " + str(seal) + " calibrated at PENTAGRAM")
return True
"""
@@ -370,12 +370,12 @@ class Diablo:
if not self._pather.traverse_nodes(calibration_node, self._char, threshold=calibration_threshold,): return False
#if Config().general["info_screenshots"]: cv2.imwrite(f"./info_screenshots/info_LC_" + sealname + "_" + time.strftime("%Y%m%d_%H%M%S") + ".png", grab())
#check1 using primary templates
if not TemplateFinder().search_and_wait(templates_primary, threshold =threshold_primary, time_out=0.1, best_match=True, take_ss=False).valid:
if not TemplateFinder().search_and_wait(templates_primary, threshold =threshold_primary, timeout=0.1, best_match=True, take_ss=False).valid:
Logger.debug(f"{seal_layout1}: Layout_check step 1/2 - templates NOT found for "f"{seal_layout2}")
#cross-check for confirmation
if not confirmation_node == None:
if not self._pather.traverse_nodes(confirmation_node, self._char, threshold=calibration_threshold,): return False
if not TemplateFinder().search_and_wait(templates_confirmation, threshold=threshold_confirmation, time_out=0.1, best_match=True, take_ss=False).valid:
if not TemplateFinder().search_and_wait(templates_confirmation, threshold=threshold_confirmation, timeout=0.1, best_match=True, take_ss=False).valid:
Logger.warning(f"{seal_layout2}: Layout_check failure - could not determine the seal Layout at" f"{sealname} ("f"{boss}) - "+'\033[91m'+"aborting run"+'\033[0m')
if Config().general["info_screenshots"]: cv2.imwrite(f"./info_screenshots/info_" + seal_layout1 + "_LC_fail" + time.strftime("%Y%m%d_%H%M%S") + ".png", grab())
return False
@@ -387,7 +387,7 @@ class Diablo:
#cross-check for confirmation
if not confirmation_node2 == None:
if not self._pather.traverse_nodes(confirmation_node2, self._char, threshold=calibration_threshold,): return False
if not TemplateFinder().search_and_wait(templates_confirmation, threshold=threshold_confirmation2, time_out=0.1, best_match=True, take_ss=False).valid:
if not TemplateFinder().search_and_wait(templates_confirmation, threshold=threshold_confirmation2, timeout=0.1, best_match=True, take_ss=False).valid:
Logger.debug(f"{seal_layout2}: Layout_check step 2/2 - templates NOT found for "f"{seal_layout1} - "+'\033[94m'+"all fine, proceeding with "f"{seal_layout2}"+'\033[0m')
if not self._seal(*params_seal2): return False
else:
+8 -8
View File
@@ -40,7 +40,7 @@ class Nihlathak:
def battle(self, do_pre_buff: bool) -> Union[bool, tuple[Location, bool]]:
# TODO: We might need a second template for each option as merc might run into the template and we dont find it then
# Let's check which layout ("NI1_A = bottom exit" , "NI1_B = large room", "NI1_C = small room")
template_match = TemplateFinder().search_and_wait(["NI1_A", "NI1_B", "NI1_C"], threshold=0.65, time_out=20)
template_match = TemplateFinder().search_and_wait(["NI1_A", "NI1_B", "NI1_C"], threshold=0.65, timeout=20)
if not template_match.valid:
return False
if do_pre_buff:
@@ -50,16 +50,16 @@ class Nihlathak:
# Its xpects that the static routes defined in game.ini are named: "ni1_a", "ni1_b", "ni1_c"
self._pather.traverse_nodes_fixed(template_match.name.lower(), self._char)
found_loading_screen_func = lambda: loading.wait_for_loading_screen(2.0) or \
TemplateFinder().search_and_wait(["NI2_SEARCH_0", "NI2_SEARCH_1"], threshold=0.8, time_out=0.5).valid
TemplateFinder().search_and_wait(["NI2_SEARCH_0", "NI2_SEARCH_1"], threshold=0.8, timeout=0.5).valid
# look for stairs
if not self._char.select_by_template(["NI1_STAIRS", "NI1_STAIRS_2", "NI1_STAIRS_3", "NI1_STAIRS_4"], found_loading_screen_func, threshold=0.63, time_out=4):
if not self._char.select_by_template(["NI1_STAIRS", "NI1_STAIRS_2", "NI1_STAIRS_3", "NI1_STAIRS_4"], found_loading_screen_func, threshold=0.63, timeout=4):
# do a random tele jump and try again
pos_m = convert_abs_to_monitor((random.randint(-70, 70), random.randint(-70, 70)))
self._char.move(pos_m, force_move=True)
if not self._char.select_by_template(["NI1_STAIRS", "NI1_STAIRS_2", "NI1_STAIRS_3", "NI1_STAIRS_4"], found_loading_screen_func, threshold=0.63, time_out=4):
if not self._char.select_by_template(["NI1_STAIRS", "NI1_STAIRS_2", "NI1_STAIRS_3", "NI1_STAIRS_4"], found_loading_screen_func, threshold=0.63, timeout=4):
return False
# Wait until templates in lvl 2 entrance are found
if not TemplateFinder().search_and_wait(["NI2_SEARCH_0", "NI2_SEARCH_1", "NI2_SEARCH_2"], threshold=0.8, time_out=20).valid:
if not TemplateFinder().search_and_wait(["NI2_SEARCH_0", "NI2_SEARCH_1", "NI2_SEARCH_2"], threshold=0.8, timeout=20).valid:
return False
wait(1.0) # wait to make sure the red writing is gone once we check for the eye
@dataclass
@@ -82,11 +82,11 @@ class Nihlathak:
# Move to spot where eye would be visible
self._pather.traverse_nodes_fixed(data.circle_static_path_key, self._char)
# Search for eye
template_match = TemplateFinder().search_and_wait(data.template_name, threshold=0.7, best_match=True, time_out=3)
template_match = TemplateFinder().search_and_wait(data.template_name, threshold=0.7, best_match=True, timeout=3)
# If it is found, move down that hallway
if template_match.valid and template_match.name.endswith("_SAFE_DIST"):
self._pather.traverse_nodes_fixed(data.destination_static_path_key, self._char)
self._pather.traverse_nodes(data.save_dist_nodes, self._char, time_out=2, do_pre_move=False)
self._pather.traverse_nodes(data.save_dist_nodes, self._char, timeout=2, do_pre_move=False)
end_nodes = data.end_nodes
break
@@ -94,7 +94,7 @@ class Nihlathak:
if end_nodes is None:
self._pather.traverse_nodes_fixed("ni2_circle_back_to_a", self._char)
self._pather.traverse_nodes_fixed(check_arr[0].destination_static_path_key, self._char)
self._pather.traverse_nodes(check_arr[0].save_dist_nodes, self._char, time_out=2, do_pre_move=False)
self._pather.traverse_nodes(check_arr[0].save_dist_nodes, self._char, timeout=2, do_pre_move=False)
end_nodes = check_arr[0].end_nodes
# Attack & Pick items
+1 -1
View File
@@ -38,7 +38,7 @@ class Pindle:
def battle(self, do_pre_buff: bool) -> Union[bool, tuple[Location, bool]]:
# Kill Pindle
if not TemplateFinder().search_and_wait(["PINDLE_0", "PINDLE_1"], threshold=0.65, time_out=20).valid:
if not TemplateFinder().search_and_wait(["PINDLE_0", "PINDLE_1"], threshold=0.65, timeout=20).valid:
return False
if do_pre_buff:
self._char.pre_buff()
+1 -1
View File
@@ -35,7 +35,7 @@ class ShenkEld:
def battle(self, do_shenk: bool, do_pre_buff: bool, game_stats) -> Union[bool, tuple[Location, bool]]:
# Eldritch
game_stats.update_location("Eld" if Config().general['discord_status_condensed'] else "Eldritch")
if not TemplateFinder().search_and_wait(["ELDRITCH_0", "ELDRITCH_START"], threshold=0.65, time_out=20).valid:
if not TemplateFinder().search_and_wait(["ELDRITCH_0", "ELDRITCH_START"], threshold=0.65, timeout=20).valid:
return False
if do_pre_buff:
self._char.pre_buff()
+4 -4
View File
@@ -35,7 +35,7 @@ class Trav:
def battle(self, do_pre_buff: bool) -> Union[bool, tuple[Location, bool]]:
# Kill Council
if not TemplateFinder().search_and_wait(["TRAV_0", "TRAV_1", "TRAV_20"], threshold=0.65, time_out=20).valid:
if not TemplateFinder().search_and_wait(["TRAV_0", "TRAV_1", "TRAV_20"], threshold=0.65, timeout=20).valid:
return False
if do_pre_buff:
self._char.pre_buff()
@@ -49,9 +49,9 @@ class Trav:
wait(0.2, 0.3)
# If we can teleport we want to move back inside and also check loot there
if self._char.capabilities.can_teleport_natively or self._char.capabilities.can_teleport_with_charges:
if not self._pather.traverse_nodes([229], self._char, time_out=2.5, use_tp_charge=True):
self._pather.traverse_nodes([228, 229], self._char, time_out=2.5, use_tp_charge=True)
if not self._pather.traverse_nodes([229], self._char, timeout=2.5, use_tp_charge=True):
self._pather.traverse_nodes([228, 229], self._char, timeout=2.5, use_tp_charge=True)
picked_up_items |= self._pickit.pick_up_items(self._char, is_at_trav=True)
# Make sure we go back to the center to not hide the tp
self._pather.traverse_nodes([230], self._char, time_out=2.5)
self._pather.traverse_nodes([230], self._char, timeout=2.5)
return (Location.A3_TRAV_CENTER_STAIRS, picked_up_items)
+3 -3
View File
@@ -28,9 +28,9 @@ def exit(run_obj):
os._exit(0)
def wait_for_loading_screen(time_out):
def wait_for_loading_screen(timeout):
start = time.time()
while time.time() - start < time_out:
while time.time() - start < timeout:
img = grab()
is_loading_black_roi = np.average(img[:700, 0:250]) < 4.0
if is_loading_black_roi:
@@ -221,7 +221,7 @@ class AnyaShopper:
def select_by_template(self, template_type: str) -> bool:
Logger.debug(f"Select {template_type}")
template_match = TemplateFinder(True).search_and_wait(template_type, time_out=10, normalize_monitor=True)
template_match = TemplateFinder(True).search_and_wait(template_type, timeout=10, normalize_monitor=True)
if template_match.valid:
mouse.move(*template_match.center)
wait(0.1, 0.2)
+4 -4
View File
@@ -204,7 +204,7 @@ class TemplateFinder:
self,
ref: Union[str, list[str]],
roi: list[float] = None,
time_out: float = None,
timeout: float = None,
threshold: float = 0.68,
normalize_monitor: bool = False,
best_match: bool = False,
@@ -215,7 +215,7 @@ class TemplateFinder:
) -> TemplateMatch:
"""
Helper function that will loop and keep searching for a template
:param time_out: After this amount of time the search will stop and it will return [False, None]
:param timeout: After this amount of time the search will stop and it will return [False, None]
:param take_ss: Bool value to take screenshot on timeout or not (flag must still be set in params!)
Other params are the same as for TemplateFinder.search()
"""
@@ -232,9 +232,9 @@ class TemplateFinder:
if template_match.valid:
Logger.debug(f"Found Match: {template_match.name} ({template_match.score*100:.1f}% confidence)")
return template_match
if time_out is not None and (time.time() - start) > time_out:
if timeout is not None and (time.time() - start) > timeout:
if Config().general["info_screenshots"] and take_ss:
cv2.imwrite(f"./info_screenshots/info_wait_for_{ref}_time_out_" + time.strftime("%Y%m%d_%H%M%S") + ".png", img)
cv2.imwrite(f"./info_screenshots/info_wait_for_{ref}_timeout_" + time.strftime("%Y%m%d_%H%M%S") + ".png", img)
if take_ss:
Logger.debug(f"Could not find any of the above templates")
return template_match
+1 -1
View File
@@ -43,7 +43,7 @@ class A1(IAct):
return self._char.select_by_template(["A1_WP"], found_wp_func, threshold=0.62)
def wait_for_tp(self) -> Union[Location, bool]:
success = TemplateFinder().search_and_wait(["A1_TOWN_7", "A1_TOWN_9"], time_out=20).valid
success = TemplateFinder().search_and_wait(["A1_TOWN_7", "A1_TOWN_9"], timeout=20).valid
if not self._pather.traverse_nodes([Location.A1_TOWN_TP, Location.A1_KASHYA_CAIN], self._char, force_move=True): return False
if success:
return Location.A1_KASHYA_CAIN
+1 -1
View File
@@ -68,7 +68,7 @@ class A2(IAct):
return self._char.select_by_template(["A2_WP_LIGHT", "A2_WP_DARK"], found_wp_func, telekinesis=True)
def wait_for_tp(self) -> Union[Location, bool]:
template_match = TemplateFinder().search_and_wait(["A2_TOWN_21", "A2_TOWN_22", "A2_TOWN_20", "A2_TOWN_19"], time_out=20)
template_match = TemplateFinder().search_and_wait(["A2_TOWN_21", "A2_TOWN_22", "A2_TOWN_20", "A2_TOWN_19"], timeout=20)
if template_match.valid:
self._pather.traverse_nodes((Location.A2_TP, Location.A2_FARA_STASH), self._char, force_move=True)
return Location.A2_FARA_STASH
+1 -1
View File
@@ -54,7 +54,7 @@ class A3(IAct):
return self._char.select_by_template("A3_WP", found_wp_func, telekinesis=True)
def wait_for_tp(self) -> Union[Location, bool]:
template_match = TemplateFinder().search_and_wait("A3_TOWN_10", time_out=20)
template_match = TemplateFinder().search_and_wait("A3_TOWN_10", timeout=20)
if template_match.valid:
self._pather.traverse_nodes((Location.A3_STASH_WP, Location.A3_STASH_WP), self._char, force_move=True)
return Location.A3_STASH_WP
+1 -1
View File
@@ -40,7 +40,7 @@ class A4(IAct):
return self._char.select_by_template(["A4_WP", "A4_WP_2"], found_wp_func, threshold=0.62, telekinesis=False)
def wait_for_tp(self) -> Union[Location, bool]:
success = TemplateFinder().search_and_wait(["A4_TOWN_4", "A4_TOWN_5", "A4_TOWN_6"], time_out=20).valid
success = TemplateFinder().search_and_wait(["A4_TOWN_4", "A4_TOWN_5", "A4_TOWN_6"], timeout=20).valid
if success:
return Location.A4_TOWN_START
return False
+1 -1
View File
@@ -76,7 +76,7 @@ class A5(IAct):
return self._char.select_by_template("A5_WP", found_wp_func, telekinesis=True)
def wait_for_tp(self) -> Union[Location, bool]:
success = TemplateFinder().search_and_wait(["A5_TOWN_1", "A5_TOWN_0"], time_out=20).valid
success = TemplateFinder().search_and_wait(["A5_TOWN_1", "A5_TOWN_0"], timeout=20).valid
if success:
return Location.A5_TOWN_START
return False
+1 -1
View File
@@ -21,7 +21,7 @@ class IAct:
# Is trade/repair implemented in this Town?
def can_trade_and_repair(self) -> bool: return False
# Is merc resurrection implemented for this Town?
def can_identify(self) -> bool: return False
def can_identify(self) -> bool: return False
def can_gamble (self) -> bool: return False
# If any of the above functions return True for the Town, the respective method must be implemented
def open_trade_menu(self, curr_loc: Location) -> Union[Location, bool]: return False
+4 -4
View File
@@ -43,12 +43,12 @@ class TownManager:
location = Location.A1_TOWN_START
return location
def wait_for_town_spawn(self, time_out: float = None) -> Location:
def wait_for_town_spawn(self, timeout: float = None) -> Location:
"""Wait for the char to spawn in town after starting a new game
:param time_out: Optional float value for time out in seconds, defaults to None
:return: Location of the town (e.g. Location.A4_TOWN_START) or None if nothing was found within time_out time
:param timeout: Optional float value for time out in seconds, defaults to None
:return: Location of the town (e.g. Location.A4_TOWN_START) or None if nothing was found within timeout time
"""
template_match = TemplateFinder().search_and_wait(TOWN_MARKERS, best_match=True, time_out=time_out)
template_match = TemplateFinder().search_and_wait(TOWN_MARKERS, best_match=True, timeout=timeout)
if template_match.valid:
return TownManager.get_act_from_location(template_match.name)
return None
+2 -2
View File
@@ -1,7 +1,7 @@
import itertools
from random import randint
from config import Config
from ui_manager import detect_screen_object, wait_for_screen_object, ScreenObjects
from ui_manager import detect_screen_object, wait_until_visible, ScreenObjects
from .inventory_collection import InventoryCollection
from .stash import Stash
from .gem_picking import SimpleGemPicking
@@ -176,7 +176,7 @@ class Transmute:
return self._game_stats._game_counter - self._last_game >= int(every_x_game)
def run_transmutes(self, force=False) -> None:
gold_btn = wait_for_screen_object(ScreenObjects.GoldBtnStash, time_out = 20)
gold_btn = wait_until_visible(ScreenObjects.GoldBtnStash, timeout = 8)
if not gold_btn.valid:
Logger.error("Could not determine to be in stash menu. Continue...")
return
+3 -3
View File
@@ -7,14 +7,14 @@ def check_for_black_screen() -> bool:
img = grab()
return np.average(img[:, 0:Config().ui_roi["loading_left_black"][2]]) < 1.5
def wait_for_loading_screen(time_out: float = 10.0) -> bool:
def wait_for_loading_screen(timeout: float = 10.0) -> bool:
"""
Waits until loading screen apears
:param time_out: Maximum time to search for a loading screen
:param timeout: Maximum time to search for a loading screen
:return: True if loading screen was found within the timeout. False otherwise
"""
start = time.time()
while time.time() - start < time_out:
while time.time() - start < timeout:
if check_for_black_screen():
return True
return False
+2 -2
View File
@@ -8,7 +8,7 @@ from utils.misc import cut_roi, color_filter, wait
from screen import grab
from config import Config
from template_finder import TemplateFinder
from ui_manager import wait_for_screen_object, ScreenObjects
from ui_manager import wait_until_visible, ScreenObjects
from inventory import consumables
def is_left_skill_selected(template_list: List[str]) -> bool:
@@ -27,7 +27,7 @@ def has_tps() -> bool:
"""
if Config().char["tp"]:
keyboard.send(Config().char["tp"])
template_match = wait_for_screen_object(ScreenObjects.TownPortalSkill, time_out=4)
template_match = wait_until_visible(ScreenObjects.TownPortalSkill, timeout=4)
if not template_match.valid:
Logger.warning("You are out of tps")
if Config().general["info_screenshots"]:
+2 -2
View File
@@ -5,8 +5,8 @@ import keyboard
from utils.custom_mouse import mouse
from logger import Logger
from utils.misc import wait
from ui_manager import wait_until_visible, detect_screen_object, select_screen_object_match, ScreenObjects, list_visible_objects
from inventory import common
from ui_manager import wait_for_screen_object, detect_screen_object, select_screen_object_match, ScreenObjects, list_visible_objects
from screen import convert_screen_to_monitor
def enable_no_pickup() -> bool:
@@ -19,7 +19,7 @@ def enable_no_pickup() -> bool:
keyboard.write('/nopickup',delay=.20)
keyboard.send('enter')
wait(0.1, 0.25)
item_pickup_text = wait_for_screen_object(ScreenObjects.ItemPickupText, time_out=3)
item_pickup_text = wait_until_visible(ScreenObjects.ItemPickupText, timeout=3)
if not item_pickup_text.valid:
return False
if item_pickup_text.name == "ITEM_PICKUP_DISABLED":
+32 -24
View File
@@ -2,6 +2,7 @@ import keyboard
import os
import numpy as np
import time
from typing import Union, TypeVar, Callable
from utils.custom_mouse import mouse
from utils.misc import wait
from logger import Logger
@@ -14,6 +15,7 @@ from game_stats import GameStats
messenger = Messenger()
game_stats = GameStats()
T = TypeVar('T')
@dataclass
class ScreenObject:
@@ -21,12 +23,13 @@ class ScreenObject:
ref: list[str]
inp_img: np.ndarray = None
roi: list[float] = None
time_out: float = 30
timeout: float = 30
threshold: float = 0.68
normalize_monitor: bool = False
best_match: bool = False
use_grayscale: bool = False
color_match: list[np.array] = None
suppress_debug: bool = False
def __call__(self, cls):
cls._screen_object = self
@@ -272,17 +275,15 @@ class ScreenObjects:
def detect_screen_object(screen_object: ScreenObject, img: np.ndarray = None) -> TemplateMatch:
roi = Config().ui_roi[screen_object.roi] if screen_object.roi else None
img = grab() if img is None else img
match = TemplateFinder().search(
return TemplateFinder().search(
ref = screen_object.ref,
inp_img = img,
threshold = screen_object.threshold,
roi = roi,
best_match = screen_object.best_match,
use_grayscale = screen_object.use_grayscale,
normalize_monitor = screen_object.normalize_monitor)
if match.valid:
return match
return match
normalize_monitor = screen_object.normalize_monitor
)
def select_screen_object_match(match: TemplateMatch, delay_factor: tuple[float, float] = (0.9, 1.1)) -> None:
mouse.move(*convert_screen_to_monitor(match.center), delay_factor=delay_factor)
@@ -290,21 +291,28 @@ def select_screen_object_match(match: TemplateMatch, delay_factor: tuple[float,
mouse.click("left")
wait(0.05, 0.09)
def wait_for_screen_object(screen_object: ScreenObject, time_out: int = None) -> TemplateMatch:
roi = Config().ui_roi[screen_object.roi] if screen_object.roi else None
time_out = time_out if time_out else 30
match = TemplateFinder().search_and_wait(
ref = screen_object.ref,
time_out = time_out,
threshold = screen_object.threshold,
roi = roi,
best_match = screen_object.best_match,
use_grayscale = screen_object.use_grayscale,
normalize_monitor = screen_object.normalize_monitor)
if match.valid:
return match
def is_visible(screen_object: ScreenObject, img: np.ndarray = None) -> bool:
return detect_screen_object(screen_object, img).valid
def wait_until_visible(screen_object: ScreenObject, timeout: float = 30) -> TemplateMatch:
if not (match := wait_until(lambda: detect_screen_object(screen_object), lambda match: match.valid, timeout)[0]).valid:
Logger.debug(f"{screen_object.ref} not found after {timeout} seconds")
return match
def wait_until_hidden(screen_object: ScreenObject, timeout: float = 3) -> bool:
if not (hidden := wait_until(lambda: detect_screen_object(screen_object).valid, lambda res: not res, timeout)[1]):
Logger.debug(f"{screen_object.ref} still found after {timeout} seconds")
return hidden
def wait_until(func: Callable[[], T], is_success: Callable[[T], bool], timeout = None) -> Union[T, None]:
start = time.time()
while (time.time() - start) < timeout:
res = func()
if (success := is_success(res)):
break
wait(0.1)
return res, success
def hover_over_screen_object_match(match) -> None:
mouse.move(*convert_screen_to_monitor(match.center))
wait(0.2, 0.4)
@@ -313,8 +321,7 @@ def list_visible_objects(img: np.ndarray = None) -> list:
img = grab() if img is None else img
visible=[]
for pair in [a for a in vars(ScreenObjects).items() if not a[0].startswith('__') and a[1] is not None]:
if (match := detect_screen_object(pair[1], img)).valid:
# visible.append(match)
if detect_screen_object(pair[1], img).valid:
visible.append(pair[0])
return visible
@@ -334,6 +341,7 @@ if __name__ == "__main__":
print("Go to D2R window and press f11 to start game")
keyboard.wait("f11")
from config import Config
while 1:
print(list_visible_objects())
time.sleep(1)
# while 1:
# print(list_visible_objects())
# time.sleep(1)
print(f"res = {wait_until_visible(ScreenObjects.BeltExpandable, 7)}")