A2 Pathing + Arcane run + Static for Sorc + Static Tele robustification + Chest class (#348)
This commit is contained in:
+21
-7
@@ -28,8 +28,8 @@ from char.sorceress import LightSorc, BlizzSorc, NovaSorc
|
||||
from char.trapsin import Trapsin
|
||||
from char.hammerdin import Hammerdin
|
||||
from char.barbarian import Barbarian
|
||||
from run import Pindle, ShenkEld, Trav, Nihlatak
|
||||
from town import TownManager, A3, A4, A5
|
||||
from run import Pindle, ShenkEld, Trav, Nihlatak, Arcane
|
||||
from town import TownManager, A2, A3, A4, A5
|
||||
|
||||
# Added for dclone ip hunt
|
||||
from messenger import Messenger
|
||||
@@ -70,7 +70,8 @@ class Bot:
|
||||
a5 = A5(self._screen, self._template_finder, self._pather, self._char, npc_manager)
|
||||
a4 = A4(self._screen, self._template_finder, self._pather, self._char, npc_manager)
|
||||
a3 = A3(self._screen, self._template_finder, self._pather, self._char, npc_manager)
|
||||
self._town_manager = TownManager(self._template_finder, self._ui_manager, self._item_finder, a3, a4, a5)
|
||||
a2 = A2(self._screen, self._template_finder, self._pather, self._char, npc_manager)
|
||||
self._town_manager = TownManager(self._template_finder, self._ui_manager, self._item_finder, a2, a3, a4, a5)
|
||||
self._route_config = self._config.routes
|
||||
self._route_order = self._config.routes_order
|
||||
|
||||
@@ -83,6 +84,7 @@ class Bot:
|
||||
"run_pindle": self._route_config["run_pindle"],
|
||||
"run_shenk": self._route_config["run_shenk"] or self._route_config["run_eldritch"],
|
||||
"run_nihlatak": self._route_config["run_nihlatak"],
|
||||
"run_arcane": self._route_config["run_arcane"],
|
||||
}
|
||||
# Adapt order to the config
|
||||
self._do_runs = OrderedDict((k, self._do_runs[k]) for k in self._route_order if k in self._do_runs and self._do_runs[k])
|
||||
@@ -94,6 +96,7 @@ class Bot:
|
||||
self._shenk = ShenkEld(self._template_finder, self._pather, self._town_manager, self._ui_manager, self._char, self._pickit)
|
||||
self._trav = Trav(self._template_finder, self._pather, self._town_manager, self._ui_manager, self._char, self._pickit)
|
||||
self._nihlatak = Nihlatak(self._screen, self._template_finder, self._pather, self._town_manager, self._ui_manager, self._char, self._pickit)
|
||||
self._arcane = Arcane(self._template_finder, self._pather, self._town_manager, self._ui_manager, self._char, self._pickit)
|
||||
|
||||
# Create member variables
|
||||
self._pick_corpse = pick_corpse
|
||||
@@ -108,7 +111,7 @@ class Bot:
|
||||
self._ran_no_pickup = False
|
||||
|
||||
# Create State Machine
|
||||
self._states=['hero_selection', 'town', 'pindle', 'shenk', 'trav', 'nihlatak']
|
||||
self._states=['hero_selection', 'town', 'pindle', 'shenk', 'trav', 'nihlatak', 'arcane']
|
||||
self._transitions = [
|
||||
{ 'trigger': 'create_game', 'source': 'hero_selection', 'dest': 'town', 'before': "on_create_game"},
|
||||
# Tasks within town
|
||||
@@ -118,9 +121,10 @@ class Bot:
|
||||
{ 'trigger': 'run_shenk', 'source': 'town', 'dest': 'shenk', 'before': "on_run_shenk"},
|
||||
{ 'trigger': 'run_trav', 'source': 'town', 'dest': 'trav', 'before': "on_run_trav"},
|
||||
{ 'trigger': 'run_nihlatak', 'source': 'town', 'dest': 'nihlatak', 'before': "on_run_nihlatak"},
|
||||
{ 'trigger': 'run_arcane', 'source': 'town', 'dest': 'arcane', 'before': "on_run_arcane"},
|
||||
# End run / game
|
||||
{ 'trigger': 'end_run', 'source': ['shenk', 'pindle', 'nihlatak','trav'], 'dest': 'town', 'before': "on_end_run"},
|
||||
{ 'trigger': 'end_game', 'source': ['town', 'shenk', 'pindle', 'nihlatak', 'trav', 'end_run'], 'dest': 'hero_selection', 'before': "on_end_game"},
|
||||
{ 'trigger': 'end_run', 'source': ['shenk', 'pindle', 'nihlatak', 'trav', 'arcane'], 'dest': 'town', 'before': "on_end_run"},
|
||||
{ 'trigger': 'end_game', 'source': ['town', 'shenk', 'pindle', 'nihlatak', 'trav', 'arcane', 'end_run'], 'dest': 'hero_selection', 'before': "on_end_game"},
|
||||
]
|
||||
self.machine = Machine(model=self, states=self._states, initial="hero_selection", transitions=self._transitions, queued=True)
|
||||
|
||||
@@ -258,7 +262,7 @@ class Bot:
|
||||
|
||||
# Check if we are out of tps or need repairing
|
||||
need_repair = self._ui_manager.repair_needed()
|
||||
if self._tps_left < random.randint(2, 5) or need_repair or self._config.char["always_repair"]:
|
||||
if self._tps_left < random.randint(3, 5) or need_repair or self._config.char["always_repair"]:
|
||||
if need_repair: Logger.info("Repair needed. Gear is about to break")
|
||||
else: Logger.info("Repairing and buying TPs at next Vendor")
|
||||
self._curr_loc = self._town_manager.repair_and_fill_tps(self._curr_loc)
|
||||
@@ -360,3 +364,13 @@ class Bot:
|
||||
if self._curr_loc:
|
||||
res = self._nihlatak.battle(not self._pre_buffed)
|
||||
self._ending_run_helper(res)
|
||||
|
||||
def on_run_arcane(self):
|
||||
res = False
|
||||
self._do_runs["run_arcane"] = False
|
||||
self._game_stats.update_location("Arc" if self._config.general['discord_status_condensed'] else "Arcane")
|
||||
self._curr_loc = self._arcane.approach(self._curr_loc)
|
||||
if self._curr_loc:
|
||||
res = self._arcane.battle(not self._pre_buffed)
|
||||
self._tps_left -= self._arcane.used_tps
|
||||
self._ending_run_helper(res)
|
||||
|
||||
@@ -143,6 +143,20 @@ class Hammerdin(IChar):
|
||||
self._cast_hammers(1.2, "redemption")
|
||||
return True
|
||||
|
||||
def kill_summoner(self) -> bool:
|
||||
# move mouse to below altar
|
||||
pos_m = self._screen.convert_abs_to_monitor((0, 20))
|
||||
mouse.move(*pos_m, randomize=80, delay_factor=[0.5, 0.7])
|
||||
# Attack
|
||||
self._cast_hammers(self._char_config["atk_len_arc"])
|
||||
wait(0.1, 0.15)
|
||||
self._cast_hammers(1.6, "redemption")
|
||||
# Move a bit back and another round
|
||||
self._move_and_attack((0, 80), self._char_config["atk_len_arc"] * 0.5)
|
||||
wait(0.1, 0.15)
|
||||
self._cast_hammers(1.6, "redemption")
|
||||
return True
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import os
|
||||
|
||||
+5
-2
@@ -42,7 +42,8 @@ class IChar:
|
||||
template_type: Union[str, List[str]],
|
||||
success_func: Callable = None,
|
||||
time_out: float = 8,
|
||||
threshold: float = 0.68
|
||||
threshold: float = 0.68,
|
||||
telekinesis: bool = False
|
||||
) -> bool:
|
||||
"""
|
||||
Finds any template from the template finder and interacts with it
|
||||
@@ -192,7 +193,6 @@ class IChar:
|
||||
Logger.warning("Failed to switch weapon, try again")
|
||||
wait(0.5)
|
||||
|
||||
|
||||
def pre_buff(self):
|
||||
pass
|
||||
|
||||
@@ -210,3 +210,6 @@ class IChar:
|
||||
|
||||
def kill_nihlatak(self, end_nodes: list[int]) -> bool:
|
||||
raise ValueError("Nihlatak is not implemented!")
|
||||
|
||||
def kill_summoner(self) -> bool:
|
||||
raise ValueError("Arcane is not implemented!")
|
||||
|
||||
@@ -220,7 +220,19 @@ class BlizzSorc(Sorceress):
|
||||
self._blizzard((0, 0), spray=10)
|
||||
return True
|
||||
|
||||
|
||||
def kill_summoner(self) -> bool:
|
||||
# Attack
|
||||
delay = [0.2, 0.3]
|
||||
cast_pos_abs = np.array([0, 0])
|
||||
pos_m = self._screen.convert_abs_to_monitor((-20, 20))
|
||||
mouse.move(*pos_m, randomize=80, delay_factor=[0.5, 0.7])
|
||||
for _ in range(int(self._char_config["atk_len_arc"])):
|
||||
self._right_attack(cast_pos_abs, delay, 11)
|
||||
self._left_attack(cast_pos_abs, delay, 11)
|
||||
wait(self._cast_duration, self._cast_duration + 0.2)
|
||||
return True
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import os
|
||||
import keyboard
|
||||
|
||||
@@ -145,6 +145,17 @@ class LightSorc(Sorceress):
|
||||
self._pather.traverse_nodes(end_nodes, self, time_out=0.8)
|
||||
return True
|
||||
|
||||
def kill_summoner(self) -> bool:
|
||||
# Attack
|
||||
delay = [0.2, 0.3]
|
||||
cast_pos_abs = np.array([0, 0])
|
||||
pos_m = self._screen.convert_abs_to_monitor((-20, 20))
|
||||
mouse.move(*pos_m, randomize=80, delay_factor=[0.5, 0.7])
|
||||
for _ in range(int(self._char_config["atk_len_arc"])):
|
||||
self._right_attack(cast_pos_abs, delay, 11)
|
||||
self._left_attack(cast_pos_abs, delay, 11)
|
||||
wait(self._cast_duration, self._cast_duration + 0.2)
|
||||
return True
|
||||
|
||||
if __name__ == "__main__":
|
||||
import os
|
||||
|
||||
@@ -41,15 +41,13 @@ class NovaSorc(Sorceress):
|
||||
def kill_eldritch(self) -> bool:
|
||||
self._pather.traverse_nodes_fixed([(675, 30)], self)
|
||||
self._cast_static(0.6)
|
||||
atk_len = max(1.3, self._char_config["atk_len_eldritch"] - 0.7)
|
||||
self._nova(atk_len)
|
||||
self._nova(self._char_config["atk_len_eldritch"])
|
||||
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._cast_static(0.6)
|
||||
atk_len = max(1.5, self._char_config["atk_len_shenk"] - 1.0)
|
||||
self._nova(atk_len)
|
||||
self._nova(self._char_config["atk_len_shenk"])
|
||||
return True
|
||||
|
||||
def kill_council(self) -> bool:
|
||||
@@ -84,6 +82,18 @@ class NovaSorc(Sorceress):
|
||||
self._move_and_attack((-70, -35), atk_len)
|
||||
return True
|
||||
|
||||
def kill_summoner(self) -> bool:
|
||||
# move mouse to below altar
|
||||
pos_m = self._screen.convert_abs_to_monitor((0, 20))
|
||||
mouse.move(*pos_m, randomize=80, delay_factor=[0.5, 0.7])
|
||||
# Attack
|
||||
self._nova(self._char_config["atk_len_arc"])
|
||||
# Move a bit back and another round
|
||||
self._move_and_attack((0, 80), self._char_config["atk_len_arc"] * 0.5)
|
||||
wait(0.1, 0.15)
|
||||
self._nova(self._char_config["atk_len_arc"] * 0.5)
|
||||
return True
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import os
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import keyboard
|
||||
from typing import Dict, Tuple, Union, List, Callable
|
||||
from utils.custom_mouse import mouse
|
||||
from char import IChar
|
||||
from template_finder import TemplateFinder
|
||||
@@ -33,6 +34,39 @@ class Sorceress(IChar):
|
||||
else:
|
||||
return super().pick_up_item(pos, item_name, prev_cast_start)
|
||||
|
||||
def select_by_template(
|
||||
self,
|
||||
template_type: Union[str, List[str]],
|
||||
success_func: Callable = None,
|
||||
time_out: 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"] and telekinesis:
|
||||
return super().select_by_template(template_type, success_func, time_out, 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
|
||||
if self._template_finder.search("WAYPOINT_MENU", self._screen.grab()).valid:
|
||||
keyboard.send("esc")
|
||||
start = time.time()
|
||||
while time_out is None or (time.time() - start) < time_out:
|
||||
template_match = self._template_finder.search(template_type, self._screen.grab(), threshold=threshold)
|
||||
if template_match.valid:
|
||||
x_m, y_m = self._screen.convert_screen_to_monitor(template_match.position)
|
||||
keyboard.send(self._skill_hotkeys["telekinesis"])
|
||||
wait(0.1, 0.2)
|
||||
mouse.move(x_m, y_m)
|
||||
wait(0.2, 0.3)
|
||||
mouse.click(button="right")
|
||||
# check the successfunction for 2 sec, if not found, try again
|
||||
check_success_start = time.time()
|
||||
while time.time() - check_success_start < 2:
|
||||
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)
|
||||
|
||||
def pre_buff(self):
|
||||
if self._char_config["cta_available"]:
|
||||
self._pre_buff_cta()
|
||||
|
||||
@@ -150,4 +150,3 @@ if __name__ == "__main__":
|
||||
pather = Pather(screen, t_finder)
|
||||
ui_manager = UiManager(screen, t_finder)
|
||||
char = Trapsin(config.trapsin, config.char, screen, t_finder, ui_manager, pather)
|
||||
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
import time
|
||||
import os
|
||||
|
||||
from logger import Logger
|
||||
from template_finder import TemplateFinder
|
||||
from screen import Screen
|
||||
from char import IChar
|
||||
from config import Config
|
||||
from utils.custom_mouse import mouse
|
||||
from utils.misc import wait
|
||||
|
||||
|
||||
class Chest:
|
||||
def __init__(self, char: IChar, template_finder: TemplateFinder, template: str = None):
|
||||
self._config = Config()
|
||||
self._screen = Screen(self._config.general["monitor"])
|
||||
self._char = char
|
||||
self._template_finder = template_finder
|
||||
self._folder_name = "chests"
|
||||
# load all templates
|
||||
self._templates = []
|
||||
for filename in os.listdir(f'assets/{self._folder_name}/{template}'):
|
||||
filename = filename.lower()
|
||||
if filename.endswith('.png'):
|
||||
chest = filename[:-4].upper()
|
||||
self._templates.append(chest)
|
||||
|
||||
def open_up_chests(self, time_out: 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:
|
||||
template_match = self._template_finder.search(templates, self._screen.grab(), roi=self._config.ui_roi["reduce_to_center"], threshold=threshold, use_grayscale=True, best_match=True)
|
||||
# search for at least 1.5 second, if no chest found, break
|
||||
if not template_match.valid:
|
||||
if time.time() - start > 1.5:
|
||||
break
|
||||
else:
|
||||
found_chest = True
|
||||
x_m, y_m = self._screen.convert_screen_to_monitor(template_match.position)
|
||||
# move mouse and check for label
|
||||
mouse.move(x_m, y_m, delay_factor=[0.4, 0.6])
|
||||
wait(0.13, 0.16)
|
||||
chest_label = self._template_finder.search("CHEST_LABEL", self._screen.grab(), threshold=0.85)
|
||||
if chest_label.valid:
|
||||
Logger.debug(f"Opening {template_match.name} ({template_match.score*100:.1f}% confidence)")
|
||||
# TODO: Act as picking up a potion to support telekinesis. This workaround needs a proper solution.
|
||||
self._char.pick_up_item([x_m, y_m], 'potion')
|
||||
wait(0.13, 0.16)
|
||||
locked_chest = self._template_finder.search("LOCKED", self._screen.grab(), threshold=0.85)
|
||||
if locked_chest.valid:
|
||||
templates.remove(template_match.name)
|
||||
Logger.debug("No more keys, removing locked chest template")
|
||||
continue
|
||||
else:
|
||||
templates.remove(template_match.name)
|
||||
Logger.debug("No chests left")
|
||||
return found_chest
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import keyboard
|
||||
import os
|
||||
keyboard.add_hotkey('f12', lambda: Logger.info('Force Exit (f12)') or os._exit(1))
|
||||
print("Move to d2r window and press f11")
|
||||
keyboard.wait("f11")
|
||||
from char.hammerdin import Hammerdin
|
||||
from screen import Screen
|
||||
from pather import Pather
|
||||
from config import Config
|
||||
from ui import UiManager
|
||||
config = Config()
|
||||
screen = Screen(config.general["monitor"])
|
||||
template_finder = TemplateFinder(screen)
|
||||
pather = Pather(screen, template_finder)
|
||||
ui_manager = UiManager(screen, template_finder)
|
||||
char = Hammerdin(config.hammerdin, config.char, screen, template_finder, ui_manager, pather)
|
||||
chest = Chest(char, template_finder, 'arcane')
|
||||
chest.open_up_chests(threshold=0.8)
|
||||
+3
-1
@@ -104,13 +104,15 @@ class Config:
|
||||
"stash_gold": bool(int(self._select_val("char", "stash_gold"))),
|
||||
"gold_trav_only": bool(int(self._select_val("char", "gold_trav_only"))),
|
||||
"use_merc": bool(int(self._select_val("char", "use_merc"))),
|
||||
"id_items": bool(int(self._select_val("char", "ID_items"))),
|
||||
"id_items": bool(int(self._select_val("char", "id_items"))),
|
||||
"open_chests": bool(int(self._select_val("char", "open_chests"))),
|
||||
"pre_buff_every_run": bool(int(self._select_val("char", "pre_buff_every_run"))),
|
||||
"cta_available": bool(int(self._select_val("char", "cta_available"))),
|
||||
"weapon_switch": self._select_val("char", "weapon_switch"),
|
||||
"battle_orders": self._select_val("char", "battle_orders"),
|
||||
"battle_command": self._select_val("char", "battle_command"),
|
||||
"casting_frames": int(self._select_val("char", "casting_frames")),
|
||||
"atk_len_arc": float(self._select_val("char", "atk_len_arc")),
|
||||
"atk_len_trav": float(self._select_val("char", "atk_len_trav")),
|
||||
"atk_len_pindle": float(self._select_val("char", "atk_len_pindle")),
|
||||
"atk_len_eldritch": float(self._select_val("char", "atk_len_eldritch")),
|
||||
|
||||
@@ -48,7 +48,7 @@ class HealthManager:
|
||||
|
||||
def update_location(self, loc: Location):
|
||||
if loc is not None and type(loc) == str:
|
||||
bosses = ["shenk", "eldritch", "pindle", "nihlatak", "trav"]
|
||||
bosses = ["shenk", "eldritch", "pindle", "nihlatak", "trav", "arc"]
|
||||
prev_value = self._pausing
|
||||
self._pausing = not any(substring in loc for substring in bosses)
|
||||
if self._pausing != prev_value:
|
||||
|
||||
@@ -145,4 +145,4 @@ if __name__ == "__main__":
|
||||
# cv2.putText(img, item.name, item.center, cv2.FONT_HERSHEY_SIMPLEX, 0.8, (255, 255, 255), 1, cv2.LINE_AA)
|
||||
# img = cv2.resize(img, None, fx=0.5, fy=0.5)
|
||||
cv2.imshow('test', img)
|
||||
cv2.waitKey(1)
|
||||
cv2.waitKey(1)
|
||||
@@ -16,6 +16,7 @@ class Npc:
|
||||
ANYA = "anya"
|
||||
TYRAEL = "tyrael"
|
||||
ORMUS = "ormus"
|
||||
FARA = "fara"
|
||||
CAIN = "cain"
|
||||
|
||||
class NpcManager:
|
||||
@@ -90,6 +91,17 @@ class NpcManager:
|
||||
},
|
||||
"template_group": ["ORMUS_0", "ORMUS_1", "ORMUS_2", "ORMUS_3", "ORMUS_4", "ORMUS_5"]
|
||||
},
|
||||
Npc.FARA: {
|
||||
"name_tag_white": color_filter(self._template_finder.get_template("FARA_NAME_TAG_WHITE"), self._config.colors["white"])[1],
|
||||
"name_tag_gold": color_filter(self._template_finder.get_template("FARA_NAME_TAG_GOLD"), self._config.colors["gold"])[1],
|
||||
"action_btns": {
|
||||
"trade_repair": {
|
||||
"white": color_filter(self._template_finder.get_template("TRADE_REPAIR"), self._config.colors["white"])[1],
|
||||
"blue": color_filter(self._template_finder.get_template("TRADE_REPAIR_BLUE"), self._config.colors["blue"])[1],
|
||||
}
|
||||
},
|
||||
"template_group": ["FARA_LIGHT_1", "FARA_LIGHT_2", "FARA_LIGHT_3", "FARA_LIGHT_4", "FARA_LIGHT_5", "FARA_LIGHT_6", "FARA_LIGHT_7", "FARA_LIGHT_8", "FARA_LIGHT_9", "FARA_MEDIUM_1", "FARA_MEDIUM_2", "FARA_MEDIUM_3", "FARA_MEDIUM_4", "FARA_MEDIUM_5", "FARA_MEDIUM_6", "FARA_MEDIUM_7", "FARA_DARK_1", "FARA_DARK_2", "FARA_DARK_3", "FARA_DARK_4", "FARA_DARK_5", "FARA_DARK_6", "FARA_DARK_7"]
|
||||
},
|
||||
# Currently just nametag added to avoid having him focused when wanting to talk to tyrael or qual
|
||||
#added in the ability to ID items at cain.
|
||||
Npc.CAIN: {
|
||||
|
||||
+43
-7
@@ -46,12 +46,21 @@ class Location:
|
||||
A3_ORMUS = "a3_ormus"
|
||||
A3_STASH_WP = "a3_stash_wp"
|
||||
A3_ASHEARA = "a3_asheara"
|
||||
# A2 Town
|
||||
A2_TOWN_START = "a2_town_start"
|
||||
A2_WP = "a2_wp"
|
||||
A2_TP = "a2_tp"
|
||||
A2_FARA_STASH = "a2_fara_stash"
|
||||
# Trav
|
||||
A3_TRAV_START = "a3_trav_start"
|
||||
A3_TRAV_CENTER_STAIRS = "a3_trav_center_stairs"
|
||||
# Nihalatk
|
||||
A5_NIHLATAK_START = "a5_nihlatak_lvl1_start"
|
||||
A5_NIHLATAK_END = "a5_nihlatak_lvl2_end"
|
||||
# Arcane
|
||||
A2_ARC_START = "a2_arc_start"
|
||||
A2_ARC_CHECKPOINT = "a2_arc_checkpoint"
|
||||
A2_ARC_END = "a2_arc_end"
|
||||
|
||||
|
||||
class Pather:
|
||||
@@ -126,6 +135,15 @@ class Pather:
|
||||
190: {"A3_TOWN_12": (27, -198), "A3_TOWN_16": (831, 258), "A3_TOWN_14": (-318, 185), "A3_TOWN_15": (411, -9), "A3_TOWN_5": (-301, -334), "A3_TOWN_13": (-543, -10), "A3_TOWN_17": (707, 541)},
|
||||
191: {"A3_TOWN_15": (-79, -256), "A3_TOWN_16": (341, 11), "A3_TOWN_17": (217, 294), "A3_TOWN_18": (542, 408), "A3_TOWN_19": (779, 171)},
|
||||
192: {"A3_TOWN_17": (-187, 78), "A3_TOWN_16": (-63, -205), "A3_TOWN_18": (138, 192), "A3_TOWN_19": (375, -45)},
|
||||
# A2 town
|
||||
400: {"A2_TOWN_2": (-169, 160), "A2_TOWN_0": (290, -114), "A2_TOWN_3": (-348, 225), "A2_TOWN_1": (-345, -374)},
|
||||
401: {"A2_TOWN_3": (45, -202), "A2_TOWN_7": (-40, 266), "A2_TOWN_2": (224, -267), "A2_TOWN_6": (-505, 26), "A2_TOWN_5": (116, 203)},
|
||||
402: {"A2_TOWN_8": (182, 121), "A2_TOWN_6": (103, -348), "A2_TOWN_11": (128, 357), "A2_TOWN_9": (-324, 223), "A2_TOWN_12": (-213, 500)},
|
||||
403: {"A2_TOWN_11": (161, -18), "A2_TOWN_12": (-180, 125), "A2_TOWN_9": (-291, -152), "A2_TOWN_8": (215, -254), "A2_TOWN_13": (149, 392)},
|
||||
404: {"A2_TOWN_14": (79, 190), "A2_TOWN_15": (244, -12), "A2_TOWN_13": (-270, 123), "A2_TOWN_11": (-258, -287), "A2_TOWN_12": (-599, -143)},
|
||||
405: {"A2_TOWN_10": (65, -175), "A2_TOWN_17": (-108, 164), "A2_TOWN_16": (-304, -11), "A2_TOWN_9": (319, -68), "A2_TOWN_18": (-415, -284)},
|
||||
406: {"A2_TOWN_18": (108, -143), "A2_TOWN_16": (219, 129), "A2_TOWN_19": (-293, 21), "A2_TOWN_17": (415, 304), "A2_TOWN_10": (588, -34)},
|
||||
408: {"A2_TOWN_20": (-26, -109), "A2_TOWN_22": (-82, 278), "A2_TOWN_19": (344, 38), "A2_TOWN_21": (-518, -299), "A2_TOWN_18": (745, -125)},
|
||||
# Trav
|
||||
220: {"TRAV_0": (445, 384), "TRAV_20": (-259, 267), "TRAV_1": (-248, -139), "TRAV_2": (-682, 21), "TRAV_21": (25, 180)},
|
||||
221: {"TRAV_2": (-153, -101), "TRAV_3": (-125, 201), "TRAV_20": (270, 145), "TRAV_1": (281, -261), "TRAV_4": (-459, 122)},
|
||||
@@ -142,6 +160,13 @@ class Pather:
|
||||
301: {"TRAV_V3_7": (63, -113), "TRAV_V3_6": (55, 77), "TRAV_V3_0": (-27, -315), "TRAV_V3_5": (-450, -175), "TRAV_V3_8": (329, -188)},
|
||||
302: {"TRAV_V3_0": (-18, 6), "TRAV_V3_7": (73, 208), "TRAV_V3_8": (339, 133), "TRAV_V3_6": (65, 398), "TRAV_V3_5": (-440, 146)},
|
||||
304: {"TRAV_V2_4": (125, -148), "TRAV_V2_3": (-187, 55), "TRAV_V2_1": (-207, 59), "TRAV_V2_2": (267, 183), "TRAV_V2_0": (-159, 403)},
|
||||
# Arcane
|
||||
450: {"ARC_START": (49, 62)},
|
||||
453: {"ARC_START": (-259, 62)},
|
||||
456: {"ARC_START": (145, 264)},
|
||||
459: {"ARC_START": (-356, 258)},
|
||||
461: {"ARC_ALTAR": (60, 120), "ARC_ALTAR3": (-272, 200), "ARC_CENTER_2": (67, 41), "ARC_END_STAIRS": (76, -344), "ARC_END_STAIRS_2": (60, -160)},
|
||||
462: {"ARC_PLATFORM_1": (0, -100), "ARC_PLATFORM_2": (50, -100), "ARC_PLATFORM_3": (70, 0)},
|
||||
# Nil - End of Arm A
|
||||
500: {"NIL2A_0": (-200, 1), "NIL2A_2": (-181, -102), "NIL2A_1": (329, 146), "NIL2A_4": (835, 24), "NIL2A_5": (-384, -20), "NIL2A_6": (-600, 410)},
|
||||
501: {"NIL2A_4": (270+20, 259-50), "NIL2A_1": (-236+20, 381-50), "NIL2A_2": (-746+20, 133-50), "NIL2A_0": (-765+20, 236-50)},
|
||||
@@ -176,7 +201,7 @@ class Pather:
|
||||
(Location.A5_QUAL_KEHK, Location.A5_NIHLATHAK_PORTAL): [12, 11, 10, 6, 8, 9],
|
||||
(Location.A5_QUAL_KEHK, Location.A5_WP): [12, 11, 10, 6],
|
||||
(Location.A5_QUAL_KEHK, Location.A5_STASH): [12, 11, 10, 6, 5],
|
||||
(Location.A5_LARZUK, Location.A5_QUAL_KEHK): [13, 14, 5, 6, 10, 11, 12],
|
||||
(Location.A5_LARZUK, Location.A5_QUAL_KEHK): [13, 14, 5, 6, 10, 11, 12],
|
||||
(Location.A5_LARZUK, Location.A5_NIHLATHAK_PORTAL): [14, 13, 5, 6, 8, 9],
|
||||
(Location.A5_LARZUK, Location.A5_WP): [14, 13, 5],
|
||||
# Pindle
|
||||
@@ -201,6 +226,12 @@ class Pather:
|
||||
(Location.A3_ASHEARA, Location.A3_STASH_WP): [191, 190, 189, 185, 186, 187, 188],
|
||||
(Location.A3_STASH_WP, Location.A3_STASH_WP): [188],
|
||||
(Location.A3_STASH_WP, Location.A3_ORMUS): [187, 186, 185],
|
||||
# A2 Town
|
||||
(Location.A2_TOWN_START, Location.A2_WP): [400, 401, 402, 403, 404],
|
||||
(Location.A2_TOWN_START, Location.A2_FARA_STASH): [400, 401, 402, 405],
|
||||
(Location.A2_FARA_STASH, Location.A2_WP): [403, 404],
|
||||
(Location.A2_TP, Location.A2_FARA_STASH): [408, 406, 405],
|
||||
(Location.A2_WP, Location.A2_FARA_STASH): [404, 403, 405],
|
||||
# Trav
|
||||
(Location.A3_TRAV_START, Location.A3_TRAV_CENTER_STAIRS): [220, 221, 222, 223, 224, 225, 226],
|
||||
}
|
||||
@@ -226,7 +257,7 @@ class Pather:
|
||||
def _convert_rel_to_abs(rel_loc: Tuple[float, float], pos_abs: Tuple[float, float]) -> Tuple[float, float]:
|
||||
return (rel_loc[0] + pos_abs[0], rel_loc[1] + pos_abs[1])
|
||||
|
||||
def traverse_nodes_fixed(self, key: Union[str, List[Tuple[float, float]]], char: IChar):
|
||||
def traverse_nodes_fixed(self, key: Union[str, List[Tuple[float, float]]], char: IChar) -> bool:
|
||||
if not char.can_teleport():
|
||||
error_msg = "Teleport is requiered for static pathing"
|
||||
Logger.error(error_msg)
|
||||
@@ -237,6 +268,7 @@ class Pather:
|
||||
else:
|
||||
path = key
|
||||
i = 0
|
||||
stuck_count = 0
|
||||
while i < len(path):
|
||||
x_m, y_m = self._screen.convert_screen_to_monitor(path[i])
|
||||
x_m += int(random.random() * 6 - 3)
|
||||
@@ -252,9 +284,13 @@ class Pather:
|
||||
if score > 0.15:
|
||||
i += 1
|
||||
else:
|
||||
stuck_count += 1
|
||||
Logger.debug(f"Teleport cancel detected. Try same teleport action again. ({score:.4f})")
|
||||
if stuck_count >= 5:
|
||||
return False
|
||||
# if type(key) == str and ("_save_dist" in key or "_end" in key):
|
||||
# cv2.imwrite(f"./info_screenshots/nil_path_{key}_" + time.strftime("%Y%m%d_%H%M%S") + ".png", self._screen.grab())
|
||||
return True
|
||||
|
||||
def _adjust_abs_range_to_screen(self, abs_pos: Tuple[float, float]) -> Tuple[float, float]:
|
||||
"""
|
||||
@@ -367,7 +403,7 @@ class Pather:
|
||||
Logger.error("Got stuck exit pather")
|
||||
return False
|
||||
|
||||
# Sometimes we get stuck at rocks and stuff, after 2.5 seconds force a move into the last know direction
|
||||
# Sometimes we get stuck at rocks and stuff, after a few seconds force a move into the last know direction
|
||||
if not did_force_move and time.time() - last_move > 3.1:
|
||||
pos_abs = (0, 150)
|
||||
if last_direction is not None:
|
||||
@@ -425,7 +461,7 @@ if __name__ == "__main__":
|
||||
x, y = pather._screen.convert_abs_to_screen(ref_pos_abs)
|
||||
cv2.circle(display_img, (x, y), 5, (0, 255, 0), 3)
|
||||
cv2.putText(display_img, template_type, (x, y), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2, cv2.LINE_AA)
|
||||
# display_img = cv2.resize(display_img, None, fx=0.5, fy=0.5)
|
||||
display_img = cv2.resize(display_img, None, fx=0.5, fy=0.5)
|
||||
cv2.imshow("debug", display_img)
|
||||
cv2.waitKey(1)
|
||||
|
||||
@@ -441,7 +477,7 @@ if __name__ == "__main__":
|
||||
t_finder = TemplateFinder(screen)
|
||||
pather = Pather(screen, t_finder)
|
||||
|
||||
display_all_nodes(pather, "ELD")
|
||||
display_all_nodes(pather, "A2_TOWN")
|
||||
|
||||
# # changing node pos and generating new code
|
||||
# code = ""
|
||||
@@ -455,5 +491,5 @@ if __name__ == "__main__":
|
||||
|
||||
ui_manager = UiManager(screen, t_finder)
|
||||
char = Hammerdin(config.hammerdin, config.char, screen, t_finder, ui_manager, pather)
|
||||
pather.traverse_nodes_fixed("trav_safe_dist", char)
|
||||
# pather.traverse_nodes([226, 228, 229], char)
|
||||
# pather.traverse_nodes([452], char)
|
||||
pather.traverse_nodes_fixed("arc_top_right", char)
|
||||
|
||||
@@ -2,3 +2,4 @@ from .pindle import Pindle
|
||||
from .shenk_eld import ShenkEld
|
||||
from .trav import Trav
|
||||
from .nihlatak import Nihlatak
|
||||
from .arcane import Arcane
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
from char.i_char import IChar
|
||||
from config import Config
|
||||
from logger import Logger
|
||||
from pather import Location, Pather
|
||||
from typing import Union
|
||||
from item.pickit import PickIt
|
||||
from template_finder import TemplateFinder
|
||||
from town.town_manager import TownManager
|
||||
from ui import UiManager
|
||||
from utils.misc import wait
|
||||
from dataclasses import dataclass
|
||||
from chest import Chest
|
||||
|
||||
|
||||
class Arcane:
|
||||
def __init__(
|
||||
self,
|
||||
template_finder: TemplateFinder,
|
||||
pather: Pather,
|
||||
town_manager: TownManager,
|
||||
ui_manager: UiManager,
|
||||
char: IChar,
|
||||
pickit: PickIt
|
||||
):
|
||||
self._config = Config()
|
||||
self._template_finder = template_finder
|
||||
self._pather = pather
|
||||
self._town_manager = town_manager
|
||||
self._ui_manager = ui_manager
|
||||
self._char = char
|
||||
self._pickit = pickit
|
||||
self._chest = Chest(self._char, self._template_finder, 'arcane')
|
||||
self.used_tps = 0
|
||||
|
||||
def approach(self, start_loc: Location) -> Union[bool, Location]:
|
||||
Logger.info("Run Arcane")
|
||||
if not self._char.can_teleport():
|
||||
raise ValueError("Arcane requires teleport")
|
||||
if not self._town_manager.open_wp(start_loc):
|
||||
return False
|
||||
wait(0.4)
|
||||
self._ui_manager.use_wp(2, 7)
|
||||
return Location.A2_ARC_START
|
||||
|
||||
def _find_summoner(self, traverse_to_summoner: list[tuple[float, float]]) -> bool:
|
||||
# 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 = self._template_finder.search_and_wait(templates_platform, threshold=0.55, time_out=0.5, use_grayscale=True, take_ss=False)
|
||||
match_summoner = self._template_finder.search_and_wait(tempaltes_summoner, threshold=0.79, time_out=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 = self._template_finder.search_and_wait(tempaltes_summoner, threshold=0.79, time_out=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):
|
||||
return True
|
||||
else:
|
||||
# Traverse to center of platform
|
||||
self._pather.traverse_nodes([462], self._char, time_out=1.3, force_tp=True)
|
||||
return False
|
||||
|
||||
def battle(self, do_pre_buff: bool) -> Union[bool, tuple[Location, bool]]:
|
||||
picked_up_items = False
|
||||
@dataclass
|
||||
class PathData:
|
||||
calib_node_start: int
|
||||
static_path_forward: str
|
||||
jump_to_summoner: list[tuple[float, float]]
|
||||
|
||||
path_arr = [
|
||||
PathData(450, "arc_top_right", [(500, 40)]),
|
||||
PathData(453, "arc_top_left", [(500, 40)]),
|
||||
PathData(456, "arc_bottom_right", [(500, 40)]),
|
||||
PathData(459, "arc_bottom_left", [(500, 20), (700, 100)])
|
||||
]
|
||||
|
||||
picked_up_items = False
|
||||
self.used_tps = 0
|
||||
if do_pre_buff:
|
||||
self._char.pre_buff()
|
||||
|
||||
for i, data in enumerate(path_arr):
|
||||
# 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):
|
||||
return False
|
||||
found = self._find_summoner(data.jump_to_summoner)
|
||||
# Kill the summoner or trash mob
|
||||
self._char.kill_summoner()
|
||||
if self._config.char["open_chests"]:
|
||||
self._chest.open_up_chests()
|
||||
picked_up_items |= self._pickit.pick_up_items(self._char)
|
||||
if found:
|
||||
return (Location.A2_ARC_END, picked_up_items)
|
||||
elif i < len(path_arr) - 1:
|
||||
# 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
|
||||
if not self._town_manager.wait_for_tp(Location.A2_TP):
|
||||
return False
|
||||
if not self.approach(Location.A2_FARA_STASH):
|
||||
return False
|
||||
return False
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from screen import Screen
|
||||
import keyboard
|
||||
from game_stats import GameStats
|
||||
import os
|
||||
keyboard.add_hotkey('f12', lambda: os._exit(1))
|
||||
keyboard.wait("f11")
|
||||
from config import Config
|
||||
from ui import UiManager
|
||||
from bot import Bot
|
||||
config = Config()
|
||||
screen = Screen(config.general["monitor"])
|
||||
game_stats = GameStats()
|
||||
bot = Bot(screen, game_stats, False)
|
||||
bot._arcane._find_summoner([(500, 40)])
|
||||
+1
-1
@@ -107,4 +107,4 @@ class Nihlatak:
|
||||
self._char.kill_nihlatak(end_nodes)
|
||||
wait(0.2, 0.3)
|
||||
picked_up_items = self._pickit.pick_up_items(self._char)
|
||||
return (Location.A5_NIHLATAK_END, picked_up_items)
|
||||
return (Location.A5_NIHLATAK_END, picked_up_items)
|
||||
+1
-1
@@ -117,7 +117,7 @@ if __name__ == "__main__":
|
||||
p1 = (x, y)
|
||||
p2 = (x+w, y+h)
|
||||
cv2.rectangle(test_img, p1, p2, (0, 255, 0), 2)
|
||||
cv2.putText(test_img, roi_key, (p1[0], p1[1]+20), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 0), 1, cv2.LINE_AA)
|
||||
cv2.putText(test_img, roi_key, (p1[0], p1[1]+20), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 1, cv2.LINE_AA)
|
||||
|
||||
if show_pt:
|
||||
pass
|
||||
|
||||
@@ -22,7 +22,7 @@ class TemplateFinder:
|
||||
Loads images from assets/templates and assets/npc and provides search functions
|
||||
to find these assets within another image
|
||||
"""
|
||||
def __init__(self, screen: Screen, template_pathes: list[str] = ["assets\\templates", "assets\\npc", "assets\\item_properties"]):
|
||||
def __init__(self, screen: Screen, template_pathes: list[str] = ["assets\\templates", "assets\\npc", "assets\\item_properties", "assets\\chests"]):
|
||||
self._screen = screen
|
||||
self._config = Config()
|
||||
self.last_res = None
|
||||
@@ -185,18 +185,19 @@ if __name__ == "__main__":
|
||||
config = Config()
|
||||
screen = Screen(config.general["monitor"])
|
||||
template_finder = TemplateFinder(screen)
|
||||
search_templates = ["REPAIR_NEEDED"]
|
||||
search_templates = ["ARC_ALTAR", "ARC_ALTAR2"]
|
||||
while 1:
|
||||
# img = cv2.imread("")
|
||||
img = screen.grab()
|
||||
display_img = img.copy()
|
||||
start = time.time()
|
||||
for key in search_templates:
|
||||
template_match = template_finder.search(key, img, best_match=True, threshold=0.35, use_grayscale=True)
|
||||
template_match = template_finder.search(key, img, best_match=True, threshold=0.5, use_grayscale=True)
|
||||
if template_match.valid:
|
||||
cv2.putText(display_img, str(template_match.name), template_match.position, cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 0), 2, cv2.LINE_AA)
|
||||
cv2.putText(display_img, str(template_match.name), template_match.position, cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2, cv2.LINE_AA)
|
||||
cv2.circle(display_img, template_match.position, 7, (255, 0, 0), thickness=5)
|
||||
print(f"Name: {template_match.name} Pos: {template_match.position}, Score: {template_match.score}")
|
||||
x, y = template_match.position
|
||||
print(f"Name: {template_match.name} Pos: {template_match.position}, Dist: {625-x, 360-y}, Score: {template_match.score}")
|
||||
# print(time.time() - start)
|
||||
display_img = cv2.resize(display_img, None, fx=0.5, fy=0.5, interpolation=cv2.INTER_NEAREST)
|
||||
cv2.imshow('test', display_img)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
from .a2 import A2
|
||||
from .a3 import A3
|
||||
from .a4 import A4
|
||||
from .a5 import A5
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
from char import IChar
|
||||
from town.i_act import IAct
|
||||
from screen import Screen
|
||||
from config import Config
|
||||
from npc_manager import NpcManager, Npc
|
||||
from pather import Pather, Location
|
||||
from typing import Union
|
||||
from template_finder import TemplateFinder
|
||||
from utils.misc import wait
|
||||
|
||||
|
||||
class A2(IAct):
|
||||
def __init__(self, screen: Screen, template_finder: TemplateFinder, pather: Pather, char: IChar, npc_manager: NpcManager):
|
||||
self._config = Config()
|
||||
self._screen = screen
|
||||
self._pather = pather
|
||||
self._char = char
|
||||
self._npc_manager = npc_manager
|
||||
self._template_finder = template_finder
|
||||
|
||||
def get_wp_location(self) -> Location: return Location.A2_WP
|
||||
def can_stash(self) -> bool: return True
|
||||
def can_buy_pots(self) -> bool: return False
|
||||
def can_heal(self) -> bool: return True
|
||||
def can_trade_and_repair(self) -> bool: return True
|
||||
|
||||
def heal(self, curr_loc: Location) -> Union[Location, bool]:
|
||||
if not self._pather.traverse_nodes((curr_loc, Location.A2_FARA_STASH), self._char): return False
|
||||
self._npc_manager.open_npc_menu(Npc.FARA)
|
||||
return Location.A2_FARA_STASH
|
||||
|
||||
def open_stash(self, curr_loc: Location) -> Union[Location, bool]:
|
||||
if not self._pather.traverse_nodes((curr_loc, Location.A2_FARA_STASH), self._char):
|
||||
return False
|
||||
wait(0.3)
|
||||
def stash_is_open_func():
|
||||
img = self._screen.grab()
|
||||
found = self._template_finder.search("INVENTORY_GOLD_BTN", img, roi=self._config.ui_roi["gold_btn"]).valid
|
||||
found |= self._template_finder.search("INVENTORY_GOLD_BTN", img, roi=self._config.ui_roi["gold_btn_stash"]).valid
|
||||
return found
|
||||
if not self._char.select_by_template(["A2_STASH_LIGHT", "A2_STASH_DARK"], stash_is_open_func, telekinesis=True):
|
||||
return False
|
||||
return Location.A2_FARA_STASH
|
||||
|
||||
def open_trade_and_repair_menu(self, curr_loc: Location) -> Union[Location, bool]:
|
||||
if not self._pather.traverse_nodes((curr_loc, Location.A2_FARA_STASH), self._char): return
|
||||
self._npc_manager.open_npc_menu(Npc.FARA)
|
||||
self._npc_manager.press_npc_btn(Npc.FARA, "trade_repair")
|
||||
return Location.A2_FARA_STASH
|
||||
|
||||
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: self._template_finder.search("WAYPOINT_MENU", self._screen.grab()).valid
|
||||
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 = self._template_finder.search_and_wait(["A2_TOWN_21", "A2_TOWN_22", "A2_TOWN_20", "A2_TOWN_19"], time_out=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
|
||||
return False
|
||||
+2
-3
@@ -7,7 +7,6 @@ from pather import Pather, Location
|
||||
from typing import Union
|
||||
from template_finder import TemplateFinder
|
||||
from utils.misc import wait
|
||||
from utils.custom_mouse import mouse
|
||||
|
||||
|
||||
class A3(IAct):
|
||||
@@ -44,7 +43,7 @@ class A3(IAct):
|
||||
found = self._template_finder.search("INVENTORY_GOLD_BTN", img, roi=self._config.ui_roi["gold_btn"]).valid
|
||||
found |= self._template_finder.search("INVENTORY_GOLD_BTN", img, roi=self._config.ui_roi["gold_btn_stash"]).valid
|
||||
return found
|
||||
if not self._char.select_by_template("A3_STASH", stash_is_open_func):
|
||||
if not self._char.select_by_template("A3_STASH", stash_is_open_func, telekinesis=True):
|
||||
return False
|
||||
return Location.A3_STASH_WP
|
||||
|
||||
@@ -52,7 +51,7 @@ class A3(IAct):
|
||||
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: self._template_finder.search("WAYPOINT_MENU", self._screen.grab()).valid
|
||||
return self._char.select_by_template("A3_WP", found_wp_func)
|
||||
return self._char.select_by_template("A3_WP", found_wp_func, telekinesis=True)
|
||||
|
||||
def wait_for_tp(self) -> Union[Location, bool]:
|
||||
template_match = self._template_finder.search_and_wait("A3_TOWN_10", time_out=20)
|
||||
|
||||
+1
-1
@@ -34,7 +34,7 @@ class A4(IAct):
|
||||
wait(0.5, 0.7)
|
||||
found_wp_func = lambda: self._template_finder.search("WAYPOINT_MENU", self._screen.grab()).valid
|
||||
# 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)
|
||||
return self._char.select_by_template(["A4_WP", "A4_WP_2"], found_wp_func, threshold=0.62, telekinesis=True)
|
||||
|
||||
def wait_for_tp(self) -> Union[Location, bool]:
|
||||
success = self._template_finder.search_and_wait(["A4_TOWN_4", "A4_TOWN_5", "A4_TOWN_6"], time_out=20).valid
|
||||
|
||||
+2
-2
@@ -62,7 +62,7 @@ class A5(IAct):
|
||||
found = self._template_finder.search("INVENTORY_GOLD_BTN", img, roi=self._config.ui_roi["gold_btn"]).valid
|
||||
found |= self._template_finder.search("INVENTORY_GOLD_BTN", img, roi=self._config.ui_roi["gold_btn_stash"]).valid
|
||||
return found
|
||||
if not self._char.select_by_template(["A5_STASH", "A5_STASH_2"], stash_is_open_func):
|
||||
if not self._char.select_by_template(["A5_STASH", "A5_STASH_2"], stash_is_open_func, telekinesis=True):
|
||||
return False
|
||||
return Location.A5_STASH
|
||||
|
||||
@@ -76,7 +76,7 @@ class A5(IAct):
|
||||
if not self._pather.traverse_nodes((curr_loc, Location.A5_WP), self._char): return False
|
||||
wait(0.5, 0.7)
|
||||
found_wp_func = lambda: self._template_finder.search("WAYPOINT_MENU", self._screen.grab()).valid
|
||||
return self._char.select_by_template("A5_WP", found_wp_func)
|
||||
return self._char.select_by_template("A5_WP", found_wp_func, telekinesis=True)
|
||||
|
||||
def wait_for_tp(self) -> Union[Location, bool]:
|
||||
success = self._template_finder.search_and_wait(["A5_TOWN_1", "A5_TOWN_0"], time_out=20).valid
|
||||
|
||||
@@ -5,17 +5,18 @@ from config import Config
|
||||
from pather import Location
|
||||
from logger import Logger
|
||||
from ui import UiManager
|
||||
from town import IAct, A3, A4, A5
|
||||
from town import IAct, A2, A3, A4, A5
|
||||
from utils.misc import wait
|
||||
|
||||
|
||||
class TownManager:
|
||||
def __init__(self, template_finder: TemplateFinder, ui_manager: UiManager, item_finder: ItemFinder, a3: A3, a4: A4, a5: A5):
|
||||
def __init__(self, template_finder: TemplateFinder, ui_manager: UiManager, item_finder: ItemFinder, a2: A2, a3: A3, a4: A4, a5: A5):
|
||||
self._config = Config()
|
||||
self._template_finder = template_finder
|
||||
self._ui_manager = ui_manager
|
||||
self._item_finder = item_finder
|
||||
self._acts: dict[Location, IAct] = {
|
||||
Location.A2_TOWN_START: a2,
|
||||
Location.A3_TOWN_START: a3,
|
||||
Location.A4_TOWN_START: a4,
|
||||
Location.A5_TOWN_START: a5
|
||||
@@ -30,6 +31,8 @@ class TownManager:
|
||||
location = Location.A4_TOWN_START
|
||||
elif loc.upper().startswith("A3_"):
|
||||
location = Location.A3_TOWN_START
|
||||
elif loc.upper().startswith("A2_"):
|
||||
location = Location.A2_TOWN_START
|
||||
return location
|
||||
|
||||
def wait_for_town_spawn(self, time_out: float = None) -> Location:
|
||||
@@ -40,7 +43,8 @@ class TownManager:
|
||||
template_match = self._template_finder.search_and_wait([
|
||||
"A5_TOWN_0", "A5_TOWN_1",
|
||||
"A4_TOWN_4", "A4_TOWN_5",
|
||||
"A3_TOWN_0", "A3_TOWN_1"
|
||||
"A3_TOWN_0", "A3_TOWN_1",
|
||||
"A2_TOWN_0", "A2_TOWN_1", "A2_TOWN_10"
|
||||
], best_match=True, time_out=time_out)
|
||||
if template_match.valid:
|
||||
return TownManager.get_act_from_location(template_match.name)
|
||||
@@ -60,7 +64,8 @@ class TownManager:
|
||||
curr_act = TownManager.get_act_from_location(curr_loc)
|
||||
if curr_act is None: return False
|
||||
# check if we already are in the desired act
|
||||
if act_idx == 3: act = Location.A3_TOWN_START
|
||||
if act_idx == 2: act = Location.A2_TOWN_START
|
||||
elif act_idx == 3: act = Location.A3_TOWN_START
|
||||
elif act_idx == 4: act = Location.A4_TOWN_START
|
||||
elif act_idx == 5: act = Location.A5_TOWN_START
|
||||
else:
|
||||
@@ -179,5 +184,6 @@ if __name__ == "__main__":
|
||||
a5 = A5(screen, template_finder, pather, char, npc_manager)
|
||||
a4 = A4(screen, template_finder, pather, char, npc_manager)
|
||||
a3 = A3(screen, template_finder, pather, char, npc_manager)
|
||||
town_manager = TownManager(template_finder, ui_manager, item_finder, a3, a4, a5)
|
||||
print(town_manager.stash(Location.A5_STASH))
|
||||
a2 = A2(screen, template_finder, pather, char, npc_manager)
|
||||
town_manager = TownManager(template_finder, ui_manager, item_finder, a2, a3, a4, a5)
|
||||
print(town_manager.open_wp(Location.A2_TOWN_START))
|
||||
|
||||
@@ -189,4 +189,4 @@ if __name__ == "__main__":
|
||||
ui_manager = UiManager(screen, template_finder)
|
||||
belt_manager = BeltManager(screen, template_finder)
|
||||
belt_manager.update_pot_needs()
|
||||
print(belt_manager._pot_needs)
|
||||
print(belt_manager._pot_needs)
|
||||
@@ -34,7 +34,7 @@ class UiManager():
|
||||
:param act: Index of the desired act starting at 1 [A1 = 1, A2 = 2, A3 = 3, ...]
|
||||
:param idx: Index of the waypoint from top. Note that it start at 0!
|
||||
"""
|
||||
str_to_idx_map = {"WP_A3_ACTIVE": 3, "WP_A4_ACTIVE": 4, "WP_A5_ACTIVE": 5}
|
||||
str_to_idx_map = {"WP_A2_ACTIVE": 2, "WP_A3_ACTIVE": 3, "WP_A4_ACTIVE": 4, "WP_A5_ACTIVE": 5}
|
||||
template_match = self._template_finder.search([*str_to_idx_map], self._screen.grab(), threshold=0.7, best_match=True, roi=self._config.ui_roi["wp_act_roi"])
|
||||
curr_active_act = str_to_idx_map[template_match.name] if template_match.valid else -1
|
||||
if curr_active_act != act:
|
||||
|
||||
@@ -47,7 +47,7 @@ class NodeRecorder:
|
||||
def find_templates(self, img):
|
||||
ref_points = {}
|
||||
for key in self._template_finder._templates:
|
||||
found = self._template_finder.search(key, img, use_grayscale=True)
|
||||
found = self._template_finder.search(key, img, use_grayscale=False, threshold=0.77)
|
||||
if found.valid:
|
||||
ref_points[key] = found.position
|
||||
return ref_points
|
||||
@@ -127,7 +127,7 @@ class NodeRecorder:
|
||||
results = sorted(new_path, key=lambda r: r["dist"])
|
||||
code = f"{k}: " + "{"
|
||||
for i, res in enumerate(results):
|
||||
if res["dist"] < 900 and i < 5:
|
||||
if res["dist"] < 1100 and i < 8:
|
||||
code += f'"{res["key"].upper()}": {res["pos"]}, '
|
||||
f.write(code + "}\n")
|
||||
f.close()
|
||||
@@ -156,5 +156,6 @@ if __name__ == "__main__":
|
||||
cv2.putText(img, key, recorder.ref_points[key], cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2, cv2.LINE_AA)
|
||||
except Exception:
|
||||
pass
|
||||
img = cv2.resize(img, None, fx=0.5, fy=0.5)
|
||||
cv2.imshow("vis", img)
|
||||
cv2.waitKey(1)
|
||||
|
||||
Reference in New Issue
Block a user