Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1f16c13300 | ||
|
|
ee479fb526 | ||
|
|
d05ff50f76 | ||
|
|
927ade6108 | ||
|
|
5eda34ded0 | ||
|
|
d9b9d66e3b | ||
|
|
1adeb3a32a | ||
|
|
1abf57eae1 | ||
|
|
65797eba8f | ||
|
|
03a6e8b05c | ||
|
|
2c09459bde | ||
|
|
7564fea4b0 | ||
|
|
95b27f099f | ||
|
|
e96c042853 | ||
|
|
57bf7a0f7d | ||
|
|
da892d9043 | ||
|
|
7fad5e9aff | ||
|
|
840ffcdf0c | ||
|
|
1078563f2d |
@@ -50,8 +50,3 @@ jobs:
|
||||
run: |
|
||||
C:\Miniconda\condabin\conda.bat activate botty
|
||||
coverage xml
|
||||
- name: Upload Coverage to Codecov
|
||||
uses: codecov/codecov-action@v2
|
||||
with:
|
||||
verbose: true
|
||||
files: ./coverage.xml
|
||||
|
||||
@@ -97,7 +97,7 @@ run_shenk=0
|
||||
| battle_order | Hotkey for battle orders from cta (only needed if cta_available=1) |
|
||||
| battle_command | Hotkey for battle command from cta (only needed if cta_available=1) |
|
||||
| stash_gold | Bool value to stash gold each time when stashing items |
|
||||
| gold_trav_only | Hacky config that will restrict gold pickup to trav only. misc_gold must be set to 1 for this to have any effect |
|
||||
| min_gold_to_pick | Minimum quantity of gold to pickup (also must set misc_gold=1 in pickit config) |
|
||||
| use_merc | Set to 1 for using merc. Set to 0 for not using merc (will not revive merc when dead), default = 1 |
|
||||
| atk_len_arc | Attack length for hdin/sorc fighting arcane |
|
||||
| atk_len_trav | Attack length for hdin fighting trav (note this atk length will be applied in 4 different spots each) |
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 2.6 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.8 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.7 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.1 KiB |
@@ -1 +0,0 @@
|
||||
comment: false
|
||||
@@ -22,6 +22,7 @@ mana_globe=117,120,20,121,255,255
|
||||
blue_slot=102,194,18,138,230,54
|
||||
green_slot=33,181,18,87,258,69
|
||||
red_slot=161,204,28,197,240,64
|
||||
tab_text=0,0,125,180,255,255
|
||||
|
||||
[ui_pos]
|
||||
screen_width=1280
|
||||
@@ -130,6 +131,9 @@ right_panel_header=830,0,450,54
|
||||
npc_dialogue=458,4,21,140
|
||||
bind_skill=516,619,251,29
|
||||
quest_skill_btn=284,455,710,121
|
||||
left_inventory_tabs=31,62,385,28
|
||||
tab_indicator=31,82,385,14
|
||||
deposit_btn=454,365,186,43
|
||||
|
||||
[path]
|
||||
; static pathes in format: x0,y0, x1,y1, x2,y2, ...
|
||||
|
||||
+2
-1
@@ -38,6 +38,7 @@ casting_frames=10
|
||||
show_items=alt
|
||||
inventory_screen=i
|
||||
tp=f9
|
||||
;Note: loot columns are counted from left to right. Items on the left side of inventory will be considered lootable.
|
||||
num_loot_columns=5
|
||||
force_move=e
|
||||
;Note: stand_still can not be the default "shift" as it would interfere with merc healing
|
||||
@@ -57,7 +58,7 @@ battle_command=f8
|
||||
; ==== Optional configs ====
|
||||
; ==========================
|
||||
stash_gold=1
|
||||
gold_trav_only=0
|
||||
min_gold_to_pick=1000
|
||||
use_merc=1
|
||||
; Attack length for barbarians should be as high as 8-10 and even 10-12 for trav/shenk
|
||||
atk_len_arc=2.5
|
||||
|
||||
+25
-19
@@ -8,9 +8,9 @@ import cv2
|
||||
from copy import copy
|
||||
from typing import Union
|
||||
from collections import OrderedDict
|
||||
from health_manager import set_pause_state
|
||||
from transmute import Transmute
|
||||
|
||||
from utils.misc import wait
|
||||
from game_stats import GameStats
|
||||
from logger import Logger
|
||||
from config import Config
|
||||
@@ -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, 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()
|
||||
@@ -253,17 +251,21 @@ class Bot:
|
||||
self.trigger_or_stop("maintenance")
|
||||
|
||||
def on_maintenance(self):
|
||||
# Pause health manager if not already paused
|
||||
set_pause_state(True)
|
||||
|
||||
# Dismiss skill/quest/help/stats icon if they are on screen
|
||||
if not view.dismiss_skills_icon():
|
||||
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"])
|
||||
@@ -281,7 +283,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:
|
||||
@@ -323,7 +325,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)
|
||||
@@ -340,10 +341,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:
|
||||
@@ -360,11 +360,9 @@ 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)
|
||||
if not match.valid and Config().char["use_merc"]:
|
||||
if not is_visible(ScreenObjects.MercIcon) and Config().char["use_merc"]:
|
||||
Logger.info("Resurrect merc")
|
||||
self._game_stats.log_merc_death()
|
||||
self._curr_loc = self._town_manager.resurrect(self._curr_loc)
|
||||
@@ -399,11 +397,11 @@ class Bot:
|
||||
self._curr_loc = False
|
||||
self._pre_buffed = False
|
||||
view.save_and_exit()
|
||||
set_pause_state(True)
|
||||
self._game_stats.log_end_game(failed=failed)
|
||||
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):
|
||||
@@ -413,9 +411,11 @@ class Bot:
|
||||
if success:
|
||||
self._curr_loc = self._town_manager.wait_for_tp(self._curr_loc)
|
||||
if self._curr_loc:
|
||||
set_pause_state(True)
|
||||
return self.trigger_or_stop("maintenance")
|
||||
if not skills.has_tps():
|
||||
consumables.set_needs("tp", 20)
|
||||
set_pause_state(True)
|
||||
self.trigger_or_stop("end_game", failed=True)
|
||||
|
||||
# All the runs go here
|
||||
@@ -441,6 +441,7 @@ class Bot:
|
||||
self._game_stats.update_location("Pin" if Config().general['discord_status_condensed'] else "Pindle")
|
||||
self._curr_loc = self._pindle.approach(self._curr_loc)
|
||||
if self._curr_loc:
|
||||
set_pause_state(False)
|
||||
res = self._pindle.battle(not self._pre_buffed)
|
||||
self._ending_run_helper(res)
|
||||
|
||||
@@ -449,6 +450,7 @@ class Bot:
|
||||
self._do_runs["run_shenk"] = False
|
||||
self._curr_loc = self._shenk.approach(self._curr_loc)
|
||||
if self._curr_loc:
|
||||
set_pause_state(False)
|
||||
res = self._shenk.battle(Config().routes["run_shenk"], not self._pre_buffed, self._game_stats)
|
||||
self._ending_run_helper(res)
|
||||
|
||||
@@ -458,6 +460,7 @@ class Bot:
|
||||
self._game_stats.update_location("Trav" if Config().general['discord_status_condensed'] else "Travincal")
|
||||
self._curr_loc = self._trav.approach(self._curr_loc)
|
||||
if self._curr_loc:
|
||||
set_pause_state(False)
|
||||
res = self._trav.battle(not self._pre_buffed)
|
||||
self._ending_run_helper(res)
|
||||
|
||||
@@ -467,6 +470,7 @@ class Bot:
|
||||
self._game_stats.update_location("Nihl" if Config().general['discord_status_condensed'] else "Nihlathak")
|
||||
self._curr_loc = self._nihlathak.approach(self._curr_loc)
|
||||
if self._curr_loc:
|
||||
set_pause_state(False)
|
||||
res = self._nihlathak.battle(not self._pre_buffed)
|
||||
self._ending_run_helper(res)
|
||||
|
||||
@@ -476,6 +480,7 @@ class Bot:
|
||||
self._game_stats.update_location("Arc" if Config().general['discord_status_condensed'] else "Arcane")
|
||||
self._curr_loc = self._arcane.approach(self._curr_loc)
|
||||
if self._curr_loc:
|
||||
set_pause_state(False)
|
||||
res = self._arcane.battle(not self._pre_buffed)
|
||||
self._ending_run_helper(res)
|
||||
|
||||
@@ -485,5 +490,6 @@ class Bot:
|
||||
self._game_stats.update_location("Dia" if Config().general['discord_status_condensed'] else "Diablo")
|
||||
self._curr_loc = self._diablo.approach(self._curr_loc)
|
||||
if self._curr_loc:
|
||||
set_pause_state(False)
|
||||
res = self._diablo.battle(not self._pre_buffed)
|
||||
self._ending_run_helper(res)
|
||||
|
||||
@@ -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
@@ -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])
|
||||
|
||||
@@ -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
@@ -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:
|
||||
|
||||
+7
-10
@@ -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 is_visible, 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,17 +77,16 @@ 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
|
||||
"""
|
||||
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:
|
||||
if is_visible(ScreenObjects.WaypointLabel):
|
||||
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)")
|
||||
@@ -203,16 +202,14 @@ class IChar:
|
||||
mouse.click(button="right")
|
||||
consumables.increment_need("tp", 1)
|
||||
wait(0.8, 1.3) # takes quite a while for tp to be visible
|
||||
template_match = detect_screen_object(ScreenObjects.TownPortal)
|
||||
if template_match.valid:
|
||||
if (template_match := detect_screen_object(ScreenObjects.TownPortal)).valid:
|
||||
pos = template_match.center
|
||||
pos = (pos[0], pos[1] + 30)
|
||||
# Note: Template is top of portal, thus move the y-position a bit to the bottom
|
||||
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)
|
||||
if match.valid:
|
||||
if wait_until_visible(ScreenObjects.Loading, 2).valid:
|
||||
return True
|
||||
# move mouse away to not overlay with the town portal if mouse is in center
|
||||
pos_screen = convert_monitor_to_screen(mouse.get_position())
|
||||
|
||||
+15
-19
@@ -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,11 +678,8 @@ 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)
|
||||
if match.valid:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
return wait_until_visible(ScreenObjects.Loading, 2).valid
|
||||
|
||||
else:
|
||||
Logger.debug("cant find durance!, trying fixed loc")
|
||||
return False
|
||||
@@ -719,8 +716,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)
|
||||
if match.valid:
|
||||
if wait_until_visible(ScreenObjects.Loading, 2).valid:
|
||||
return True
|
||||
return False
|
||||
|
||||
@@ -728,7 +724,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 +741,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 +860,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')
|
||||
|
||||
|
||||
|
||||
@@ -73,26 +73,33 @@ class BlizzSorc(Sorceress):
|
||||
self.move(pos_m, force_move=True)
|
||||
self._blizzard((-50, -50), spray=10)
|
||||
self._cast_static()
|
||||
wait(0.75)
|
||||
#move down
|
||||
pos_m = convert_abs_to_monitor((0, 85))
|
||||
self.pre_move()
|
||||
self.move(pos_m, force_move=True)
|
||||
wait(0.70)
|
||||
self._blizzard((-170, -350), spray=10)
|
||||
self._cast_static()
|
||||
#move down
|
||||
wait(0.75)
|
||||
pos_m = convert_abs_to_monitor((0, 75))
|
||||
self.pre_move()
|
||||
self.move(pos_m, force_move=True)
|
||||
self._blizzard((100, -300), spray=10)
|
||||
self._cast_static()
|
||||
wait(0.75)
|
||||
pos_m = convert_abs_to_monitor((0, 55))
|
||||
self.pre_move()
|
||||
self.move(pos_m, force_move=True)
|
||||
wait(1.0)
|
||||
self._blizzard((-50, -130), spray=10)
|
||||
self._cast_static()
|
||||
wait(3.0)
|
||||
pos_m = convert_abs_to_monitor((0, -100))
|
||||
self.pre_move()
|
||||
self.move(pos_m, force_move=True)
|
||||
self._blizzard((-50, -130), spray=10)
|
||||
self._cast_static()
|
||||
wait(1.75)
|
||||
self._pather.traverse_nodes_fixed("eldritch_end", self)
|
||||
return True
|
||||
|
||||
@@ -101,7 +108,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 +137,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 +161,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 +174,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 +183,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 +215,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 +240,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:
|
||||
|
||||
@@ -57,9 +57,9 @@ class LightSorc(Sorceress):
|
||||
def kill_pindle(self) -> bool:
|
||||
pindle_pos_abs = convert_screen_to_abs(Config().path["pindle_end"][0])
|
||||
cast_pos_abs = [pindle_pos_abs[0] * 0.9, pindle_pos_abs[1] * 0.9]
|
||||
self._lightning(cast_pos_abs, spray=11)
|
||||
for _ in range(int(Config().char["atk_len_pindle"])):
|
||||
self._chain_lightning(cast_pos_abs, spray=11)
|
||||
self._lightning(cast_pos_abs, spray=11)
|
||||
wait(self._cast_duration, self._cast_duration + 0.2)
|
||||
# Move to items
|
||||
self._pather.traverse_nodes_fixed("pindle_end", self)
|
||||
@@ -68,9 +68,9 @@ class LightSorc(Sorceress):
|
||||
def kill_eldritch(self) -> bool:
|
||||
eld_pos_abs = convert_screen_to_abs(Config().path["eldritch_end"][0])
|
||||
cast_pos_abs = [eld_pos_abs[0] * 0.9, eld_pos_abs[1] * 0.9]
|
||||
self._lightning(cast_pos_abs, spray=50)
|
||||
for _ in range(int(Config().char["atk_len_eldritch"])):
|
||||
self._chain_lightning(cast_pos_abs, spray=90)
|
||||
self._lightning(cast_pos_abs, spray=50)
|
||||
# Move to items
|
||||
wait(self._cast_duration, self._cast_duration + 0.2)
|
||||
self._pather.traverse_nodes_fixed("eldritch_end", self)
|
||||
@@ -81,36 +81,37 @@ class LightSorc(Sorceress):
|
||||
if shenk_pos_abs is None:
|
||||
shenk_pos_abs = convert_screen_to_abs(Config().path["shenk_end"][0])
|
||||
cast_pos_abs = [shenk_pos_abs[0] * 0.9, shenk_pos_abs[1] * 0.9]
|
||||
for _ in range(int(Config().char["atk_len_shenk"] * 0.5)):
|
||||
self._chain_lightning(cast_pos_abs, spray=90)
|
||||
pos_m = convert_abs_to_monitor((150, 50))
|
||||
self.pre_move()
|
||||
self.move(pos_m, force_move=True)
|
||||
shenk_pos_abs = convert_screen_to_abs(Config().path["shenk_end"][0])
|
||||
cast_pos_abs = [shenk_pos_abs[0] * 0.9, shenk_pos_abs[1] * 0.9]
|
||||
for _ in range(int(Config().char["atk_len_shenk"] * 0.5)):
|
||||
self._chain_lightning(cast_pos_abs, spray=90)
|
||||
self._lightning(cast_pos_abs, spray=60)
|
||||
for _ in range(int(Config().char["atk_len_shenk"] * 0.5)):
|
||||
self._chain_lightning(cast_pos_abs, spray=90)
|
||||
pos_m = convert_abs_to_monitor((150, 50))
|
||||
self.pre_move()
|
||||
self.move(pos_m, force_move=True)
|
||||
shenk_pos_abs = convert_screen_to_abs(Config().path["shenk_end"][0])
|
||||
cast_pos_abs = [shenk_pos_abs[0] * 0.9, shenk_pos_abs[1] * 0.9]
|
||||
self._lightning(cast_pos_abs, spray=60)
|
||||
for _ in range(int(Config().char["atk_len_shenk"] * 0.5)):
|
||||
self._chain_lightning(cast_pos_abs, spray=90)
|
||||
pos_m = convert_abs_to_monitor((150, 50))
|
||||
self.pre_move()
|
||||
self.move(pos_m, force_move=True)
|
||||
shenk_pos_abs = convert_screen_to_abs(Config().path["shenk_end"][0])
|
||||
cast_pos_abs = [shenk_pos_abs[0] * 0.9, shenk_pos_abs[1] * 0.9]
|
||||
self._lightning(cast_pos_abs, spray=60)
|
||||
for _ in range(int(Config().char["atk_len_shenk"])):
|
||||
self._chain_lightning(cast_pos_abs, spray=90)
|
||||
self._lightning(cast_pos_abs, spray=60)
|
||||
self.pre_move()
|
||||
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 +127,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 +135,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 +150,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 +185,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:
|
||||
@@ -204,6 +205,7 @@ class LightSorc(Sorceress):
|
||||
|
||||
if nihlathak_pos_abs is not None:
|
||||
cast_pos_abs = np.array([nihlathak_pos_abs[0] * 0.9, nihlathak_pos_abs[1] * 0.9])
|
||||
self._lightning(cast_pos_abs, spray=60)
|
||||
self._chain_lightning(cast_pos_abs, delay, 90)
|
||||
# Do some tele "dancing" after each sequence
|
||||
if i < atk_len - 1:
|
||||
@@ -220,7 +222,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:
|
||||
@@ -229,8 +231,8 @@ class LightSorc(Sorceress):
|
||||
pos_m = convert_abs_to_monitor((-20, 20))
|
||||
mouse.move(*pos_m, randomize=80, delay_factor=[0.5, 0.7])
|
||||
for _ in range(int(Config().char["atk_len_arc"])):
|
||||
self._chain_lightning(cast_pos_abs, spray=11)
|
||||
self._lightning(cast_pos_abs, spray=11)
|
||||
self._chain_lightning(cast_pos_abs, spray=11)
|
||||
wait(self._cast_duration, self._cast_duration + 0.2)
|
||||
return True
|
||||
|
||||
|
||||
@@ -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])
|
||||
|
||||
@@ -10,7 +10,7 @@ import time
|
||||
from typing import Tuple
|
||||
from pather import Pather
|
||||
from config import Config
|
||||
from ui_manager import detect_screen_object, ScreenObjects
|
||||
from ui_manager import ScreenObjects, is_visible
|
||||
|
||||
class Sorceress(IChar):
|
||||
def __init__(self, skill_hotkeys: dict, pather: Pather):
|
||||
@@ -38,20 +38,19 @@ 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:
|
||||
if is_visible(ScreenObjects.WaypointLabel):
|
||||
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 +64,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
@@ -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
@@ -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:
|
||||
|
||||
+5
-2
@@ -180,7 +180,10 @@ class Config:
|
||||
self._shop_config.read('config/shop.ini')
|
||||
self._custom = configparser.ConfigParser()
|
||||
if os.environ.get('RUN_ENV') != "test" and os.path.exists('config/custom.ini'):
|
||||
self._custom.read('config/custom.ini')
|
||||
try:
|
||||
self._custom.read('config/custom.ini')
|
||||
except configparser.MissingSectionHeaderError:
|
||||
Logger.error("custom.ini missing section header, defaulting to params.ini")
|
||||
|
||||
self.general = {
|
||||
"saved_games_folder": self._select_val("general", "saved_games_folder"),
|
||||
@@ -238,7 +241,7 @@ class Config:
|
||||
"belt_hp_columns": int(self._select_val("char", "belt_hp_columns")),
|
||||
"belt_mp_columns": int(self._select_val("char", "belt_mp_columns")),
|
||||
"stash_gold": bool(int(self._select_val("char", "stash_gold"))),
|
||||
"gold_trav_only": bool(int(self._select_val("char", "gold_trav_only"))),
|
||||
"min_gold_to_pick": int(_default_iff(self._select_val("char", "min_gold_to_pick"), '', 0)),
|
||||
"use_merc": bool(int(self._select_val("char", "use_merc"))),
|
||||
"id_items": bool(int(self._select_val("char", "id_items"))),
|
||||
"open_chests": bool(int(self._select_val("char", "open_chests"))),
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
from utils.misc import color_filter, cut_roi, wait
|
||||
from template_finder import TemplateFinder
|
||||
from screen import convert_screen_to_monitor, grab
|
||||
from utils.misc import wait
|
||||
from screen import grab
|
||||
from config import Config
|
||||
from utils.custom_mouse import mouse
|
||||
import keyboard
|
||||
import cv2
|
||||
from logger import Logger
|
||||
import time
|
||||
from ui import view
|
||||
from ui_manager import ScreenObjects, detect_screen_object
|
||||
from ui_manager import ScreenObjects, is_visible
|
||||
|
||||
|
||||
class DeathManager:
|
||||
@@ -36,8 +34,7 @@ class DeathManager:
|
||||
|
||||
def handle_death_screen(self):
|
||||
img = grab()
|
||||
template_match = detect_screen_object(ScreenObjects.YouHaveDied, img)
|
||||
if template_match.valid:
|
||||
if is_visible(ScreenObjects.YouHaveDied, img):
|
||||
Logger.warning("You have died!")
|
||||
if Config().general["info_screenshots"]:
|
||||
self._last_death_screenshot = "./info_screenshots/info_debug_death_" + time.strftime("%Y%m%d_%H%M%S") + ".png"
|
||||
@@ -55,7 +52,7 @@ class DeathManager:
|
||||
wait(0.1, 0.2)
|
||||
mouse.release(button="left")
|
||||
time.sleep(1)
|
||||
if detect_screen_object(ScreenObjects.MainMenu).valid:
|
||||
if is_visible(ScreenObjects.MainMenu):
|
||||
# in this case chicken executed and left the game, but we were still dead.
|
||||
return True
|
||||
keyboard.send("esc")
|
||||
|
||||
@@ -42,7 +42,6 @@ class GameController:
|
||||
do_restart = False
|
||||
messenger = Messenger()
|
||||
while 1:
|
||||
self.health_manager.update_location(self.bot.get_curr_location())
|
||||
max_game_length_reached = self.game_stats.get_current_game_length() > Config().general["max_game_length_s"]
|
||||
max_consecutive_fails_reached = False if not Config().general["max_consecutive_fails"] else self.game_stats.get_consecutive_runs_failed() >= Config().general["max_consecutive_fails"]
|
||||
if max_game_length_reached or max_consecutive_fails_reached or self.death_manager.died() or self.health_manager.did_chicken():
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
from screen import grab
|
||||
from config import Config
|
||||
from death_manager import DeathManager
|
||||
import time
|
||||
import keyboard
|
||||
from ui_manager import detect_screen_object, ScreenObjects
|
||||
from ui_manager import ScreenObjects, is_visible
|
||||
from ui import view, loading
|
||||
from utils.misc import set_d2r_always_on_top
|
||||
|
||||
@@ -23,22 +22,22 @@ class GameRecovery:
|
||||
# make sure we are not on loading screen
|
||||
is_loading = loading.check_for_black_screen()
|
||||
while is_loading:
|
||||
is_loading = detect_screen_object(ScreenObjects.Loading).valid
|
||||
is_loading = is_visible(ScreenObjects.Loading)
|
||||
is_loading |= loading.check_for_black_screen()
|
||||
time.sleep(0.5)
|
||||
# lets just see if you might already be at hero selection
|
||||
if detect_screen_object(ScreenObjects.MainMenu).valid:
|
||||
if is_visible(ScreenObjects.MainMenu):
|
||||
return True
|
||||
# would have been too easy, maybe we have died?
|
||||
if self._death_manager.handle_death_screen():
|
||||
time.sleep(1)
|
||||
continue
|
||||
# check for save/exit button
|
||||
if detect_screen_object(ScreenObjects.SaveAndExit).valid:
|
||||
if is_visible(ScreenObjects.SaveAndExit):
|
||||
view.save_and_exit()
|
||||
continue
|
||||
# maybe we are in-game in stash/inventory, press escape
|
||||
elif detect_screen_object(ScreenObjects.InGame).valid:
|
||||
elif is_visible(ScreenObjects.InGame):
|
||||
keyboard.send("esc")
|
||||
time.sleep(1)
|
||||
return False
|
||||
|
||||
+33
-21
@@ -9,8 +9,23 @@ from logger import Logger
|
||||
from screen import grab
|
||||
import time
|
||||
from config import Config
|
||||
from inventory import common
|
||||
from ui import view, meters
|
||||
from ui_manager import detect_screen_object, ScreenObjects
|
||||
from ui_manager import ScreenObjects, is_visible
|
||||
|
||||
pause_state = True
|
||||
|
||||
def get_pause_state():
|
||||
global pause_state
|
||||
return pause_state
|
||||
|
||||
def set_pause_state(state: bool):
|
||||
global pause_state
|
||||
prev = get_pause_state()
|
||||
if prev != state:
|
||||
debug_str = "pausing" if state else "active"
|
||||
Logger.info(f"Health Manager is now {debug_str}")
|
||||
pause_state = state
|
||||
|
||||
class HealthManager:
|
||||
def __init__(self):
|
||||
@@ -21,8 +36,8 @@ class HealthManager:
|
||||
self._last_mana = time.time()
|
||||
self._last_merc_heal = time.time()
|
||||
self._callback = None
|
||||
self._pausing = True
|
||||
self._last_chicken_screenshot = None
|
||||
self._count_panel_detects = 0
|
||||
|
||||
def stop_monitor(self):
|
||||
self._do_monitor = False
|
||||
@@ -35,16 +50,7 @@ class HealthManager:
|
||||
|
||||
def reset_chicken_flag(self):
|
||||
self._did_chicken = False
|
||||
self._pausing = True
|
||||
|
||||
def update_location(self, loc: Location):
|
||||
if loc is not None and type(loc) == str:
|
||||
bosses = ["shenk", "eldritch", "pindle", "nihlathak", "trav", "arc", "diablo"]
|
||||
prev_value = self._pausing
|
||||
self._pausing = not any(substring in loc for substring in bosses)
|
||||
if self._pausing != prev_value:
|
||||
debug_str = "pausing" if self._pausing else "active"
|
||||
Logger.info(f"Health Manager is now {debug_str}")
|
||||
set_pause_state(True)
|
||||
|
||||
def _do_chicken(self, img):
|
||||
if self._callback is not None:
|
||||
@@ -64,7 +70,7 @@ class HealthManager:
|
||||
time.sleep(0.01)
|
||||
view.save_and_exit()
|
||||
self._did_chicken = True
|
||||
self._pausing = True
|
||||
set_pause_state(True)
|
||||
|
||||
def start_monitor(self):
|
||||
Logger.info("Start health monitoring")
|
||||
@@ -74,9 +80,9 @@ class HealthManager:
|
||||
while self._do_monitor:
|
||||
time.sleep(0.1)
|
||||
# Wait until the flag is reset by main.py
|
||||
if self._did_chicken or self._pausing: continue
|
||||
if self._did_chicken or get_pause_state(): continue
|
||||
img = grab()
|
||||
if detect_screen_object(ScreenObjects.InGame, img).valid:
|
||||
if is_visible(ScreenObjects.InGame, img):
|
||||
health_percentage = meters.get_health(img)
|
||||
mana_percentage = meters.get_mana(img)
|
||||
# check rejuv
|
||||
@@ -103,7 +109,7 @@ class HealthManager:
|
||||
belt.drink_potion("mana", stats=[health_percentage, mana_percentage])
|
||||
self._last_mana = time.time()
|
||||
# check merc
|
||||
if detect_screen_object(ScreenObjects.MercIcon, img).valid:
|
||||
if is_visible(ScreenObjects.MercIcon, img):
|
||||
merc_health_percentage = meters.get_merc_health(img)
|
||||
last_drink = time.time() - self._last_merc_heal
|
||||
if merc_health_percentage < Config().char["merc_chicken"]:
|
||||
@@ -115,20 +121,26 @@ class HealthManager:
|
||||
elif merc_health_percentage < Config().char["heal_merc"] and last_drink > 7.0:
|
||||
belt.drink_potion("health", merc=True, stats=[merc_health_percentage])
|
||||
self._last_merc_heal = time.time()
|
||||
if detect_screen_object(ScreenObjects.QuestSkillBtn, img).valid:
|
||||
Logger.warning(f"Skill, stats, or quest button appeared--quit to avoid accidental allocations.")
|
||||
self._do_chicken(img)
|
||||
if is_visible(ScreenObjects.LeftPanel, img) or is_visible(ScreenObjects.RightPanel, img):
|
||||
Logger.warning(f"Found an open inventory / quest / skill / stats page. Close it.")
|
||||
self._count_panel_detects += 1
|
||||
if self._count_panel_detects >= 2:
|
||||
self._count_panel_detects = 0
|
||||
Logger.warning(f"Found an open inventory / quest / skill / stats page again. Chicken to dismiss.")
|
||||
self._do_chicken(img)
|
||||
common.close()
|
||||
Logger.debug("Stop health monitoring")
|
||||
|
||||
|
||||
# Testing: Start dying or lossing mana and see if it works
|
||||
# Testing: Start dying or losing mana and see if it works
|
||||
if __name__ == "__main__":
|
||||
import threading
|
||||
import keyboard
|
||||
import os
|
||||
from health_manager import set_pause_state
|
||||
keyboard.add_hotkey('f12', lambda: Logger.info('Exit Health Manager') or os._exit(1))
|
||||
manager = HealthManager()
|
||||
manager._pausing = False
|
||||
set_pause_state(True)
|
||||
Logger.info("Press f12 to exit health manager")
|
||||
health_monitor_thread = threading.Thread(target=manager.start_monitor)
|
||||
health_monitor_thread.start()
|
||||
|
||||
+25
-14
@@ -3,14 +3,33 @@ from logger import Logger
|
||||
from typing import List
|
||||
import numpy as np
|
||||
from template_finder import TemplateFinder
|
||||
from inventory import common, consumables
|
||||
from inventory.consumables import item_consumables_map
|
||||
from inventory import common, consumables, personal
|
||||
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).valid:
|
||||
success = view.return_to_play()
|
||||
if not success:
|
||||
return None
|
||||
return img
|
||||
|
||||
def _potion_type(img: np.ndarray) -> str:
|
||||
"""
|
||||
@@ -80,10 +99,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 +129,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 +154,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)
|
||||
|
||||
+108
-28
@@ -5,15 +5,15 @@ 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, is_visible, wait_until_hidden
|
||||
from utils.misc import wait, trim_black, color_filter, cut_roi
|
||||
from inventory import consumables, personal
|
||||
from ui import view
|
||||
from screen import grab
|
||||
from screen import convert_screen_to_monitor, grab
|
||||
from dataclasses import dataclass
|
||||
from logger import Logger
|
||||
from ocr import Ocr
|
||||
|
||||
from template_finder import TemplateMatch
|
||||
|
||||
@dataclass
|
||||
class BoxInfo:
|
||||
@@ -65,10 +65,9 @@ def slot_has_item(slot_img: np.ndarray) -> bool:
|
||||
|
||||
def close(img: np.ndarray = None) -> np.ndarray:
|
||||
img = grab() if img is None else img
|
||||
if detect_screen_object(ScreenObjects.RightPanel, img).valid:
|
||||
if is_visible(ScreenObjects.RightPanel, img) or is_visible(ScreenObjects.LeftPanel, img):
|
||||
keyboard.send("esc")
|
||||
wait(0.1, 0.2)
|
||||
if detect_screen_object(ScreenObjects.RightPanel).valid:
|
||||
if not wait_until_hidden(ScreenObjects.RightPanel, 1) and not wait_until_hidden(ScreenObjects.LeftPanel, 1):
|
||||
success = view.return_to_play()
|
||||
if not success:
|
||||
return None
|
||||
@@ -113,13 +112,13 @@ 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
|
||||
img = grab()
|
||||
filtered = []
|
||||
left_panel_open = detect_screen_object(ScreenObjects.LeftPanel, img).valid
|
||||
left_panel_open = is_visible(ScreenObjects.LeftPanel, img)
|
||||
if action == "drop":
|
||||
filtered = [ item for item in items if item.keep == False and item.sell == False ]
|
||||
elif action == "sell":
|
||||
@@ -127,7 +126,7 @@ def transfer_items(items: list, action: str = "drop") -> list:
|
||||
if not left_panel_open:
|
||||
Logger.error(f"transfer_items: Can't perform, vendor is not open")
|
||||
elif action == "stash":
|
||||
if detect_screen_object(ScreenObjects.GoldBtnStash, img).valid:
|
||||
if is_visible(ScreenObjects.GoldBtnStash, img):
|
||||
filtered = [ item for item in items if item.keep == True ]
|
||||
else:
|
||||
Logger.error(f"transfer_items: Can't perform, stash is not open")
|
||||
@@ -143,16 +142,15 @@ def transfer_items(items: list, action: str = "drop") -> list:
|
||||
keyboard.send('ctrl', do_release=False)
|
||||
wait(0.2, 0.4)
|
||||
for item in filtered:
|
||||
attempts = 0
|
||||
attempts = 1
|
||||
prev_gold_img = cut_roi(grab(), roi=Config().ui_roi["inventory_gold_digits"])
|
||||
while attempts < 2:
|
||||
while attempts <= 2:
|
||||
# move to item position and left click
|
||||
mouse.move(*item.pos, randomize=4, delay_factor=[0.2, 0.4])
|
||||
wait(0.2, 0.4)
|
||||
mouse.press(button="left")
|
||||
wait(0.2, 0.4)
|
||||
mouse.release(button="left")
|
||||
wait(0.2, 0.4)
|
||||
# if dropping, drag item to middle if vendor/stash is open
|
||||
if action == "drop" and left_panel_open:
|
||||
center_mouse()
|
||||
@@ -160,28 +158,33 @@ def transfer_items(items: list, action: str = "drop") -> list:
|
||||
mouse.press(button="left")
|
||||
wait(0.2, 0.3)
|
||||
mouse.release(button="left")
|
||||
wait(0.8, 1)
|
||||
# check if item is still there
|
||||
img=grab()
|
||||
slot_img = get_slot_pos_and_img(img, item.column, item.row)[1]
|
||||
if not slot_has_item(slot_img):
|
||||
item_remains = True
|
||||
start = time.time()
|
||||
while item_remains and (time.time() - start) < 3:
|
||||
slot_img = get_slot_pos_and_img(grab(), item.column, item.row)[1]
|
||||
item_remains = slot_has_item(slot_img)
|
||||
if not item_remains:
|
||||
# item successfully transferred, delete from list
|
||||
for cnt, o_item in enumerate(items):
|
||||
if o_item.pos == item.pos:
|
||||
items.pop(cnt)
|
||||
break
|
||||
# check and see if inventory gold count changed
|
||||
new_gold_img = cut_roi(img, roi=Config().ui_roi["inventory_gold_digits"])
|
||||
if prev_gold_img.shape == new_gold_img.shape and not(np.bitwise_xor(prev_gold_img, new_gold_img).any()):
|
||||
Logger.info("Inventory gold is full, force stash")
|
||||
personal.set_inventory_gold_full(True)
|
||||
else:
|
||||
personal.set_inventory_gold_full(False)
|
||||
if action == "sell":
|
||||
# check and see if inventory gold count changed
|
||||
start = time.time()
|
||||
gold_unchanged = True
|
||||
while gold_unchanged and (time.time() - start) < 2:
|
||||
new_gold_img = cut_roi(grab(), roi=Config().ui_roi["inventory_gold_digits"])
|
||||
gold_unchanged = prev_gold_img.shape == new_gold_img.shape and not(np.bitwise_xor(prev_gold_img, new_gold_img).any())
|
||||
if gold_unchanged:
|
||||
Logger.info("Inventory gold is full, force stash")
|
||||
personal.set_inventory_gold_full(gold_unchanged)
|
||||
break
|
||||
else:
|
||||
# item is still there, try again
|
||||
attempts += 1
|
||||
if attempts > 1:
|
||||
if attempts > 2:
|
||||
Logger.error(f"transfer_items: could not stash in position {item.pos}")
|
||||
if (action == "drop" and not left_panel_open) or action in ["sell", "stash"]:
|
||||
keyboard.send('ctrl', do_press=False)
|
||||
@@ -215,15 +218,92 @@ def read_gold(img: np.ndarray = None, type: str = "inventory"):
|
||||
Logger.debug(f"{type.upper()} gold: {number}")
|
||||
return number
|
||||
|
||||
def wait_for_left_inventory(timeout: int = 5):
|
||||
start=time.time()
|
||||
while time.time() - start < timeout:
|
||||
if left_inventory_ready(grab()):
|
||||
Logger.debug("Vendor/stash inventory ready")
|
||||
return True
|
||||
wait(0.1)
|
||||
Logger.error("wait_for_left_inventory: Vendor/stash inventory not detected")
|
||||
return False
|
||||
|
||||
def left_inventory_ready(img = np.ndarray):
|
||||
# on laggy PC's or online the vendor may take longer to have all of its inventory ready
|
||||
if is_visible(ScreenObjects.LeftPanel, img):
|
||||
# check for tab text
|
||||
text, _ = color_filter(img, Config().colors["tab_text"])
|
||||
text = cut_roi(text, Config().ui_roi["left_inventory_tabs"])
|
||||
# check for red slots in inventory space
|
||||
red, _ = color_filter(img, Config().colors["red_slot"])
|
||||
red = cut_roi(red, Config().ui_roi["left_inventory"])
|
||||
# check for blue slots in inventory space
|
||||
blue, _ = color_filter(img, Config().colors["blue_slot"])
|
||||
blue = cut_roi(blue, Config().ui_roi["left_inventory"])
|
||||
# if none of the above are true, then inventory is empty and there are no tabs (not loaded yet)
|
||||
return any(np.sum(i) > 0 for i in [text, red, blue])
|
||||
return False
|
||||
|
||||
def tab_properties(idx: int = 0) -> dict[int, int, tuple]:
|
||||
tab_width = round(int(Config().ui_roi["tab_indicator"][2]) / 4)
|
||||
x_start = int(Config().ui_roi["tab_indicator"][0])
|
||||
left = idx * tab_width + x_start
|
||||
right = (idx + 1) * tab_width + x_start
|
||||
x_center = (left + right) / 2
|
||||
y_center = int(Config().ui_roi["tab_indicator"][1]) - 5
|
||||
return {
|
||||
"left": round(left),
|
||||
"right": round(right),
|
||||
"center": (x_center, y_center)
|
||||
}
|
||||
|
||||
def indicator_location_to_tab_count(pos: tuple) -> int:
|
||||
for i in range(3):
|
||||
tab = tab_properties(i)
|
||||
if tab["left"] <= pos[0] < tab["right"]:
|
||||
return i
|
||||
|
||||
def get_active_tab(indicator: TemplateMatch = None) -> int:
|
||||
indicator = detect_screen_object(ScreenObjects.TabIndicator) if indicator is None else indicator
|
||||
if indicator.valid:
|
||||
return indicator_location_to_tab_count(indicator.center)
|
||||
else:
|
||||
Logger.error("common/get_active_tab(): Error finding tab indicator")
|
||||
return False
|
||||
|
||||
def select_tab(idx: int):
|
||||
# stash or vendor must be open
|
||||
# indices start from 0
|
||||
if not get_active_tab() == idx:
|
||||
tab = tab_properties(idx)
|
||||
pos = convert_screen_to_monitor(tab["center"])
|
||||
mouse.move(*pos)
|
||||
wait(0.2, 0.3)
|
||||
mouse.click("left")
|
||||
wait(0.2, 0.3)
|
||||
|
||||
if __name__ == "__main__":
|
||||
import os
|
||||
import keyboard
|
||||
from config import Config
|
||||
from screen import start_detecting_window, stop_detecting_window
|
||||
from utils.misc import color_filter
|
||||
start_detecting_window()
|
||||
keyboard.add_hotkey('f12', lambda: os._exit(1))
|
||||
keyboard.add_hotkey('f12', lambda: Logger.info('Force Exit (f12)') or stop_detecting_window() or os._exit(1))
|
||||
print("Move to d2r window and press f11")
|
||||
keyboard.wait("f11")
|
||||
|
||||
img = grab()
|
||||
print(read_gold(img, "inventory"))
|
||||
stop_detecting_window()
|
||||
#select_tab(0)
|
||||
|
||||
color = Config().colors["tab_text"]
|
||||
while 1:
|
||||
# img = cv2.imread("")
|
||||
img = grab()
|
||||
|
||||
a = cut_roi(img, Config().ui_roi["deposit_ok"])
|
||||
b, _ = color_filter(img, Config().colors["tab_text"])
|
||||
|
||||
print(np.sum(b))
|
||||
|
||||
cv2.imshow('test', b)
|
||||
key = cv2.waitKey(1)
|
||||
@@ -1,15 +1,13 @@
|
||||
from config import Config
|
||||
import cv2
|
||||
import numpy as np
|
||||
import keyboard
|
||||
import time
|
||||
import parse
|
||||
from utils.custom_mouse import mouse
|
||||
from template_finder import TemplateFinder
|
||||
from ui_manager import detect_screen_object, ScreenObjects
|
||||
from inventory import personal, common
|
||||
from inventory import personal
|
||||
from utils.misc import wait
|
||||
from screen import grab, convert_screen_to_monitor
|
||||
from screen import grab
|
||||
from dataclasses import dataclass
|
||||
from logger import Logger
|
||||
from item import ItemCropper
|
||||
@@ -136,11 +134,14 @@ def update_tome_key_needs(img: np.ndarray = None, item_type: str = "tp") -> bool
|
||||
# get the item description box
|
||||
item_box = ItemCropper().crop_item_descr(hovered_item, model="engd2r_inv_th_fast")
|
||||
if item_box.valid:
|
||||
result = parse.search("Quantity: {:d}", item_box.ocr_result.text).fixed[0]
|
||||
if item_type.lower() in ["tp", "id"]:
|
||||
set_needs(item_type, 20 - result)
|
||||
if item_type.lower() == "key":
|
||||
set_needs(item_type, 12 - result)
|
||||
try:
|
||||
result = parse.search("Quantity: {:d}", item_box.ocr_result.text).fixed[0]
|
||||
if item_type.lower() in ["tp", "id"]:
|
||||
set_needs(item_type, 20 - result)
|
||||
if item_type.lower() == "key":
|
||||
set_needs(item_type, 12 - result)
|
||||
except Exception as e:
|
||||
Logger.error(f"update_tome_key_needs: unable to parse quantity for {item_type}. Exception: {e}")
|
||||
else:
|
||||
Logger.error(f"update_tome_key_needs: Failed to capture item description box for {item_type}")
|
||||
if Config().general["info_screenshots"]:
|
||||
|
||||
+51
-52
@@ -3,7 +3,7 @@ from game_stats import GameStats
|
||||
from item import ItemFinder, Item
|
||||
from item.item_cropper import ItemText
|
||||
from logger import Logger
|
||||
from screen import convert_abs_to_monitor, grab, convert_screen_to_monitor
|
||||
from screen import grab, convert_screen_to_monitor
|
||||
import keyboard
|
||||
import cv2
|
||||
import time
|
||||
@@ -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, is_visible, select_screen_object_match, wait_until_visible, ScreenObjects, center_mouse
|
||||
from item import ItemCropper
|
||||
from messages import Messenger
|
||||
|
||||
@@ -58,68 +58,64 @@ def stash_all_items(items: list = None):
|
||||
global messenger
|
||||
if items is None:
|
||||
Logger.debug("No items to stash, skip")
|
||||
return False
|
||||
return 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:
|
||||
Logger.error("Could not determine to be in stash menu. Continue...")
|
||||
return
|
||||
Logger.debug("Found inventory gold btn")
|
||||
# Wait for stash to fully load
|
||||
if not common.wait_for_left_inventory():
|
||||
Logger.error("stash_all_items(): Could not determine to be in stash menu. Continue...")
|
||||
return items
|
||||
# stash gold
|
||||
if Config().char["stash_gold"]:
|
||||
img = grab()
|
||||
if detect_screen_object(ScreenObjects.GoldNone, img).valid:
|
||||
Logger.debug("No gold to stash")
|
||||
else:
|
||||
if not is_visible(ScreenObjects.GoldNone):
|
||||
Logger.debug("Stashing gold")
|
||||
stash.move_to_stash_tab(min(3, stash.curr_stash["gold"]))
|
||||
try:
|
||||
# Try to read gold count with OCR
|
||||
stash_full_of_gold = common.read_gold(img, "stash") == 2500000
|
||||
except:
|
||||
stash_full_of_gold = False
|
||||
common.select_tab(min(3, stash.get_curr_stash()["gold"]))
|
||||
wait(0.7, 1)
|
||||
stash_full_of_gold = False
|
||||
# Try to read gold count with OCR
|
||||
try: stash_full_of_gold = common.read_gold(grab(), "stash") == 2500000
|
||||
except: pass
|
||||
if not stash_full_of_gold:
|
||||
# If gold read by OCR fails, fallback to old method
|
||||
mouse.move(*gold_btn.center, randomize=4)
|
||||
wait(0.1, 0.15)
|
||||
mouse.press(button="left")
|
||||
wait(0.25, 0.35)
|
||||
mouse.release(button="left")
|
||||
wait(0.4, 0.6)
|
||||
keyboard.send("enter") #if stash already full of gold just nothing happens -> gold stays on char -> no popup window
|
||||
wait(1.3, 1.6)
|
||||
gold_btn = detect_screen_object(ScreenObjects.GoldBtnInventory)
|
||||
select_screen_object_match(gold_btn)
|
||||
if wait_until_visible(ScreenObjects.DepositBtn, 3).valid:
|
||||
keyboard.send("enter") #if stash already full of gold just nothing happens -> gold stays on char -> no popup window
|
||||
else:
|
||||
Logger.error("stash_all_items(): deposit button not detected, failed to stash gold")
|
||||
# move cursor away from button to interfere with screen grab
|
||||
mouse.move(-120, 0, absolute=False, randomize=15)
|
||||
stash_full_of_gold = not detect_screen_object(ScreenObjects.GoldNone).valid
|
||||
mouse.move(-120, 0, absolute=False, randomize=15, delay_factor=[0.3, 0.5])
|
||||
# if 0 gold becomes visible in personal inventory then the stash tab still has room for gold
|
||||
stash_full_of_gold = not wait_until_visible(ScreenObjects.GoldNone, 1.5).valid
|
||||
if stash_full_of_gold:
|
||||
Logger.info("Stash tab is full of gold, selecting next stash tab.")
|
||||
stash.curr_stash["gold"] += 1
|
||||
Logger.debug("Stash tab is full of gold, selecting next stash tab.")
|
||||
stash.set_curr_stash(gold = (stash.get_curr_stash()["gold"] + 1))
|
||||
if Config().general["info_screenshots"]:
|
||||
cv2.imwrite("./info_screenshots/info_gold_stash_full_" + time.strftime("%Y%m%d_%H%M%S") + ".png", grab())
|
||||
if stash.curr_stash["gold"] > 3:
|
||||
if stash.get_curr_stash()["gold"] > 3:
|
||||
#decide if gold pickup should be disabled or gambling is active
|
||||
vendor.set_gamble_status(True)
|
||||
else:
|
||||
# move to next stash
|
||||
wait(0.5, 0.6)
|
||||
# move to next stash tab
|
||||
return stash_all_items(items=items)
|
||||
else:
|
||||
set_inventory_gold_full(False)
|
||||
# check if stash tab is completely full (no empty slots)
|
||||
stash.move_to_stash_tab(stash.curr_stash["items"])
|
||||
while stash.curr_stash["items"] <= 3:
|
||||
common.select_tab(stash.get_curr_stash()["items"])
|
||||
while stash.get_curr_stash()["items"] <= 3:
|
||||
img = grab()
|
||||
if detect_screen_object(ScreenObjects.EmptyStashSlot, img).valid:
|
||||
if is_visible(ScreenObjects.EmptyStashSlot, img):
|
||||
break
|
||||
else:
|
||||
Logger.info(f"Stash tab completely full, advance to next")
|
||||
Logger.debug(f"Stash tab completely full, advance to next")
|
||||
if Config().general["info_screenshots"]:
|
||||
cv2.imwrite("./info_screenshots/stash_tab_completely_full_" + time.strftime("%Y%m%d_%H%M%S") + ".png", img)
|
||||
stash.curr_stash["items"] += -1 if Config().char["fill_shared_stash_first"] else 1
|
||||
if (Config().char["fill_shared_stash_first"] and stash.curr_stash["items"] < 0) or stash.curr_stash["items"] > 3:
|
||||
if Config().char["fill_shared_stash_first"]:
|
||||
stash.set_curr_stash(items = (stash.get_curr_stash()["items"] - 1))
|
||||
else:
|
||||
stash.set_curr_stash(items = (stash.get_curr_stash()["items"] + 1))
|
||||
if (Config().char["fill_shared_stash_first"] and stash.get_curr_stash()["items"] < 0) or stash.get_curr_stash()["items"] > 3:
|
||||
stash.stash_full()
|
||||
stash.move_to_stash_tab(stash.curr_stash["items"])
|
||||
common.select_tab(stash.get_curr_stash()["items"])
|
||||
# stash stuff
|
||||
while True:
|
||||
items = common.transfer_items(items, "stash")
|
||||
@@ -128,10 +124,13 @@ def stash_all_items(items: list = None):
|
||||
Logger.debug("Wanted to stash item, but it's still in inventory. Assumes full stash. Move to next.")
|
||||
if Config().general["info_screenshots"]:
|
||||
cv2.imwrite("./info_screenshots/debug_info_inventory_not_empty_" + time.strftime("%Y%m%d_%H%M%S") + ".png", grab())
|
||||
stash.curr_stash["items"] += -1 if Config().char["fill_shared_stash_first"] else 1
|
||||
if (Config().char["fill_shared_stash_first"] and stash.curr_stash["items"] < 0) or stash.curr_stash["items"] > 3:
|
||||
if Config().char["fill_shared_stash_first"]:
|
||||
stash.set_curr_stash(items = (stash.get_curr_stash()["items"] - 1))
|
||||
else:
|
||||
stash.set_curr_stash(items = (stash.get_curr_stash()["items"] + 1))
|
||||
if (Config().char["fill_shared_stash_first"] and stash.get_curr_stash()["items"] < 0) or stash.get_curr_stash()["items"] > 3:
|
||||
stash.stash_full()
|
||||
stash.move_to_stash_tab(stash.curr_stash["items"])
|
||||
common.select_tab(stash.get_curr_stash()["items"])
|
||||
else:
|
||||
break
|
||||
Logger.debug("Done stashing")
|
||||
@@ -144,7 +143,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,:]
|
||||
|
||||
@@ -162,7 +160,7 @@ def keep_item(item_box: ItemText = None, found_item: Item = None, do_logging: bo
|
||||
and any(item_type in found_item.name for item_type in ["uniq", "magic", "rare", "set"])
|
||||
)
|
||||
# items that are checked to be kept should all be identified unless an error occurred or user preferred unidentified
|
||||
or detect_screen_object(ScreenObjects.Unidentified, item_box.data).valid
|
||||
or is_visible(ScreenObjects.Unidentified, item_box.data)
|
||||
):
|
||||
include_props = False
|
||||
exclude_props = False
|
||||
@@ -259,13 +257,14 @@ def specific_inventory_roi(desired: str = "reserved"):
|
||||
|
||||
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:
|
||||
if not is_visible(ScreenObjects.RightPanel, img):
|
||||
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:
|
||||
Logger.error(f"personal.open(): Failed to open inventory")
|
||||
return None
|
||||
img = grab()
|
||||
return img
|
||||
@@ -277,7 +276,7 @@ def inspect_items(inp_img: np.ndarray = None, close_window: bool = True, game_st
|
||||
"""
|
||||
center_mouse()
|
||||
img = open(inp_img)
|
||||
vendor_open = detect_screen_object(ScreenObjects.GoldBtnVendor, inp_img).valid
|
||||
vendor_open = is_visible(ScreenObjects.GoldBtnVendor, inp_img)
|
||||
slots = []
|
||||
# check which slots have items
|
||||
for column, row in itertools.product(range(Config().char["num_loot_columns"]), range(4)):
|
||||
@@ -341,7 +340,7 @@ def inspect_items(inp_img: np.ndarray = None, close_window: bool = True, game_st
|
||||
implied_no_id = not (Config().items[found_item.name].include or Config().items[found_item.name].exclude)
|
||||
implied_no_id |= not any(item_type in found_item.name for item_type in ["uniq", "magic", "rare", "set"])
|
||||
if not implied_no_id:
|
||||
if (is_unidentified := detect_screen_object(ScreenObjects.Unidentified, item_box.data).valid):
|
||||
if (is_unidentified := is_visible(ScreenObjects.Unidentified, item_box.data)):
|
||||
need_id = True
|
||||
center_mouse()
|
||||
tome_state, tome_pos = common.tome_state(grab(), tome_type = "id", roi = specific_inventory_roi("reserved"))
|
||||
@@ -394,7 +393,7 @@ def inspect_items(inp_img: np.ndarray = None, close_window: bool = True, game_st
|
||||
common.transfer_items([box], action = "sell")
|
||||
continue
|
||||
# if item is to be kept and is already ID'd or doesn't need ID, log and stash
|
||||
if keep and not need_id:
|
||||
if game_stats is not None and (keep and not need_id):
|
||||
game_stats.log_item_keep(found_item.name, Config().items[found_item.name].pickit_type == 2, item_box.data, item_box.ocr_result.text)
|
||||
# if item is to be kept or still needs to be sold or identified, append to list
|
||||
if keep or sell or need_id:
|
||||
|
||||
+10
-19
@@ -12,28 +12,19 @@ curr_stash = {
|
||||
"gold": 0
|
||||
}
|
||||
|
||||
def get_curr_stash() -> dict:
|
||||
global curr_stash
|
||||
return curr_stash
|
||||
|
||||
def set_curr_stash(items: int = None, gold: int = None):
|
||||
global curr_stash
|
||||
if items is not None: curr_stash["items"] = items
|
||||
if gold is not None: curr_stash["gold"] = gold
|
||||
|
||||
def stash_full():
|
||||
Logger.error("All stash is full, quitting")
|
||||
if Config().general["custom_message_hook"]:
|
||||
Messenger().send_stash()
|
||||
os.system("taskkill /f /im D2R.exe")
|
||||
wait(1.0, 1.5)
|
||||
os._exit(0)
|
||||
|
||||
def move_to_stash_tab(stash_idx: int):
|
||||
"""Move to a specifc tab in the stash
|
||||
:param stash_idx: idx of the stash starting at 0 (personal stash)
|
||||
"""
|
||||
str_to_idx_map = {"STASH_0_ACTIVE": 0, "STASH_1_ACTIVE": 1, "STASH_2_ACTIVE": 2, "STASH_3_ACTIVE": 3}
|
||||
template_match = TemplateFinder().search([*str_to_idx_map], grab(), threshold=0.7, best_match=True, roi=Config().ui_roi["stash_btn_roi"])
|
||||
curr_active_stash = str_to_idx_map[template_match.name] if template_match.valid else -1
|
||||
if curr_active_stash != stash_idx:
|
||||
# select the start stash
|
||||
personal_stash_pos = (Config().ui_pos["stash_personal_btn_x"], Config().ui_pos["stash_personal_btn_y"])
|
||||
stash_btn_width = Config().ui_pos["stash_btn_width"]
|
||||
next_stash_pos = (personal_stash_pos[0] + stash_btn_width * stash_idx, personal_stash_pos[1])
|
||||
x_m, y_m = convert_screen_to_monitor(next_stash_pos)
|
||||
mouse.move(x_m, y_m, randomize=[30, 7], delay_factor=[1.0, 1.5])
|
||||
wait(0.2, 0.3)
|
||||
mouse.click(button="left")
|
||||
wait(0.3, 0.4)
|
||||
os._exit(0)
|
||||
+12
-14
@@ -7,8 +7,8 @@ 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 inventory import personal, common
|
||||
from ui_manager import is_visible, select_screen_object_match, wait_until_visible, ScreenObjects
|
||||
from inventory import personal, common, stash
|
||||
|
||||
gamble_count = 0
|
||||
gamble_status = False
|
||||
@@ -39,21 +39,17 @@ 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)
|
||||
if not repair_btn.valid:
|
||||
if not (repair_btn := wait_until_visible(ScreenObjects.RepairBtn, timeout=4)).valid:
|
||||
return False
|
||||
mouse.move(*repair_btn.center, randomize=12, delay_factor=[1.0, 1.5])
|
||||
wait(0.1, 0.15)
|
||||
mouse.click(button="left")
|
||||
wait(0.1, 0.15)
|
||||
if detect_screen_object(ScreenObjects.NotEnoughGold).valid:
|
||||
select_screen_object_match(repair_btn)
|
||||
if wait_until_visible(ScreenObjects.NotEnoughGold, 1).valid:
|
||||
Logger.warning("Couldn't repair--out of gold. Continue.")
|
||||
keyboard.send("esc")
|
||||
return False
|
||||
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:
|
||||
@@ -72,7 +68,7 @@ def gamble():
|
||||
wait(0.4, 0.6)
|
||||
img=grab()
|
||||
# make sure the "not enough gold" message doesn't exist
|
||||
if detect_screen_object(ScreenObjects.NotEnoughGold, img).valid:
|
||||
if is_visible(ScreenObjects.NotEnoughGold, img):
|
||||
Logger.warning(f"Out of gold, stop gambling")
|
||||
keyboard.send("esc")
|
||||
set_gamble_status(False)
|
||||
@@ -92,8 +88,10 @@ def gamble():
|
||||
if new_count >= max_gamble_count:
|
||||
break
|
||||
Logger.debug(f"Finish gambling")
|
||||
stash.set_curr_stash(gold = 0)
|
||||
personal.set_inventory_gold_full(False)
|
||||
set_gamble_status(False)
|
||||
if get_gamble_status():
|
||||
set_gamble_status(False)
|
||||
common.close()
|
||||
return None
|
||||
else:
|
||||
@@ -118,7 +116,7 @@ def buy_item(template_name: str, quantity: int = 1, img: np.ndarray = None, shif
|
||||
wait(0.5, 0.8)
|
||||
mouse.click(button="right")
|
||||
wait(0.4, 0.6)
|
||||
if detect_screen_object(ScreenObjects.NotEnoughGold).valid:
|
||||
if is_visible(ScreenObjects.NotEnoughGold):
|
||||
Logger.warning(f"Out of gold, could not purchase {template_name}")
|
||||
keyboard.send("esc")
|
||||
return False
|
||||
@@ -129,7 +127,7 @@ def buy_item(template_name: str, quantity: int = 1, img: np.ndarray = None, shif
|
||||
for _ in range(quantity):
|
||||
mouse.click(button="right")
|
||||
wait(0.9, 1.1)
|
||||
if detect_screen_object(ScreenObjects.NotEnoughGold).valid:
|
||||
if is_visible(ScreenObjects.NotEnoughGold):
|
||||
Logger.warning(f"Out of gold, could not purchase {template_name}")
|
||||
keyboard.send("esc")
|
||||
return False
|
||||
|
||||
@@ -10,7 +10,7 @@ from config import Config
|
||||
from utils.misc import color_filter, cut_roi
|
||||
from item import ItemCropper
|
||||
from template_finder import TemplateFinder
|
||||
from ocr import OcrResult
|
||||
from ocr import OcrResult, Ocr
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -115,6 +115,11 @@ class ItemFinder:
|
||||
hist = cv2.calcHist([cropped_item], [0, 1, 2], mask, [8, 8, 8], [0, 256, 0, 256, 0, 256])
|
||||
hist_result = cv2.compareHist(template.hist, hist, cv2.HISTCMP_CORREL)
|
||||
same_type = hist_result > 0.65 and hist_result is not np.inf
|
||||
# if ocr_during_pickit is off, min_gold_to_pick is set, and matched template is gold, OCR the image
|
||||
if not Config().advanced_options['ocr_during_pickit'] \
|
||||
and Config().char['min_gold_to_pick'] and 'misc_gold' == key:
|
||||
results = Ocr().image_to_text([cluster["clean_img"]], model = "engd2r_inv_th_fast", psm = 7)
|
||||
setattr(cluster, "ocr_result", results[0])
|
||||
if same_type:
|
||||
item = Item()
|
||||
item.center = (int(max_loc[0] + x + int(template.data.shape[1] * 0.5)), int(max_loc[1] + y + int(template.data.shape[0] * 0.5)))
|
||||
|
||||
+18
-12
@@ -1,9 +1,8 @@
|
||||
import time
|
||||
from inventory import belt
|
||||
import keyboard
|
||||
import cv2
|
||||
from operator import itemgetter
|
||||
from ui_manager import ScreenObjects, detect_screen_object
|
||||
from ui_manager import ScreenObjects, is_visible
|
||||
from utils.custom_mouse import mouse
|
||||
from config import Config
|
||||
from logger import Logger
|
||||
@@ -11,6 +10,7 @@ from screen import grab, convert_abs_to_monitor, convert_screen_to_monitor
|
||||
from item import ItemFinder, Item
|
||||
from char import IChar
|
||||
from inventory import consumables
|
||||
import parse
|
||||
|
||||
|
||||
class PickIt:
|
||||
@@ -18,11 +18,10 @@ class PickIt:
|
||||
self._item_finder = item_finder
|
||||
self._last_closest_item: Item = None
|
||||
|
||||
def pick_up_items(self, char: IChar, is_at_trav: bool = False) -> bool:
|
||||
def pick_up_items(self, char: IChar) -> bool:
|
||||
"""
|
||||
Pick up all items with specified char
|
||||
:param char: The character used to pick up the item
|
||||
:param is_at_trav: Dirty hack to reduce gold pickup only to trav area, should be removed once we can determine the amount of gold reliably
|
||||
:return: Bool if any items were picked up or not. (Does not account for picking up scrolls and pots)
|
||||
"""
|
||||
found_nothing = 0
|
||||
@@ -35,7 +34,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 +42,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()
|
||||
@@ -83,9 +82,16 @@ class PickIt:
|
||||
if needs["key"] <= 0:
|
||||
item_list = [x for x in item_list if "misc_key" != x.name]
|
||||
|
||||
# TODO: Hacky solution for trav only gold pickup, hope we can soon read gold ammount and filter by that...
|
||||
if Config().char["gold_trav_only"] and not is_at_trav:
|
||||
item_list = [x for x in item_list if "misc_gold" not in x.name]
|
||||
# filter out gold less than desired quantity
|
||||
if (min_gold := Config().char['min_gold_to_pick']):
|
||||
for item in item_list[:]:
|
||||
if "misc_gold" == item.name:
|
||||
try:
|
||||
ocr_gold = int(parse.search("{:d} GOLD", item.ocr_result.text).fixed[0])
|
||||
except:
|
||||
ocr_gold = 0
|
||||
if ocr_gold < min_gold:
|
||||
item_list.remove(item)
|
||||
|
||||
if len(item_list) == 0:
|
||||
# if twice no item was found, break
|
||||
@@ -145,9 +151,9 @@ class PickIt:
|
||||
if not char.capabilities.can_teleport_natively:
|
||||
time.sleep(0.2)
|
||||
|
||||
if detect_screen_object(ScreenObjects.Overburdened).valid:
|
||||
if is_visible(ScreenObjects.Overburdened):
|
||||
found_items = True
|
||||
Logger.warning("Inventory full, skipping pickit!")
|
||||
Logger.warning("Inventory full, terminating pickit!")
|
||||
# TODO: Could think about sth like: Go back to town, stash, come back picking up stuff
|
||||
break
|
||||
else:
|
||||
|
||||
+3
-1
@@ -26,14 +26,16 @@ def start_or_pause_bot(controllers: Controllers):
|
||||
else:
|
||||
# Kill any other controllers and start botty
|
||||
controllers.debugger.stop()
|
||||
controllers.game.start()
|
||||
screen.start_detecting_window()
|
||||
controllers.game.start()
|
||||
|
||||
def start_or_stop_graphic_debugger(controllers: Controllers):
|
||||
if controllers.debugger.is_running:
|
||||
controllers.debugger.stop()
|
||||
screen.stop_detecting_window()
|
||||
else:
|
||||
# Kill any other controller and start debugger
|
||||
screen.start_detecting_window()
|
||||
controllers.game.stop()
|
||||
controllers.debugger.start()
|
||||
|
||||
|
||||
+4
-3
@@ -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 ScreenObjects, center_mouse, is_visible, wait_until_hidden
|
||||
from utils.misc import color_filter, wait
|
||||
from logger import Logger
|
||||
from utils.custom_mouse import mouse
|
||||
@@ -220,9 +220,10 @@ npcs = {
|
||||
|
||||
|
||||
def escape_dialogue(img) -> np.ndarray:
|
||||
while detect_screen_object(ScreenObjects.NPCDialogue, img).valid:
|
||||
while is_visible(ScreenObjects.NPCDialogue, img):
|
||||
keyboard.send("esc")
|
||||
wait(0.2)
|
||||
if wait_until_hidden(ScreenObjects.NPCDialogue, 0.5):
|
||||
break
|
||||
img = grab()
|
||||
return img
|
||||
|
||||
|
||||
+9
-17
@@ -6,7 +6,6 @@ import random
|
||||
from typing import Tuple, Union, List
|
||||
import cv2
|
||||
import numpy as np
|
||||
from item.pickit import PickIt
|
||||
from utils.custom_mouse import mouse
|
||||
from utils.misc import wait # for stash/shrine tele cancel detection in traverse node
|
||||
from utils.misc import is_in_roi
|
||||
@@ -15,7 +14,7 @@ from logger import Logger
|
||||
from screen import convert_screen_to_monitor, convert_abs_to_screen, convert_abs_to_monitor, convert_screen_to_abs, grab, stop_detecting_window
|
||||
from template_finder import TemplateFinder
|
||||
from char import IChar
|
||||
from ui_manager import detect_screen_object, ScreenObjects
|
||||
from ui_manager import detect_screen_object, ScreenObjects, is_visible, select_screen_object_match
|
||||
|
||||
class Location:
|
||||
# A5 Town
|
||||
@@ -594,7 +593,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 +603,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 +640,17 @@ class Pather:
|
||||
while not continue_to_next_node:
|
||||
img = grab()
|
||||
# Handle timeout
|
||||
if (time.time() - last_move) > time_out:
|
||||
if detect_screen_object(ScreenObjects.WaypointLabel).valid:
|
||||
if (time.time() - last_move) > timeout:
|
||||
if is_visible(ScreenObjects.WaypointLabel):
|
||||
# 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")
|
||||
@@ -671,18 +670,11 @@ class Pather:
|
||||
|
||||
# Sometimes we get stuck at a Shrine or Stash, after a few seconds check if the screen was different, if force a left click.
|
||||
if (teleport_count + 1) % 30 == 0:
|
||||
Logger.debug("Longer-than-expected traverse: Check for an occluding shrine")
|
||||
if detect_screen_object(ScreenObjects.ShrineArea).valid:
|
||||
if (match := detect_screen_object(ScreenObjects.ShrineArea)).valid:
|
||||
if Config().general["info_screenshots"]: cv2.imwrite(f"./info_screenshots/info_shrine_check_before" + time.strftime("%Y%m%d_%H%M%S") + ".png", grab())
|
||||
Logger.debug(f"Shrine found, activating it")
|
||||
x_m, y_m = convert_abs_to_monitor((0, -130)) #above head
|
||||
mouse.move(x_m, y_m)
|
||||
wait(0.1, 0.15)
|
||||
mouse.click(button="left")
|
||||
select_screen_object_match(match)
|
||||
if Config().general["info_screenshots"]: cv2.imwrite(f"./info_screenshots/info_shrine_check_after" + time.strftime("%Y%m%d_%H%M%S") + ".png", grab())
|
||||
# we might need a check if she moved after the sequence here was executed to confirm it was successful? Otherwise we just loop again :)
|
||||
else:
|
||||
Logger.debug("Shrine not found.")
|
||||
teleport_count = 0
|
||||
break
|
||||
teleport_count += 1
|
||||
|
||||
+10
-11
@@ -10,6 +10,7 @@ from utils.misc import wait
|
||||
from dataclasses import dataclass
|
||||
from chest import Chest
|
||||
from ui import waypoint
|
||||
from health_manager import set_pause_state
|
||||
|
||||
class Arcane:
|
||||
def __init__(
|
||||
@@ -17,17 +18,17 @@ class Arcane:
|
||||
pather: Pather,
|
||||
town_manager: TownManager,
|
||||
char: IChar,
|
||||
pickit: PickIt
|
||||
pickit: PickIt,
|
||||
):
|
||||
self._pather = pather
|
||||
self._town_manager = town_manager
|
||||
self._char = char
|
||||
self._pickit = pickit
|
||||
self._chest = Chest(self._char, 'arcane')
|
||||
self.used_tps = 0
|
||||
|
||||
def approach(self, start_loc: Location) -> Union[bool, Location]:
|
||||
Logger.info("Run Arcane")
|
||||
set_pause_state(True)
|
||||
if not self._char.capabilities.can_teleport_natively:
|
||||
raise ValueError("Arcane requires teleport")
|
||||
if not self._town_manager.open_wp(start_loc):
|
||||
@@ -41,19 +42,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]]:
|
||||
@@ -72,12 +73,11 @@ class Arcane:
|
||||
]
|
||||
|
||||
picked_up_items = False
|
||||
self.used_tps = 0
|
||||
|
||||
for i, data in enumerate(path_arr):
|
||||
set_pause_state(False)
|
||||
if do_pre_buff:
|
||||
self._char.pre_buff()
|
||||
|
||||
# calibrating at start and moving towards the end of the arm
|
||||
self._pather.traverse_nodes([data.calib_node_start], self._char, force_tp=True)
|
||||
if not self._pather.traverse_nodes_fixed(data.static_path_forward, self._char):
|
||||
@@ -94,9 +94,8 @@ class Arcane:
|
||||
# Open TP and return back to town, walk to wp and start over
|
||||
if not self._char.tp_town():
|
||||
Logger.warning("TP to town failed, cancel run")
|
||||
self.used_tps += 20
|
||||
return False
|
||||
self.used_tps += 1
|
||||
set_pause_state(True)
|
||||
if not self._town_manager.wait_for_tp(Location.A2_TP):
|
||||
return False
|
||||
if not self.approach(Location.A2_FARA_STASH):
|
||||
|
||||
+18
-19
@@ -78,10 +78,9 @@ 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:
|
||||
if (template_match := detect_screen_object(ScreenObjects.TownPortalReduced)).valid:
|
||||
pos = template_match.center
|
||||
pos = (pos[0], pos[1] + 30)
|
||||
Logger.debug(location + ": Going through portal...")
|
||||
@@ -111,9 +110,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 +142,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 +160,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 +184,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 +213,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 +234,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 +250,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 +276,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 +369,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 +386,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:
|
||||
|
||||
@@ -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
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
+6
-6
@@ -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()
|
||||
@@ -45,13 +45,13 @@ class Trav:
|
||||
if not self._pather.traverse_nodes((Location.A3_TRAV_START, Location.A3_TRAV_CENTER_STAIRS), self._char, force_move=True):
|
||||
return False
|
||||
self._char.kill_council()
|
||||
picked_up_items = self._pickit.pick_up_items(self._char, is_at_trav=True)
|
||||
picked_up_items = self._pickit.pick_up_items(self._char)
|
||||
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)
|
||||
picked_up_items |= self._pickit.pick_up_items(self._char, is_at_trav=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)
|
||||
# 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)
|
||||
|
||||
+2
-1
@@ -62,7 +62,8 @@ def set_window_position(offset_x: int, offset_y: int):
|
||||
def stop_detecting_window():
|
||||
global detect_window, detect_window_thread
|
||||
detect_window = False
|
||||
detect_window_thread.join()
|
||||
if detect_window_thread:
|
||||
detect_window_thread.join()
|
||||
|
||||
def grab() -> np.ndarray:
|
||||
global monitor_roi
|
||||
|
||||
+3
-3
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
+5
-5
@@ -6,7 +6,7 @@ from npc_manager import Npc, open_npc_menu, press_npc_btn
|
||||
from pather import Pather, Location
|
||||
from typing import Union
|
||||
from template_finder import TemplateFinder
|
||||
from ui_manager import detect_screen_object, ScreenObjects
|
||||
from ui_manager import ScreenObjects, is_visible
|
||||
from utils.misc import wait
|
||||
|
||||
|
||||
@@ -38,12 +38,12 @@ class A1(IAct):
|
||||
curr_loc = Location.A1_WP_SOUTH
|
||||
if not self._pather.traverse_nodes((curr_loc, Location.A1_WP_NORTH), self._char, force_move=True): return False
|
||||
wait(0.5, 0.7)
|
||||
found_wp_func = lambda: detect_screen_object(ScreenObjects.WaypointLabel).valid
|
||||
found_wp_func = lambda: is_visible(ScreenObjects.WaypointLabel)
|
||||
# decreased threshold because we sometimes walk "over" it during pathing
|
||||
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
|
||||
@@ -68,8 +68,8 @@ class A1(IAct):
|
||||
wait(0.5, 0.6)
|
||||
def stash_is_open_func():
|
||||
img = grab()
|
||||
found = detect_screen_object(ScreenObjects.GoldBtnInventory, img).valid
|
||||
found |= detect_screen_object(ScreenObjects.GoldBtnStash, img).valid
|
||||
found = is_visible(ScreenObjects.GoldBtnInventory, img)
|
||||
found |= is_visible(ScreenObjects.GoldBtnStash, img)
|
||||
return found
|
||||
if not self._char.select_by_template(["A1_TOWN_0"], stash_is_open_func):
|
||||
return False
|
||||
|
||||
+5
-6
@@ -1,13 +1,12 @@
|
||||
from char import IChar
|
||||
from town.i_act import IAct
|
||||
from screen import grab
|
||||
from config import Config
|
||||
from npc_manager import Npc, open_npc_menu, press_npc_btn
|
||||
from pather import Pather, Location
|
||||
from typing import Union
|
||||
from template_finder import TemplateFinder
|
||||
from utils.misc import wait
|
||||
from ui_manager import detect_screen_object, ScreenObjects
|
||||
from ui_manager import ScreenObjects, is_visible
|
||||
|
||||
class A2(IAct):
|
||||
def __init__(self, pather: Pather, char: IChar):
|
||||
@@ -33,8 +32,8 @@ class A2(IAct):
|
||||
wait(0.3)
|
||||
def stash_is_open_func():
|
||||
img = grab()
|
||||
found = detect_screen_object(ScreenObjects.GoldBtnInventory, img).valid
|
||||
found |= detect_screen_object(ScreenObjects.GoldBtnStash, img).valid
|
||||
found = is_visible(ScreenObjects.GoldBtnInventory, img)
|
||||
found |= is_visible(ScreenObjects.GoldBtnStash, img)
|
||||
return found
|
||||
if not self._char.select_by_template(["A2_STASH_LIGHT", "A2_STASH_DARK"], stash_is_open_func, telekinesis=True):
|
||||
return False
|
||||
@@ -64,11 +63,11 @@ class A2(IAct):
|
||||
def open_wp(self, curr_loc: Location) -> bool:
|
||||
if not self._pather.traverse_nodes((curr_loc, Location.A2_WP), self._char, force_move=True): return False
|
||||
wait(0.5, 0.7)
|
||||
found_wp_func = lambda: detect_screen_object(ScreenObjects.WaypointLabel).valid
|
||||
found_wp_func = lambda: is_visible(ScreenObjects.WaypointLabel)
|
||||
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
|
||||
|
||||
+5
-6
@@ -1,13 +1,12 @@
|
||||
from char import IChar
|
||||
from town.i_act import IAct
|
||||
from screen import grab
|
||||
from config import Config
|
||||
from npc_manager import Npc, open_npc_menu, press_npc_btn
|
||||
from pather import Pather, Location
|
||||
from typing import Union
|
||||
from template_finder import TemplateFinder
|
||||
from utils.misc import wait
|
||||
from ui_manager import detect_screen_object, ScreenObjects
|
||||
from ui_manager import ScreenObjects, is_visible
|
||||
|
||||
|
||||
class A3(IAct):
|
||||
@@ -40,8 +39,8 @@ class A3(IAct):
|
||||
wait(0.3)
|
||||
def stash_is_open_func():
|
||||
img = grab()
|
||||
found = detect_screen_object(ScreenObjects.GoldBtnInventory, img).valid
|
||||
found |= detect_screen_object(ScreenObjects.GoldBtnStash, img).valid
|
||||
found = is_visible(ScreenObjects.GoldBtnInventory, img)
|
||||
found |= is_visible(ScreenObjects.GoldBtnStash, img)
|
||||
return found
|
||||
if not self._char.select_by_template("A3_STASH", stash_is_open_func, telekinesis=True):
|
||||
return False
|
||||
@@ -50,11 +49,11 @@ class A3(IAct):
|
||||
def open_wp(self, curr_loc: Location) -> bool:
|
||||
if not self._pather.traverse_nodes((curr_loc, Location.A3_STASH_WP), self._char, force_move=True): return False
|
||||
wait(0.5, 0.7)
|
||||
found_wp_func = lambda: detect_screen_object(ScreenObjects.WaypointLabel).valid
|
||||
found_wp_func = lambda: is_visible(ScreenObjects.WaypointLabel)
|
||||
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
|
||||
|
||||
+5
-6
@@ -1,13 +1,12 @@
|
||||
from char import IChar
|
||||
from town.i_act import IAct
|
||||
from screen import grab
|
||||
from config import Config
|
||||
from npc_manager import Npc, open_npc_menu, press_npc_btn
|
||||
from pather import Pather, Location
|
||||
from typing import Union
|
||||
from template_finder import TemplateFinder
|
||||
from utils.misc import wait
|
||||
from ui_manager import detect_screen_object, ScreenObjects
|
||||
from ui_manager import ScreenObjects, is_visible
|
||||
|
||||
|
||||
class A4(IAct):
|
||||
@@ -35,12 +34,12 @@ class A4(IAct):
|
||||
def open_wp(self, curr_loc: Location) -> bool:
|
||||
if not self._pather.traverse_nodes((curr_loc, Location.A4_WP), self._char, force_move=True): return False
|
||||
wait(0.5, 0.7)
|
||||
found_wp_func = lambda: detect_screen_object(ScreenObjects.WaypointLabel).valid
|
||||
found_wp_func = lambda: is_visible(ScreenObjects.WaypointLabel)
|
||||
# decreased threshold because we sometimes walk "over" it during pathing
|
||||
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
|
||||
@@ -72,8 +71,8 @@ class A4(IAct):
|
||||
wait(0.5, 0.6)
|
||||
def stash_is_open_func():
|
||||
img = grab()
|
||||
found = detect_screen_object(ScreenObjects.GoldBtnInventory, img).valid
|
||||
found |= detect_screen_object(ScreenObjects.GoldBtnStash, img).valid
|
||||
found = is_visible(ScreenObjects.GoldBtnInventory, img)
|
||||
found |= is_visible(ScreenObjects.GoldBtnStash, img)
|
||||
return found
|
||||
if not self._char.select_by_template(["A4_TOWN_2"], stash_is_open_func, telekinesis=True):
|
||||
return False
|
||||
|
||||
+5
-6
@@ -1,13 +1,12 @@
|
||||
from char import IChar
|
||||
from town.i_act import IAct
|
||||
from screen import grab
|
||||
from config import Config
|
||||
from npc_manager import Npc, open_npc_menu, press_npc_btn
|
||||
from pather import Pather, Location
|
||||
from typing import Union
|
||||
from template_finder import TemplateFinder
|
||||
from utils.misc import wait
|
||||
from ui_manager import detect_screen_object, ScreenObjects
|
||||
from ui_manager import ScreenObjects, is_visible
|
||||
|
||||
|
||||
class A5(IAct):
|
||||
@@ -56,8 +55,8 @@ class A5(IAct):
|
||||
wait(0.5, 0.6)
|
||||
def stash_is_open_func():
|
||||
img = grab()
|
||||
found = detect_screen_object(ScreenObjects.GoldBtnInventory, img).valid
|
||||
found |= detect_screen_object(ScreenObjects.GoldBtnStash, img).valid
|
||||
found = is_visible(ScreenObjects.GoldBtnInventory, img)
|
||||
found |= is_visible(ScreenObjects.GoldBtnStash, img)
|
||||
return found
|
||||
if not self._char.select_by_template(["A5_STASH", "A5_STASH_2"], stash_is_open_func, telekinesis=True):
|
||||
return False
|
||||
@@ -72,11 +71,11 @@ class A5(IAct):
|
||||
def open_wp(self, curr_loc: Location) -> bool:
|
||||
if not self._pather.traverse_nodes((curr_loc, Location.A5_WP), self._char, force_move=True): return False
|
||||
wait(0.5, 0.7)
|
||||
found_wp_func = lambda: detect_screen_object(ScreenObjects.WaypointLabel).valid
|
||||
found_wp_func = lambda: is_visible(ScreenObjects.WaypointLabel)
|
||||
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
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -97,7 +97,8 @@ class TownManager:
|
||||
# check if we can buy pots in current act
|
||||
if self._acts[curr_act].can_buy_pots():
|
||||
new_loc = self._acts[curr_act].open_trade_menu(curr_loc)
|
||||
if not new_loc: return False, items
|
||||
if not (new_loc and common.wait_for_left_inventory()): return False, items
|
||||
common.select_tab(3)
|
||||
img=grab()
|
||||
# Buy HP pots
|
||||
if consumables.get_needs("health") > 0:
|
||||
|
||||
+12
-21
@@ -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, select_screen_object_match, wait_until_visible, ScreenObjects
|
||||
from .inventory_collection import InventoryCollection
|
||||
from .stash import Stash
|
||||
from .gem_picking import SimpleGemPicking
|
||||
@@ -15,8 +15,7 @@ from template_finder import TemplateFinder
|
||||
import numpy as np
|
||||
import keyboard
|
||||
import cv2
|
||||
from inventory import personal
|
||||
from inventory.stash import move_to_stash_tab
|
||||
from inventory import personal, common
|
||||
|
||||
FLAWLESS_GEMS = [
|
||||
"INVENTORY_TOPAZ_FLAWLESS",
|
||||
@@ -66,11 +65,9 @@ class Transmute:
|
||||
self._wait()
|
||||
|
||||
def open_cube(self):
|
||||
move_to_stash_tab(0)
|
||||
match = detect_screen_object(ScreenObjects.CubeInventory)
|
||||
if match.valid:
|
||||
x, y = convert_screen_to_monitor(match.center)
|
||||
mouse.move(x, y)
|
||||
common.select_tab(0)
|
||||
if (match := detect_screen_object(ScreenObjects.CubeInventory)).valid:
|
||||
mouse.move(*match.center)
|
||||
self._wait()
|
||||
mouse.click("right")
|
||||
self._wait()
|
||||
@@ -78,13 +75,8 @@ class Transmute:
|
||||
Logger.error(f"Can't find cube: {match.score}")
|
||||
|
||||
def transmute(self):
|
||||
match = detect_screen_object(ScreenObjects.CubeOpened)
|
||||
if match.valid:
|
||||
x, y = convert_screen_to_monitor(match.center)
|
||||
mouse.move(x, y)
|
||||
self._wait()
|
||||
mouse.click("left")
|
||||
self._wait()
|
||||
if (match := detect_screen_object(ScreenObjects.CubeOpened)).valid:
|
||||
select_screen_object_match(match)
|
||||
|
||||
def close_cube(self):
|
||||
self._wait()
|
||||
@@ -100,7 +92,7 @@ class Transmute:
|
||||
return self.pick_from_area(column, row, Config().ui_roi["right_inventory"])
|
||||
|
||||
def pick_from_stash_at(self, index, column, row):
|
||||
move_to_stash_tab(index)
|
||||
common.select_tab(index)
|
||||
return self.pick_from_area(column, row, Config().ui_roi["left_inventory"])
|
||||
|
||||
def inspect_area(self, total_rows, total_columns, roi, known_items) -> InventoryCollection:
|
||||
@@ -134,7 +126,7 @@ class Transmute:
|
||||
def inspect_stash(self) -> Stash:
|
||||
stash = Stash()
|
||||
for i in range(4):
|
||||
move_to_stash_tab(i)
|
||||
common.select_tab(i)
|
||||
wait(0.4, 0.5)
|
||||
tab = self.inspect_area(
|
||||
10, 10, Config().ui_roi["left_inventory"], FLAWLESS_GEMS)
|
||||
@@ -148,14 +140,14 @@ class Transmute:
|
||||
while flawless_gems.count_by(gem) > 0:
|
||||
pick.append((randint(0, 3), *flawless_gems.pop(gem)))
|
||||
for tab, x, y in sorted(pick, key=lambda x: x[0]):
|
||||
move_to_stash_tab(tab)
|
||||
common.select_tab(tab)
|
||||
self.pick_from_inventory_at(x, y)
|
||||
|
||||
def select_tab_with_enough_space(self, s: Stash) -> None:
|
||||
tabs_priority = Config()._transmute_config["stash_destination"]
|
||||
for tab in tabs_priority:
|
||||
if s.get_empty_on_tab(tab) > 0:
|
||||
move_to_stash_tab(tab)
|
||||
common.select_tab(tab)
|
||||
break
|
||||
|
||||
def put_back_all_gems(self, s: Stash) -> None:
|
||||
@@ -176,8 +168,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)
|
||||
if not gold_btn.valid:
|
||||
if not wait_until_visible(ScreenObjects.GoldBtnStash, timeout = 8).valid:
|
||||
Logger.error("Could not determine to be in stash menu. Continue...")
|
||||
return
|
||||
if not force and not self.should_transmute():
|
||||
|
||||
@@ -30,8 +30,7 @@ def has_char_template_saved():
|
||||
return last_char_template is not None
|
||||
|
||||
def save_char_online_status():
|
||||
match = detect_screen_object(ScreenObjects.OnlineStatus)
|
||||
if match.valid:
|
||||
if (match := detect_screen_object(ScreenObjects.OnlineStatus)).valid:
|
||||
online_status = online_active(match)
|
||||
Logger.debug(f"Saved online status. Online={online_status}")
|
||||
else:
|
||||
@@ -45,8 +44,7 @@ def online_active(match) -> bool:
|
||||
|
||||
def save_char_template():
|
||||
img = grab()
|
||||
match = detect_screen_object(ScreenObjects.SelectedCharacter)
|
||||
if match.valid:
|
||||
if (match := detect_screen_object(ScreenObjects.SelectedCharacter)).valid:
|
||||
x, y, w, h = Config().ui_roi["character_name_sub_roi"]
|
||||
x, y = x + match.region[0], y + match.region[1]
|
||||
char_template = cut_roi(img, [x, y, w, h])
|
||||
@@ -74,8 +72,7 @@ def save_char_template():
|
||||
def select_char():
|
||||
if last_char_template is not None:
|
||||
img = grab()
|
||||
match = detect_screen_object(ScreenObjects.OnlineStatus, img)
|
||||
if match.valid:
|
||||
if (match := detect_screen_object(ScreenObjects.OnlineStatus, img)).valid:
|
||||
if online_active(match) and (not online_character):
|
||||
select_online_tab(match.region, match.center)
|
||||
img = grab()
|
||||
@@ -86,8 +83,7 @@ def select_char():
|
||||
else:
|
||||
Logger.error("select_char: Could not find online/offline tabs")
|
||||
return
|
||||
match = detect_screen_object(ScreenObjects.SelectedCharacter, img)
|
||||
if not match.valid:
|
||||
if not (match := detect_screen_object(ScreenObjects.SelectedCharacter, img)).valid:
|
||||
Logger.error("select_char: Could not find highlighted profile")
|
||||
return
|
||||
scrolls_attempts = 0
|
||||
|
||||
@@ -5,8 +5,7 @@ import keyboard
|
||||
|
||||
def handle_error() -> bool:
|
||||
Logger.warning("Server connection issue. waiting 20s")
|
||||
match = detect_screen_object(ScreenObjects.ServerError)
|
||||
if match.valid:
|
||||
if (match := detect_screen_object(ScreenObjects.ServerError)).valid:
|
||||
select_screen_object_match(match)
|
||||
wait(1, 2)
|
||||
keyboard.send("esc")
|
||||
|
||||
+3
-3
@@ -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
|
||||
|
||||
+5
-9
@@ -3,7 +3,7 @@ from config import Config
|
||||
from utils.misc import wait
|
||||
from logger import Logger
|
||||
from ui import error_screens
|
||||
from ui_manager import detect_screen_object, select_screen_object_match, ScreenObjects
|
||||
from ui_manager import detect_screen_object, is_visible, select_screen_object_match, ScreenObjects
|
||||
|
||||
def start_game() -> bool:
|
||||
"""
|
||||
@@ -13,8 +13,7 @@ def start_game() -> bool:
|
||||
Logger.debug("Wait for Play button")
|
||||
start = time.time()
|
||||
while True:
|
||||
m = detect_screen_object(ScreenObjects.PlayBtn)
|
||||
if m.valid:
|
||||
if (m := detect_screen_object(ScreenObjects.PlayBtn)).valid:
|
||||
if play_active(m):
|
||||
# found active play button
|
||||
Logger.debug(f"Found Play Btn")
|
||||
@@ -38,20 +37,17 @@ def start_game() -> bool:
|
||||
start = time.time()
|
||||
while True:
|
||||
#look for difficulty select
|
||||
m = detect_screen_object(Difficulty)
|
||||
if m.valid:
|
||||
if (m := detect_screen_object(Difficulty)).valid:
|
||||
select_screen_object_match(m)
|
||||
break
|
||||
#check for loading screen
|
||||
m = detect_screen_object(ScreenObjects.Loading)
|
||||
if m.valid:
|
||||
if is_visible(ScreenObjects.Loading):
|
||||
Logger.debug("Found loading screen / creating game screen rather than difficulty select, normal difficulty")
|
||||
break
|
||||
else:
|
||||
wait(1,2)
|
||||
# check for server issue
|
||||
m = detect_screen_object(ScreenObjects.ServerError)
|
||||
if m.valid:
|
||||
if is_visible(ScreenObjects.ServerError):
|
||||
error_screens.handle_error()
|
||||
return start_game()
|
||||
|
||||
|
||||
+5
-8
@@ -1,4 +1,3 @@
|
||||
from typing import List
|
||||
import keyboard
|
||||
from logger import Logger
|
||||
import cv2
|
||||
@@ -8,10 +7,9 @@ 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 inventory import consumables
|
||||
from ui_manager import wait_until_visible, ScreenObjects
|
||||
|
||||
def is_left_skill_selected(template_list: List[str]) -> bool:
|
||||
def is_left_skill_selected(template_list: list[str]) -> bool:
|
||||
"""
|
||||
:return: Bool if skill is currently the selected skill on the left skill slot.
|
||||
"""
|
||||
@@ -27,12 +25,11 @@ def has_tps() -> bool:
|
||||
"""
|
||||
if Config().char["tp"]:
|
||||
keyboard.send(Config().char["tp"])
|
||||
template_match = wait_for_screen_object(ScreenObjects.TownPortalSkill, time_out=4)
|
||||
if not template_match.valid:
|
||||
if not (tps_remain := wait_until_visible(ScreenObjects.TownPortalSkill, timeout=4).valid):
|
||||
Logger.warning("You are out of tps")
|
||||
if Config().general["info_screenshots"]:
|
||||
cv2.imwrite("./info_screenshots/debug_out_of_tps_" + time.strftime("%Y%m%d_%H%M%S") + ".png", grab())
|
||||
return template_match.valid
|
||||
return tps_remain
|
||||
else:
|
||||
return False
|
||||
|
||||
@@ -57,7 +54,7 @@ def is_right_skill_active() -> bool:
|
||||
avg = np.average(img)
|
||||
return avg > 75.0
|
||||
|
||||
def is_right_skill_selected(template_list: List[str]) -> bool:
|
||||
def is_right_skill_selected(template_list: list[str]) -> bool:
|
||||
"""
|
||||
:return: Bool if skill is currently the selected skill on the right skill slot.
|
||||
"""
|
||||
|
||||
+34
-11
@@ -5,7 +5,8 @@ import keyboard
|
||||
from utils.custom_mouse import mouse
|
||||
from logger import Logger
|
||||
from utils.misc import wait
|
||||
from ui_manager import wait_for_screen_object, detect_screen_object, select_screen_object_match, ScreenObjects, list_visible_objects
|
||||
from ui_manager import wait_until_hidden, wait_until_visible, detect_screen_object, select_screen_object_match, ScreenObjects, list_visible_objects, is_visible
|
||||
from inventory import common
|
||||
from screen import convert_screen_to_monitor
|
||||
|
||||
def enable_no_pickup() -> bool:
|
||||
@@ -18,8 +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)
|
||||
if not item_pickup_text.valid:
|
||||
if not (item_pickup_text := wait_until_visible(ScreenObjects.ItemPickupText, timeout=1.3)).valid:
|
||||
return False
|
||||
if item_pickup_text.name == "ITEM_PICKUP_DISABLED":
|
||||
return True
|
||||
@@ -36,13 +36,34 @@ def save_and_exit() -> bool:
|
||||
Performes save and exit action from within game
|
||||
:return: Bool if action was successful
|
||||
"""
|
||||
start=time.time()
|
||||
while not (exit_button := detect_screen_object(ScreenObjects.SaveAndExit)).valid:
|
||||
keyboard.send("esc")
|
||||
wait(0.1)
|
||||
if time.time() - start > 5:
|
||||
# if exit button isn't detected already, press escape
|
||||
attempts = 1
|
||||
success = False
|
||||
while attempts <= 2 and not success:
|
||||
if not (exit_button := detect_screen_object(ScreenObjects.SaveAndExit)).valid:
|
||||
keyboard.send("esc")
|
||||
# wait for exit button to appear
|
||||
exit_button = wait_until_visible(ScreenObjects.SaveAndExit, 3)
|
||||
# if exit button is found, double click it to be sure
|
||||
if exit_button.valid:
|
||||
select_screen_object_match(exit_button, delay_factor=(0.1, 0.3))
|
||||
wait(0.1, 0.2)
|
||||
mouse.click(button="left")
|
||||
# if center icon on player bar disappears then save/exit was successful
|
||||
success = wait_until_hidden(ScreenObjects.InGame, 3)
|
||||
attempts += 1
|
||||
if not success:
|
||||
Logger.debug("Failed to find or click save/exit button")
|
||||
return success
|
||||
|
||||
def dismiss_skills_icon() -> bool:
|
||||
start = time.time()
|
||||
while (match := detect_screen_object(ScreenObjects.QuestSkillBtn)).valid:
|
||||
select_screen_object_match(match)
|
||||
wait(0.6)
|
||||
common.close()
|
||||
if (time.time() - start) > 15:
|
||||
return False
|
||||
select_screen_object_match(exit_button, delay_factor=(0.1, 0.3))
|
||||
return True
|
||||
|
||||
def pickup_corpse():
|
||||
@@ -62,7 +83,8 @@ def return_to_play() -> bool:
|
||||
start=time.time()
|
||||
while (elapsed := (time.time() - start) < 5):
|
||||
need_escape = False
|
||||
if "DARK" in detect_screen_object(ScreenObjects.InGame, img).name:
|
||||
match = detect_screen_object(ScreenObjects.InGame, img)
|
||||
if match.valid and "DARK" in match.name:
|
||||
need_escape = True
|
||||
if not need_escape:
|
||||
for substring in substrings:
|
||||
@@ -70,11 +92,12 @@ def return_to_play() -> bool:
|
||||
break
|
||||
if need_escape:
|
||||
keyboard.send("esc")
|
||||
wait(0.6, 1)
|
||||
wait(1)
|
||||
img=grab()
|
||||
else:
|
||||
break
|
||||
if not elapsed:
|
||||
Logger.error("return_to_play(): failed to return to neutral play screen")
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
+2
-5
@@ -1,11 +1,9 @@
|
||||
import time
|
||||
import numpy as np
|
||||
import re
|
||||
|
||||
from utils.custom_mouse import mouse
|
||||
from logger import Logger
|
||||
from config import Config
|
||||
from screen import convert_screen_to_monitor, grab
|
||||
from screen import convert_screen_to_monitor
|
||||
from utils.misc import wait
|
||||
from ui import loading
|
||||
from ui_manager import detect_screen_object, ScreenObjects
|
||||
@@ -66,8 +64,7 @@ def use_wp(label: str = None, act: int = None, idx: int = None) -> bool:
|
||||
if label:
|
||||
act = _WAYPOINTS[label][0]
|
||||
idx = _WAYPOINTS[label][1]
|
||||
match = detect_screen_object(ScreenObjects.WaypointTabs)
|
||||
if match.valid:
|
||||
if (match := detect_screen_object(ScreenObjects.WaypointTabs)).valid:
|
||||
curr_active_act = get_active_act_from_match(match)
|
||||
else:
|
||||
Logger.error("Could not find waypoint tabs")
|
||||
|
||||
+54
-42
@@ -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
|
||||
normalize_monitor: bool = True
|
||||
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
|
||||
@@ -110,12 +113,14 @@ class ScreenObjects:
|
||||
OnlineStatus=ScreenObject(
|
||||
ref=["CHARACTER_STATE_ONLINE", "CHARACTER_STATE_OFFLINE"],
|
||||
roi="character_online_status",
|
||||
best_match=True
|
||||
best_match=True,
|
||||
normalize_monitor=False
|
||||
)
|
||||
SelectedCharacter=ScreenObject(
|
||||
ref=["CHARACTER_ACTIVE"],
|
||||
roi="character_select",
|
||||
threshold=0.8
|
||||
threshold=0.8,
|
||||
normalize_monitor=False
|
||||
)
|
||||
ServerError=ScreenObject(
|
||||
ref=["SERVER_ISSUES"]
|
||||
@@ -142,35 +147,29 @@ class ScreenObjects:
|
||||
TownPortal=ScreenObject(
|
||||
ref="BLUE_PORTAL",
|
||||
threshold=0.8,
|
||||
roi="tp_search",
|
||||
normalize_monitor=True
|
||||
roi="tp_search"
|
||||
)
|
||||
TownPortalReduced=ScreenObject(
|
||||
ref="BLUE_PORTAL",
|
||||
threshold=0.8,
|
||||
roi="reduce_to_center",
|
||||
normalize_monitor=True
|
||||
roi="reduce_to_center"
|
||||
)
|
||||
GoldBtnInventory=ScreenObject(
|
||||
ref="INVENTORY_GOLD_BTN",
|
||||
roi="gold_btn",
|
||||
normalize_monitor=True
|
||||
roi="gold_btn"
|
||||
)
|
||||
GoldBtnStash=ScreenObject(
|
||||
ref="INVENTORY_GOLD_BTN",
|
||||
roi="gold_btn_stash",
|
||||
normalize_monitor=True
|
||||
roi="gold_btn_stash"
|
||||
)
|
||||
GoldBtnVendor=ScreenObject(
|
||||
ref="VENDOR_GOLD",
|
||||
roi="gold_btn_stash",
|
||||
normalize_monitor=True
|
||||
roi="gold_btn_stash"
|
||||
)
|
||||
GoldNone=ScreenObject(
|
||||
ref="INVENTORY_NO_GOLD",
|
||||
roi="inventory_gold",
|
||||
threshold=0.83,
|
||||
use_grayscale=True
|
||||
threshold=0.83
|
||||
)
|
||||
TownPortalSkill=ScreenObject(
|
||||
ref=["TP_ACTIVE", "TP_INACTIVE"],
|
||||
@@ -181,7 +180,6 @@ class ScreenObjects:
|
||||
RepairBtn=ScreenObject(
|
||||
ref="REPAIR_BTN",
|
||||
roi="repair_btn",
|
||||
normalize_monitor=True,
|
||||
use_grayscale=True
|
||||
)
|
||||
YouHaveDied=ScreenObject(
|
||||
@@ -224,7 +222,7 @@ class ScreenObjects:
|
||||
use_grayscale=True
|
||||
)
|
||||
RightPanel=ScreenObject(
|
||||
ref="CLOSE_PANEL",
|
||||
ref=["CLOSE_PANEL", "CLOSE_PANEL_2"],
|
||||
roi="right_panel_header",
|
||||
threshold=0.8,
|
||||
use_grayscale=True
|
||||
@@ -248,8 +246,7 @@ class ScreenObjects:
|
||||
)
|
||||
Key=ScreenObject(
|
||||
ref="INV_KEY",
|
||||
threshold=0.8,
|
||||
normalize_monitor=True
|
||||
threshold=0.8
|
||||
)
|
||||
EmptyStashSlot=ScreenObject(
|
||||
ref="STASH_EMPTY_SLOT",
|
||||
@@ -268,43 +265,59 @@ class ScreenObjects:
|
||||
use_grayscale=True,
|
||||
roi="quest_skill_btn"
|
||||
)
|
||||
TabIndicator=ScreenObject(
|
||||
ref="TAB_INDICATOR",
|
||||
roi="tab_indicator",
|
||||
normalize_monitor=False
|
||||
)
|
||||
DepositBtn=ScreenObject(
|
||||
ref=["DEPOSIT_BTN", "DEPOSIT_BTN_BRIGHT"],
|
||||
threshold=0.8,
|
||||
roi="deposit_btn",
|
||||
)
|
||||
|
||||
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)
|
||||
def select_screen_object_match(match: TemplateMatch, delay_factor: tuple[float, float] = (0.9, 1.1), normalize_monitor: bool = False) -> None:
|
||||
pos = match.center if not normalize_monitor else convert_screen_to_monitor(match.center)
|
||||
mouse.move(*pos, delay_factor=delay_factor)
|
||||
wait(0.05, 0.09)
|
||||
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.05)
|
||||
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 +326,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 is_visible(pair[1], img):
|
||||
visible.append(pair[0])
|
||||
return visible
|
||||
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
__version__ = '0.7.1'
|
||||
__version__ = '0.7.2'
|
||||
|
||||
Reference in New Issue
Block a user