diff --git a/.gitignore b/.gitignore index 14f13cd26..bba35ad3a 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,5 @@ stats/ .coverage htmlcov/ coverage.xml +params.ini +environment.yml diff --git a/config/params.ini b/config/params.ini index 472b5e1fe..c7050b172 100644 --- a/config/params.ini +++ b/config/params.ini @@ -19,13 +19,13 @@ restart_d2r_when_stuck=0 [routes] run_trav=0 -run_pindle=1 -run_eldritch=1 +run_pindle=0 +run_eldritch=0 run_shenk=0 run_nihlathak=0 run_arcane=0 run_diablo=0 -run_cows=0 +run_cows=1 ; Note: You can not split up or change order of eldritch and shenk as botty treats it as a single run order=run_cows, run_trav, run_diablo, run_pindle, run_eldritch, run_shenk, run_nihlathak, run_arcane, @@ -34,27 +34,27 @@ order=run_cows, run_trav, run_diablo, run_pindle, run_eldritch, run_shenk, run_n ; ==== Mandatory Fields ==== ; ========================== ; These configs have to be alligned with your d2r settings and char build -type=light_sorc -casting_frames=10 -num_loot_columns=5 +type=hammerdin +casting_frames=9 +num_loot_columns=4 show_items=alt inventory_screen=i -force_move=e -tp=f9 +force_move=j +tp=h belt_rows=4 minimap=tab ;note: stand_still can not be the default "shift" as it would interfere with merc healing stand_still=capslock ;note: this is different from the default hotkey as "~" is for many keyboards not reachable without also pressing altgr -show_belt=k +show_belt=~ potion1=1 potion2=2 potion3=3 potion4=4 cta_available=0 -weapon_switch=x -battle_orders=f7 -battle_command=f8 +weapon_switch=z +battle_orders=f11 +battle_command=f12 ; ========================== ; ==== Optional configs ==== ; ========================== @@ -135,12 +135,12 @@ nova=f2 ; ==== Builds: Paladin ==== ; ========================= [hammerdin] -teleport=f1 -concentration=f2 -holy_shield=f4 -blessed_hammer=f5 -vigor= -redemption= +teleport=r +concentration=e +holy_shield=t +blessed_hammer=q +vigor=f8 +redemption=f10 cleansing= ; ========================== diff --git a/environment.yml b/environment.yml index ea0001115..12f965e9b 100644 --- a/environment.yml +++ b/environment.yml @@ -1,4 +1,4 @@ -name: botty +name: bottycthu channels: - conda-forge dependencies: diff --git a/src/bot.py b/src/bot.py index dec83feb0..7b424fd75 100644 --- a/src/bot.py +++ b/src/bot.py @@ -114,7 +114,7 @@ def __init__(self, screen: Screen, game_stats: GameStats, template_finder: Templ self._nihlathak = Nihlathak(self._screen, self._template_finder, self._pather, self._town_manager, self._ui_manager, self._char, self._pickit) self._arcane = Arcane(self._screen, self._template_finder, self._pather, self._town_manager, self._ui_manager, self._char, self._pickit) self._diablo = Diablo(self._screen, self._template_finder, self._pather, self._town_manager, self._ui_manager, self._char, self._pickit) - self._cows = Cows(self._screen, self._template_finder, self._pather, self._town_manager, self._ui_manager, self._char, self._pickit) + self._cows = Cows(self._screen, self._template_finder, self._pather, self._town_manager, self._ui_manager, self._char, self._pickit, npc_manager) # Create member variables self._pick_corpse = pick_corpse diff --git a/src/run/cows.py b/src/run/cows.py index 872d47eb7..70013b0c5 100644 --- a/src/run/cows.py +++ b/src/run/cows.py @@ -11,12 +11,16 @@ from typing import Union from item.pickit import PickIt from template_finder import TemplateFinder -from town.town_manager import TownManager +from town import TownManager #for buying tomes & interacting with Stash +from npc_manager import NpcManager, Npc #for buying tomes from ui import UiManager from utils.misc import wait from utils.custom_mouse import mouse from screen import Screen +from town import IAct, A1, A2, A3, A4, A5 +#from game_stats import GameStats import numpy as np +#from transmute import Transmute class Cows: @@ -28,7 +32,11 @@ def __init__( town_manager: TownManager, ui_manager: UiManager, char: IChar, - pickit: PickIt + pickit: PickIt, + npc_manager: NpcManager, + #game_stats: GameStats = None, + #transmute: Transmute + ): self._config = Config() self._screen = screen @@ -36,106 +44,134 @@ def __init__( self._pather = pather self._town_manager = town_manager self._ui_manager = ui_manager + self._npc_manager = npc_manager self._char = char self._pickit = pickit self._picked_up_items = False self.used_tps = 0 + #self._transmute= transmute + #self._game_stats = game_stats + #self._transmuter = Transmute(self._screen, self._template_finder, self._game_stats, self._ui_manager) #for transmute - #thus function randomly teleports around until we either get stuck or find the exit we search for - def _scout(self, corner_picker, x1_m, x2_m, y1_m, y2_m, stuck_count, super_stuck, corner_exclude, exclude1, exclude2, keepernumber)-> bool: - found = False - templates_scout = ["COW_STONY_FIELD_YELLOW"] - templates_avoid = ["COW_COLD_PLAINS", "COW_COLD_PLAINS_TRANSPARENT"] + #this function randomly teleports around until we either get stuck or find the exit we search for + def _scout(self, corner_picker, x1_m, x2_m, y1_m, y2_m, stuck_count, super_stuck, corner_exclude, exclude1, exclude2, keepernumber, found)-> bool: + + templates_scout = ["COW_STONY_FIELD_YELLOW"] #the template we scout for on the minimap, either an exit (purple) or portal (yellow) + templates_avoid = ["COW_COLD_PLAINS0"] #in case our scout can bring us to other locations, we add the "Entering ..." message here, so we know when we accidently leave the area + avoid = self._template_finder.search_and_wait(templates_avoid, best_match=True, threshold=0.8, time_out=0.1, use_grayscale=False).valid # boolean of avoid template + + #lets make sure our minimap is on Logger.debug('\033[93m' + "Scout: Starting to explore map" + '\033[0m') - if not self._template_finder.search_and_wait(["MAP_CHECK"], best_match=True, threshold=0.5, time_out=0.1, use_grayscale=False).valid: + if not self._template_finder.search_and_wait(["MAP_CHECK"], best_match=True, threshold=0.5, time_out=0.1, use_grayscale=False).valid: #check if the minimap is already on keyboard.send(self._char._skill_hotkeys["teleport"]) #switch active skill to teleport keyboard.send(self._config.char["minimap"]) #turn on minimap Logger.debug('\033[93m' + "Scout: Opening Minimap" + '\033[0m') - avoid = self._template_finder.search_and_wait(templates_avoid, best_match=True, threshold=0.9, time_out=0.1, use_grayscale=False).valid - + #so lets loop teleporting around until we find our templates while not found and not avoid: - found = self._template_finder.search_and_wait(templates_scout, best_match=True, threshold=0.9, time_out=0.1, use_grayscale=False).valid - avoid = self._template_finder.search_and_wait(templates_avoid, best_match=True, threshold=0.9, time_out=0.1, use_grayscale=False).valid - founder = self._template_finder.search_and_wait(templates_scout, best_match=True, threshold=0.9, time_out=0.1, use_grayscale=False) - + found = self._template_finder.search_and_wait(templates_scout, best_match=True, threshold=0.9, time_out=0.1, use_grayscale=False).valid #boolean, if we found it + founder = self._template_finder.search_and_wait(templates_scout, best_match=True, threshold=0.9, time_out=0.1, use_grayscale=False) #template + avoid = self._template_finder.search_and_wait(templates_avoid, best_match=True, threshold=0.8, time_out=0.1, use_grayscale=False).valid #have to repeat it from above, so its defined in the loop + + #we check if we exited our scouting location and take measures if this is the case if avoid: Logger.debug('\033[93m' + "Scout: Ended up in Cold Plains, stopping to move" + '\033[0m') + #we might have to teleport backwards 3 times & change corners. return False + # we break the loop if the template is found & return its x, y position to pos_m if founder.valid: pos_m = self._screen.convert_screen_to_monitor(founder.center) Logger.debug('\033[93m' + "Scout: target template is at position:" + str(pos_m) + '\033[0m') break - - pos_m = self._screen.convert_abs_to_monitor((random.uniform(x1_m, x2_m), random.uniform(y1_m, y2_m))) - t0 = self._screen.grab() - self._char.move(pos_m, force_tp=True, force_move=True) - t1 = self._screen.grab() + + #teleporting part + pos_m = self._screen.convert_abs_to_monitor((random.uniform(x1_m, x2_m), random.uniform(y1_m, y2_m))) # randomize our position + t0 = self._screen.grab() # take screenshot before teleport + self._char.move(pos_m, force_tp=True, force_move=True) # first of two teleports to randomized position + t1 = self._screen.grab() # take screenshot after teleport + + # check by making a diff of the screenshots, if we moved between the teleports diff = cv2.absdiff(t0, t1) diff = cv2.cvtColor(diff, cv2.COLOR_BGR2GRAY) _, mask = cv2.threshold(diff, 13, 255, cv2.THRESH_BINARY) - score = (float(np.sum(mask)) / mask.size) * (1/255.0) + score = (float(np.sum(mask)) / mask.size) * (1/255.0) #score to check how much difference there is between screenshots (high = movement, low = stuck) Logger.debug("Scout: " + str(score) + ": is our current score") - self._char.move(pos_m, force_tp=True, force_move=True) + pos_m = self._screen.convert_abs_to_monitor((random.uniform(x1_m, x2_m), random.uniform(y1_m, y2_m))) # randomize our position + self._char.move(pos_m, force_tp=True, force_move=True) # second of two teleports to randomized position + + #if we didnt move, we need to unstuck ourselves if score < .10: + #lets change scouting direction: + Logger.debug('\033[93m' + "Scout: score was too low, we seem stuck, changing direction from" + str(corner_picker) + '\033[0m') + self._corner_roller(corner_picker, x1_m, x2_m, y1_m, y2_m, stuck_count, super_stuck, corner_exclude, exclude1, exclude2, keepernumber, found) + """ stuck_count += 1 Logger.debug('\033[93m' + "Scout: We did not move, stuck count now: " + str(stuck_count) + '\033[0m') + #we are still stuck, so lets call the stuck() function to go reverse & free ourselves if stuck_count >=2: - """ super_stuck += 1 Logger.debug('\033[93m' + "Scout: We still did not move, stuck count now: " + str(stuck_count) + ", super_stuck count now: " + str(super_stuck) + ", calling stuck()" + '\033[0m') self.stuck(corner_picker, x2_m, y2_m, stuck_count, super_stuck) + + #we were not able to unstuck oursevles using the stuck() function, we thus switch the direction (corner) in which we teleport. if super_stuck >= 2: - """ Logger.debug('\033[93m' + "Scout: We seem super stuck, super_stuck count now: " + str(super_stuck) + ", calling super_stuck() to change direction" + '\033[0m') - self.super_stuck(corner_picker, x1_m, x2_m, y1_m, y2_m, stuck_count, super_stuck, corner_exclude, exclude1, exclude2, keepernumber) + self.super_stuck(corner_picker, x1_m, x2_m, y1_m, y2_m, stuck_count, super_stuck, corner_exclude, exclude1, exclude2, keepernumber, found) + """ + + #we found our template and should leave the scout() function. if found == True: Logger.debug('\033[93m' + "Scout: Found our Template, trying to click the exit template, calling exitclicker()" + '\033[0m') self._exitclicker(pos_m) + return True # this function is called when we are stuck and teleports backwards to get us unstuck def stuck(self, corner_picker, x2_m, y2_m, stuck_count, super_stuck)-> bool: - tele_math = random.randint(1, 3) + tele_math = random.randint(1, 3) #lets roll a 3-sided dice to decide in which direction we should go now. if math.fmod(tele_math, 3) == 0: Logger.debug('\033[94m' + "Stuck: " + str(corner_picker) + ": Seems we are stuck (telemath: "+ str(tele_math) +"), let's go reverse 2 x 3, first x3 to: ("+str(x2_m)+","+str(y2_m)+")" + '\033[0m') + # we go opposite of our last direction pos_m = self._screen.convert_abs_to_monitor((x2_m * -1, y2_m * -1)) self._char.move(pos_m, force_tp=True) self._char.move(pos_m, force_tp=True) self._char.move(pos_m, force_tp=True) + # and now we change direction along Y Logger.debug('\033[94m' + "Stuck: " + str(corner_picker) + ": Seems we are stuck, let's go reverse 2 x 3, second x3 to: ("+str(x2_m)+","+str(y2_m)+")" + '\033[0m') pos_m = self._screen.convert_abs_to_monitor((x2_m * -1, y2_m)) self._char.move(pos_m, force_tp=True) self._char.move(pos_m, force_tp=True) self._char.move(pos_m, force_tp=True) stuck_count += 1 - super_stuck += 3 #was 1, i want her to change corners earlier + super_stuck += 1 Logger.debug('\033[94m' + "Stuck: " + str(corner_picker) + ": stuck_count: "+str(stuck_count)+", super_stuck count: "+str(super_stuck)+ '\033[0m') else: + # we go opposite of our last direction Logger.debug('\033[94m' + "Stuck: " + str(corner_picker) + ": Seems we are stuck (telemath: "+ str(tele_math) +"), let's go reverse 2 x 3, first x3 to: ("+str(x2_m)+","+str(y2_m)+")" + '\033[0m') pos_m = self._screen.convert_abs_to_monitor((x2_m * -1, y2_m * -1)) self._char.move(pos_m, force_tp=True) self._char.move(pos_m, force_tp=True) self._char.move(pos_m, force_tp=True) + # and now we change direction along X Logger.debug('\033[94m' + "Stuck: " + str(corner_picker) + ": Seems we are stuck, let's go reverse 2 x 3, second x3 to: ("+str(x2_m)+","+str(y2_m)+")" + '\033[0m') pos_m = self._screen.convert_abs_to_monitor((x2_m, y2_m * -1)) self._char.move(pos_m, force_tp=True) self._char.move(pos_m, force_tp=True) self._char.move(pos_m, force_tp=True) stuck_count = 0 - super_stuck += 3 #was 1, i want her to change corners earlier + super_stuck += 1 Logger.debug('\033[94m' + "Stuck: " + str(corner_picker) + ": stuck_count: "+str(stuck_count)+", super_stuck count: "+str(super_stuck)+ '\033[0m') # this function is called when we cannot unstuck ourselves using stuck() and need to change directions - def super_stuck(self, corner_picker, x1_m, x2_m, y1_m, y2_m, stuck_count, super_stuck, corner_exclude, exclude1, exclude2, keepernumber)-> bool: + def super_stuck(self, corner_picker, x1_m, x2_m, y1_m, y2_m, stuck_count, super_stuck, corner_exclude, exclude1, exclude2, keepernumber, found)-> bool: Logger.debug('\033[95m' + "Super_Stuck: Got super stuck in corner " + str(corner_picker) + '\033[0m') - self._corner_roller(corner_picker, x1_m, x2_m, y1_m, y2_m, stuck_count, super_stuck, corner_exclude, exclude1, exclude2, keepernumber) + self._corner_roller(corner_picker, x1_m, x2_m, y1_m, y2_m, stuck_count, super_stuck, corner_exclude, exclude1, exclude2, keepernumber, found) #this function decides which corner to explore & hands over the x,y coordinate ranges - def _corner_roller(self, corner_picker, x1_m, x2_m, y1_m, y2_m, stuck_count, super_stuck, corner_exclude, exclude1, exclude2, keepernumber)-> bool: + def _corner_roller(self, corner_picker, x1_m, x2_m, y1_m, y2_m, stuck_count, super_stuck, corner_exclude, exclude1, exclude2, keepernumber, found)-> bool: keepernumber = random.randint(1, 4) if keepernumber == corner_exclude or keepernumber == corner_picker or keepernumber == exclude1 or keepernumber == exclude2: keepernumber = random.randint(1, 4) @@ -147,40 +183,45 @@ def _corner_roller(self, corner_picker, x1_m, x2_m, y1_m, y2_m, stuck_count, sup super_stuck = 0 stuck_count = 0 if corner_picker == 1: - Logger.debug('\033[92m' + "Cornerpicker: Picked Corner 1 = top-left" + '\033[0m') - #x1_m = -250 - #x2_m = -600 - #y1_m = -200 - #y2_m = -345 - self._scout(1, -250, -600, -200, -300, stuck_count, super_stuck, corner_exclude, exclude1, exclude2, keepernumber) #top - left + Logger.debug('\033[92m' + "Cornerpicker: Picked Corner 1 = top-left | left" + '\033[0m') + #x1_m = 325 + #x2_m = -325 + #y1_m = 225 + #y2_m = -225 + #self._scout(1, -250, -600, -200, -300, stuck_count, super_stuck, corner_exclude, exclude1, exclude2, keepernumber) #top - left + #self._scout(1, 225, -325, 125, -225, stuck_count, super_stuck, corner_exclude, exclude1, exclude2, keepernumber) # random with tendency top - left + self._scout(1, 0, -525, 0, -0, stuck_count, super_stuck, corner_exclude, exclude1, exclude2, keepernumber, found) # left elif corner_picker == 2: - Logger.debug('\033[92m' + "Cornerpicker: Picked Corner 2 = top-right" + '\033[0m') - self._scout(2, 250, 600, -200, -300, stuck_count, super_stuck, corner_exclude, exclude1, exclude2, keepernumber) # top - right + Logger.debug('\033[92m' + "Cornerpicker: Picked Corner 2 = top-right | right" + '\033[0m') + #self._scout(2, 250, 600, -200, -300, stuck_count, super_stuck, corner_exclude, exclude1, exclude2, keepernumber) # top - right + self._scout(2, 525, 0, 0, -0, stuck_count, super_stuck, corner_exclude, exclude1, exclude2, keepernumber, found) # right elif corner_picker == 3: - Logger.debug('\033[92m' + "Cornerpicker: Picked Corner 3 = bottom-right" + '\033[0m') - self._scout(3, 485, 600, 200, 300, stuck_count, super_stuck, corner_exclude, exclude1, exclude2, keepernumber) # bottom - right + Logger.debug('\033[92m' + "Cornerpicker: Picked Corner 3 = bottom-right | top " + '\033[0m') + #self._scout(3, 485, 600, 200, 300, stuck_count, super_stuck, corner_exclude, exclude1, exclude2, keepernumber) # bottom - right + self._scout(3, 0, -0, 0, -325, stuck_count, super_stuck, corner_exclude, exclude1, exclude2, keepernumber, found) # top elif corner_picker == 4: - Logger.debug('\033[92m' + "Cornerpicker: Picked Corner 4 = bottom-left" + '\033[0m') - self._scout(4, -485, -600, 200, 300, stuck_count, super_stuck, corner_exclude, exclude1, exclude2, keepernumber) # bottom - left + Logger.debug('\033[92m' + "Cornerpicker: Picked Corner 4 = bottom-left | bottom" + '\033[0m') + #self._scout(4, -485, -600, 200, 300, stuck_count, super_stuck, corner_exclude, exclude1, exclude2, keepernumber) # bottom - left + self._scout(4, 0, -0, 325, 0, stuck_count, super_stuck, corner_exclude, exclude1, exclude2, keepernumber, found) # bottom #return corner_picker, x1_m, x2_m, y1_m, y2_m, stuck_count, super_stuck, corner_exclude, exclude1, exclude2, keepernumber # This function makes us click on the purple exit tile to enter the next level def _exitclicker(self, pos_m)-> bool: - Logger.debug('\033[92m' + "Exit_Clicker: Trying to approach the Exit" + '\033[0m') + Logger.debug('\033[92m' + "Exit_Clicker: Step1: Trying to approach the Exit" + '\033[0m') closetoexit = False stuck_count = 0 super_stuck = 0 templates_scout = ["COW_STONY_FIELD_YELLOW"] templates_mapcheck = ["MAP_CHECK"] templates_exit = ["COW_STONY_FIELD_PORTAL_1", "COW_STONY_FIELD_PORTAL_0", "COW_STONY_FIELD_PORTAL_2"] - templates_nextlevel = ["COW_TRIST_0", "COW_TRIST_2", "COW_TRIST_3"] + templates_nextlevel = ["COW_TRIST_0", "COW_TRIST_2", "COW_TRIST_3", "COW_TRIST_4"] mapcheck = self._template_finder.search_and_wait(templates_mapcheck, best_match=True, threshold=0.5, time_out=0.1, use_grayscale=False) template_match = self._template_finder.search_and_wait(templates_exit, best_match=True, threshold=0.9, time_out=0.1, use_grayscale=False) if template_match.valid and mapcheck.valid: pos_m = self._screen.convert_screen_to_monitor(template_match.center) if self._template_finder.search_and_wait(templates_mapcheck, best_match=True, threshold=0.5, time_out=0.1, use_grayscale=False).valid: - Logger.debug('\033[92m' + "Exit_Clicker: Template Found, Minimap off" + '\033[0m') + Logger.debug('\033[92m' + "Exit_Clicker: Step2: Minimap Scout Template Found, Minimap off" + '\033[0m') keyboard.send(self._char._skill_hotkeys["teleport"]) #switch active skill to teleport keyboard.send(self._config.char["minimap"]) #turn on minimap @@ -191,10 +232,11 @@ def _exitclicker(self, pos_m)-> bool: if self._template_finder.search_and_wait(templates_mapcheck, best_match=True, threshold=0.5, time_out=0.1, use_grayscale=False).valid: keyboard.send(self._char._skill_hotkeys["teleport"]) #switch active skill to teleport keyboard.send(self._config.char["minimap"]) #turn on minimap - Logger.debug('\033[92m' + "Exit_Clicker: Template Found, Minimap off" + '\033[0m') + Logger.debug('\033[92m' + "Exit_Clicker: Step3a: Minimap off to identify Exit Templates" + '\033[0m') if self._template_finder.search_and_wait(templates_exit, best_match=True, threshold=0.9, time_out=0.1, use_grayscale=False) == True: pos_m = self._screen.convert_screen_to_monitor(template_match.center) - pos_m2 = (template_match.center)#SCREEN + pos_m = template_match.center + Logger.debug('\033[92m' + "Exit_Clicker: Step3b (in while): Found Exit Template, clicking on it" + '\033[0m') t0 = self._screen.grab() self._char.move(pos_m, force_tp=True, force_move=True) t1 = self._screen.grab() @@ -215,11 +257,12 @@ def _exitclicker(self, pos_m)-> bool: elif super_stuck >= 3 and not closetoexit: Logger.debug('\033[92m' + "Exit_Clicker: Super Stuck, super_stuck count: " + str(super_stuck) + '\033[0m') self.exit_super_stuck(pos_m, stuck_count, super_stuck, closetoexit) - Logger.debug('\033[93m' + "Exit_Clicker: Found Exit" + '\033[0m') + + Logger.debug('\033[92m' + "Exit_Clicker: Step4a: Found Exit Template, clicking on it" + '\033[0m') found_loading_screen_func = lambda: self._ui_manager.wait_for_loading_screen(2.0) if not self._char.select_by_template(templates_exit, found_loading_screen_func, threshold=0.7, time_out=4, telekinesis=True): # do a random tele jump and try again - Logger.debug('\033[93m' + "Exit_Clicker: Found Exit, but didnt click it" + '\033[0m') + Logger.debug('\033[92m' + "Exit_Clicker: Step4b: Found Exit, but didn't manage to click it, moving left and right and top" + '\033[0m') pos_m = self._screen.convert_abs_to_monitor((315, -150)) self._char.move(pos_m, force_move=True) if not self._char.select_by_template(templates_exit, found_loading_screen_func, threshold=0.7, time_out=4, telekinesis=True): @@ -227,18 +270,22 @@ def _exitclicker(self, pos_m)-> bool: if not self._char.select_by_template(templates_exit, found_loading_screen_func, threshold=0.7, time_out=4, telekinesis=True): pos_m = self._screen.convert_abs_to_monitor((-315, -100)) self._char.move(pos_m, force_move=True) - Logger.debug('\033[92m' + "Exit_Clicker: Found Exit, but cannot click it" + '\033[0m') + Logger.debug('\033[92m' + "Exit_Clicker: Step4c: Found Exit, but cannot click it, retry" + '\033[0m') if not self._char.select_by_template(templates_exit, found_loading_screen_func, threshold=0.7, time_out=4,telekinesis=True): - Logger.debug('\033[92m' + "Exit_Clicker: Found Exit, but didnt click it repeatedly, aborting run" + '\033[0m') + Logger.debug('\033[92m' + "Exit_Clicker: Step4d: Found Exit, but didn't manage to click it repeatedly, aborting run" + '\033[0m') return False + if not self._template_finder.search_and_wait(templates_nextlevel, threshold=0.8, time_out=.5).valid: if not self._template_finder.search_and_wait(templates_nextlevel, threshold=0.8, time_out=1).valid: - self._scout(4, -250, -400, 200, 300, 0, 0, 4, 2, 2, 4) # bottom - left + Logger.debug('\033[92m' + "Exit_Clicker: Step5: CANNOT confirm to be at the right new location, starting again to scout()" + '\033[0m') + self._scout(4, -250, -400, 200, 300, 0, 0, 4, 2, 2, 4, False) # bottom - left else: + Logger.debug('\033[92m' + "Exit_Clicker: Step5: Confirmed to be at the right new location, switching off minimap, stopping to scout()" + '\033[0m') + wait(2) keyboard.send(self._char._skill_hotkeys["teleport"]) #switch active skill to teleport keyboard.send(self._config.char["minimap"]) #turn on minimap return True - self._tristram() + #self._tristram() #this function helps us to unstuck ourselves, when we already found our exit tile, but got stuck entering the exit. @@ -380,63 +427,29 @@ def exit_super_stuck(self, pos_m, stuck_count, super_stuck, closetoexit)-> bool: stuck_count = 0 super_stuck = 0 - - #this function checks for the leg in inventory, stash & cube. - def _legcheck(self) -> bool: - Logger.debug('\033[96m' + "Checking Inventory for Leg" + '\033[0m') - keyboard.send(self._config.char["inventory_screen"]) - template_match = self._template_finder.search_and_wait(["LEG_INVENTORY"], best_match=True, threshold=0.9, time_out=0.5, use_grayscale=False) - keyboard.send(self._config.char["inventory_screen"]) - if template_match.valid: - template_match = self._template_finder.search_and_wait(["LEG_INVENTORY"], best_match=True, threshold=0.9, time_out=0.5, use_grayscale=False) - Logger.debug('\033[96m' + "Checking Inventory for Leg: found, calling open_cow_portal()" + '\033[0m') - self._open_cow_portal() - else: - Logger.debug('\033[96m' + "Checking Inventory for Leg: not found" + '\033[0m') - Logger.debug('\033[96m' + "Checking Stash for Leg" + '\033[0m') - pos_m = self._screen.convert_abs_to_monitor((160, 45)) - mouse.move(*pos_m, randomize=80, delay_factor=[0.5, 0.7]) - mouse.click(button="right") - template_match = self._template_finder.search_and_wait(["LEG_INVENTORY"], best_match=True, threshold=0.9, time_out=0.5, use_grayscale=False) - if template_match.valid: - template_match = self._template_finder.search_and_wait(["LEG_INVENTORY"], best_match=True, threshold=0.9, time_out=0.5, use_grayscale=False) - Logger.debug('\033[96m' + "Checking Stash for Leg: found, calling open_cow_portal()" + '\033[0m') - self._open_cow_portal() - else: - Logger.debug('\033[96m' + "Checking Stash for Leg: not found" + '\033[0m') - Logger.debug('\033[96m' + "Checking Cube for Leg" + '\033[0m') - template_match = self._template_finder.search_and_wait(["HORADRIC_CUBE"], best_match=True, threshold=0.9, time_out=0.5, use_grayscale=False) - #pos_m = self._screen.convert_screen_to_monitor(template_match.center) - pos_m = template_match.center - mouse.move(pos_m) - wait(0.1, 0.15) - mouse.click(button="right") - template_match = self._template_finder.search_and_wait(["LEG_INVENTORY"], best_match=True, threshold=0.9, time_out=0.5, use_grayscale=False) - if template_match.valid: - template_match = self._template_finder.search_and_wait(["LEG_INVENTORY"], best_match=True, threshold=0.9, time_out=0.5, use_grayscale=False) - Logger.debug('\033[96m' + "Checking Cube for Leg: found, calling open_cow_portal()" + '\033[0m') - self._open_cow_portal() - else: - Logger.debug('\033[96m' + "Checking Cube for Leg: not found, need to get it in stony field" + '\033[0m') - - - - #open inventory, search for leg - #open stash, search for leg - #open cube, search for leg - return True - - + #this function gets the leg in tristram def _tristram(self) -> bool: - logger.info('\033[93m' + "Entering Old Tristram to get the leg" + '\033[0m') - logger.info('\033[93m' + "Calibrating at Entrance TP" + '\033[0m') + #lets make sure our minimap is OFF + if self._template_finder.search_and_wait(["MAP_CHECK"], best_match=True, threshold=0.5, time_out=0.1, use_grayscale=False).valid: #check if the minimap is already on + keyboard.send(self._config.char["minimap"]) #turn off minimap + Logger.debug('\033[95m' + "Old Tristram: Closing Minimap" + '\033[0m') + + #Calibrate at WP + logger.info('\033[95m' + "Old Tristram: Entering Old Tristram to get the leg" + '\033[0m') + logger.info('\033[95m' + "Old Tristram: Calibrating at Entrance TP" + '\033[0m') if not self._pather.traverse_nodes([1000], self._char, time_out=5): return False - logger.info('\033[93m' + "Static Path to Corpse" + '\033[0m') + + #Static path to Wirt + logger.info('\033[95m' + "Old Tristram: Static Path to Corpse" + '\033[0m') self._pather.traverse_nodes_fixed("cow_trist_tp_leg", self._char) - logger.info('\033[93m' + "Calibrating at Corpse" + '\033[0m') + + #Calibrate at Wirt + logger.info('\033[95m' + "Old Tristram: Calibrating at Corpse" + '\033[0m') if not self._pather.traverse_nodes([1001], self._char, time_out=5): return False - logger.info('\033[93m' + "Looting the leg the Corpse" + '\033[0m') + + #Loot Wirt + logger.info('\033[95m' + "Old Tristram: Looting the leg the Corpse" + '\033[0m') if not self._char.select_by_template(["COW_WIRT_CLOSED"], threshold=0.63, time_out=4,telekinesis=True): # do a random tele jump and try again pos_m = self._screen.convert_abs_to_monitor((random.randint(-70, 70), random.randint(-70, 70))) @@ -444,40 +457,223 @@ def _tristram(self) -> bool: #and recalibrate at wirt if not self._pather.traverse_nodes([1001], self._char, time_out=5): return False if not self._char.select_by_template(["COW_WIRT_CLOSED"], threshold=0.63, time_out=4, telekinesis=True): + logger.info('\033[95m' + "Old Tristram: Unable to interact with the wirt" + '\033[0m') return False - #self._legdance(["COW_WIRT_OPEN.PNG"],["COW_WIRT_CLOSED"],"Old-Tristram", [1001]) - logger.info('\033[93m' + "Grabbing the leg" + '\033[0m') + + #Loot Leg + logger.info('\033[95m' + "Old Tristram: Grabbing the leg" + '\033[0m') self._picked_up_items |= self._pickit.pick_up_items(self._char) - logger.info('\033[93m' + "Making TP to go home" + '\033[0m') + + #TP Home + logger.info('\033[95m' + "Old Tristram: Making TP to go home" + '\033[0m') if not self._ui_manager.has_tps(): - Logger.warning('\033[93m' + "Cows: Open TP failed. Aborting run." + '\033[0m') + Logger.warning('\033[95m' + "Old Tristram: Open TP failed. Aborting run." + '\033[0m') self.used_tps += 20 return False mouse.click(button="right") self.used_tps += 1 - #return True - #enter portal if not self._char.select_by_template(["BLUE_PORTAL"], threshold=0.7, time_out=4,telekinesis=True): # do a random tele jump and try again pos_m = self._screen.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(["BLUE_PORTAL"], threshold=0.7, time_out=4,telekinesis=True): return False - #return True - + if not self._char.select_by_template(["BLUE_PORTAL"], threshold=0.7, time_out=4,telekinesis=True): Logger.debug('\033[93m' + "Tristram: didnt make it through Portal :/" + '\033[0m') + wait(1) + return True + + + #stolen from Transmute.py + def open_cube(self): + self._ui_manager._move_to_stash_tab(0) + screen = self._screen.grab() + match = self._template_finder.search( + ["HORADRIC_CUBE"], screen, threshold=0.9, roi=Config.ui_roi["left_inventory"]) + if match.valid: + x, y = self._screen.convert_screen_to_monitor(match.center) + mouse.move(x, y) + wait(0.2) + mouse.click("right") + wait(0.2) + else: + Logger.error(f"Can't find cube: {match.score}") + + #stolen from Transmute.py + def transmute(self): + screen = self._screen.grab() + match = self._template_finder.search( + ["CUBE_TRANSMUTE_BTN"], screen, roi=Config.ui_roi["cube_btn_roi"]) + if match.valid: + x, y = self._screen.convert_screen_to_monitor(match.center) + mouse.move(x, y) + wait(0.2) + mouse.click("left") + wait(0.2) + + + #stolen from Transmute.py + def close_cube(self): + wait(0.2) + keyboard.send("esc") + + #from ui_manager + # def buy_tome(self, curr_loc: Location) -> Union[Location, bool]: + # curr_act = TownManager.get_act_from_location(curr_loc) + # if curr_act is None: return False + # # 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 + # tp_tome = self._template_finder.search_and_wait("TP_TOME", roi=self._config.ui_roi["left_inventory"], time_out=3, normalize_monitor=True) + # if not tp_tome.valid: return False + # keyboard.send('ctrl', do_release=False) + # mouse.move(*tp_tome.center, randomize=8, delay_factor=[1.0, 1.5]) + # wait(0.1, 0.15) + # mouse.click(button="right") + # wait(0.1, 0.15) + # keyboard.send('ctrl', do_press=False) + # wait(0.1, 0.2) + # self._ui_manager.close_vendor_screen() + # return new_loc + + #this function checks for the leg in inventory, stash & cube. + def _legcheck(self) -> bool: + + #open inventory, search for leg + Logger.debug('\033[96m' + "Legcheck: Checking Inventory for Leg" + '\033[0m') + keyboard.send(self._config.char["inventory_screen"]) + legfound = self._template_finder.search_and_wait(["LEG_INVENTORY"], best_match=True, threshold=0.9, time_out=0.5, use_grayscale=False).valid + #wait(2) + keyboard.send(self._config.char["inventory_screen"]) + + #we found the leg + if legfound: + Logger.debug('\033[96m' + "Checking Inventory for Leg: found, calling open_cow_portal()" + '\033[0m') + return True + + #open stash, search for leg + else: + Logger.debug('\033[96m' + "Legcheck: Checking Inventory for Leg: not found" + '\033[0m') + Logger.debug('\033[96m' + "Legcheck: Checking Stash for Leg" + '\033[0m') + self._town_manager.open_stash(Location.A1_TOWN_START) + #wait(2) + if self._template_finder.search_and_wait(["LEG_INVENTORY"], best_match=True, threshold=0.9, time_out=0.5, use_grayscale=False).valid: + Logger.debug('\033[96m' + "Legcheck: Checking Stash for Leg: found, moving it to inventory" + '\033[0m') + leg = self._template_finder.search_and_wait("LEG_INVENTORY", roi=self._config.ui_roi["left_inventory"], time_out=3, normalize_monitor=True) + if not leg.valid: + Logger.debug('\033[96m' + "Legcheck: Failed to transfer Leg to Inventory, aborting run)" + '\033[0m') + return False + keyboard.send('ctrl', do_release=False) + mouse.move(*leg.center, randomize=8, delay_factor=[1.0, 1.5]) + wait(0.1, 0.15) + mouse.click(button="left") + wait(0.1, 0.15) + keyboard.send('ctrl', do_press=False) + Logger.debug('\033[96m' + "Legcheck: Transferred Leg to from Stash Inventory, calling open_cow_portal())" + '\033[0m') + return True + + #open cube, search for leg + else: + Logger.debug('\033[96m' + "Legcheck: Checking Stash for Leg: not found" + '\033[0m') + Logger.debug('\033[96m' + "Legcheck: Checking Cube for Leg" + '\033[0m') + #wait(2) + self.open_cube() #self.transmute.open_cube() + if self._template_finder.search_and_wait(["LEG_INVENTORY"], best_match=True, threshold=0.9, time_out=0.5, use_grayscale=False).valid: + Logger.debug('\033[96m' + "Legcheck: Checking Cube for Leg: found, moving it to inventory" + '\033[0m') + leg = self._template_finder.search_and_wait("LEG_INVENTORY", roi=self._config.ui_roi["cube_area_roi"], time_out=3, normalize_monitor=True) + if not leg.valid: + Logger.debug('\033[96m' + "Legcheck: Failed to transfer Leg to Inventory, aborting run)" + '\033[0m') + return False + keyboard.send('ctrl', do_release=False) + mouse.move(*leg.center, randomize=8, delay_factor=[1.0, 1.5]) + wait(0.1, 0.15) + mouse.click(button="left") + wait(0.1, 0.15) + keyboard.send('ctrl', do_press=False) + Logger.debug('\033[96m' + "Legcheck: Transferred Leg from Cube to Inventory, calling open_cow_portal())" + '\033[0m') + return True + else: + Logger.debug('\033[96m' + "Legcheck: Checking Cube for Leg: not found, need to get it in stony field" + '\033[0m') + #i assume we leave the cube in the stash + return False + + + #this function opens the cow portal def _open_cow_portal(self)-> bool: - #go to akara, buy a tome - #go to stash & cube leg & top - #enter portal - self._cows() - #return True + + #go to stash, get cube & leg + + #go to Akara, get a Tome of TP + logger.info('\033[91m' + "Open_Cow_Portal: TP to Akara - buy Tome" + '\033[0m') + #self._pather.traverse_nodes([708, 700,705,706,707], self._char) #Stash to Akara + #self._pather.traverse_nodes([700], self._char) #calibrate at a1_town_start + if not self._pather.traverse_nodes((Location.A1_TOWN_START, Location.A1_AKARA), self._char, force_move=True): return False #walk to Akara + + ############################################################ + # vvvvvv CTHU END, FROM HERE IT DOES NOT WORK YET vvvvvv # + ############################################################ + + + #open vendor menu + #wait(3) #giving you 3 sec to open manually the vendor menu + self._npc_manager.open_npc_menu(Npc.AKARA) + self._npc_manager.press_npc_btn(Npc.AKARA, "trade") + + #check if there is a tome for sale & buy it + tp_tome = self._template_finder.search_and_wait("TP_TOME", roi=self._config.ui_roi["left_inventory"], time_out=3, normalize_monitor=True) + if not tp_tome.valid: return False + keyboard.send('ctrl', do_release=False) + mouse.move(*tp_tome.center, randomize=8, delay_factor=[1.0, 1.5]) + wait(0.1, 0.15) + mouse.click(button="right") + wait(0.1, 0.15) + keyboard.send('ctrl', do_press=False) + self._ui_manager.close_vendor_screen() + # curr_loc = TownManager.get_act_from_location(curr_loc) + # self.buy_tome(curr_loc) + + logger.info('\033[91m' + "Open_Cow_Portal: Akara to Stash - get Cube"+ '\033[0m') + self._pather.traverse_nodes([707,706,705, 700, 701], self._char) #Akara to Stash + self._town_manager.open_stash(Location.A1_TOWN_START) # this causes issues: using this function, she stashes the leg & drops the tome :D - At this stage we just want to open the stash and NOT put items into it. + + Logger.info('\033[91m' + "Open_Cow_Portal: Opening Cube"+ '\033[0m') + self.open_cube() + tp_tome = self._template_finder.search_and_wait("TP_TOME", roi=self._config.ui_roi["right_inventory"], time_out=3, normalize_monitor=True) #we must search for the tome in our loot columns. I run with loot columns=4, so we take an ROI of 4x 40px = 160x160 from the left inventory. + if not tp_tome.valid: + Logger.info('\033[91m' + "Open_Cow_Portal: Did not find TOME to transmute, aborting run"+ '\033[0m') + return False + keyboard.send('ctrl', do_release=False) + mouse.move(*tp_tome.center, randomize=8, delay_factor=[1.0, 1.5]) + wait(0.1, 0.15) + mouse.click(button="left") + wait(0.1, 0.15) + keyboard.send('ctrl', do_press=False) + wait(0.5) + #we might need to let botty learn where the leg is right now, it needs to be either in the inv & cube. if in inv, we move it to cube. if in stash, it has to go to inv. + legmove = self._template_finder.search_and_wait("LEG_INVENTORY", roi=self._config.ui_roi["right_inventory"], time_out=3, normalize_monitor=True) + if not legmove.valid: + Logger.info('\033[91m' + "Open_Cow_Portal: Did not find LEG to transmute, aborting run"+ '\033[0m') + return False + keyboard.send('ctrl', do_release=False) + mouse.move(*legmove.center, randomize=8, delay_factor=[1.0, 1.5]) + wait(0.1, 0.15) + mouse.click(button="left") + wait(0.1, 0.15) + keyboard.send('ctrl', do_press=False) + logger.info('\033[91m' + "Open_Cow_Portal: Transmuting"+ '\033[0m') + wait(1) + self.transmute() + wait(1) + self.close_cube() + return True # this function kills cows def _cows(self) -> bool: - #train neuronal network to search for heads and feet of cows that are not dead as positive + logger.info('\033[93m' + "Cows: Entering Portal"+ '\033[0m') + if not self._char.select_by_template(["COW_STONY_FIELD_PORTAL_1"], threshold=0.63, time_out=4,telekinesis=True): return False + #train neuronal network to search for heads and feet of cows that are not dead as positive according to this tutorial: https://www.youtube.com/watch?v=XrCAvs9AePM&list=PL1m2M8LQlzfKtkKq2lK5xko4X-8EZzFPI&index=8 #train it with images from dead cows and from empty cow levels as negative #search for template head or feet, cast attack rotation & pickit, repeat until? return True @@ -486,36 +682,51 @@ def approach(self, start_loc: Location) -> Union[bool, Location, bool]: Logger.info("Run Secret Cow Level") if not self._char.capabilities.can_teleport_natively: raise ValueError("Cows requires teleport") - #CHECK IF THE LEG IS ALREADY IN THE STASH OR INVENTORY! - #if yes: open_cows() - #if no: stony_field() - logger.info("Legcheck") - #self._legcheck() - logger.info("Opening WP & moving to Stony Field") - if not self._town_manager.open_wp(start_loc): - return False - wait(0.4) - self._ui_manager.use_wp(1, 2) - return Location.A1_STONY_FIELD_WP + logger.info("Checking if we have the Wirt's Leg already") + + if not self._legcheck(): + logger.info("We dont have the Leg, let's grab it! Opening WP & moving to Stony Field") + if not self._town_manager.open_wp(start_loc): + return False + wait(0.4) + self._ui_manager.use_wp(1, 2) + return Location.A1_STONY_FIELD_WP + else: + self._open_cow_portal() def battle(self, do_pre_buff: bool) -> Union[bool, tuple[Location, bool]]: if do_pre_buff: self._char.pre_buff() self._picked_up_items = False self.used_tps = 0 + start_time = time.time() stuck_count = 0 + + # find old tristram portal in stony field + found = False keyboard.send(self._char._skill_hotkeys["teleport"]) #switch active skill to teleport - self._scout(1, -50, 50, -150, -250, stuck_count, 0, 4, 2, 2, 0) #tries to get to exit - #pre, during_1, during_2, diffed = self._map_capture() - #self.map_diff(pre, during_1, during_2) + self._scout(1, -100, 100, -200, -300, stuck_count, 0, 3, 2, 2, 0, found) #scout top + + # lets evaluate how long we scouted + scout_duration = (time.time() - start_time) + #time.strftime("%H:%M:%S", time.gmtime(scout_duration)) + Logger.debug ("Scouting Duration [s]: " + str(round(scout_duration))) + + #get the leg & TP to town if not self._tristram(): return False + Logger.debug ("I arrived back in town") + wait(3) + + #check if we have the leg. + if not self._legcheck(): return False #if no leg is found, stop the run. + + #get stuff from stash & akara if not self._open_cow_portal(): return False - """ - if not self._stony_field(): return False - if not self._tristram(): return False + + # go through TP & kill cows if not self._cows(): return False - """ - return (Location.A1_COWS_END, self._picked_up_items) + + return (Location.A1_COW_END, self._picked_up_items) if __name__ == "__main__": from screen import Screen @@ -530,4 +741,7 @@ def battle(self, do_pre_buff: bool) -> Union[bool, tuple[Location, bool]]: config = Config() screen = Screen() game_stats = GameStats() - bot = Bot(screen, game_stats, False) \ No newline at end of file + bot = Bot(screen, game_stats, False) + + #pre, during_1, during_2, diffed = self._map_capture() + #self.map_diff(pre, during_1, during_2) \ No newline at end of file