示例#1
0
 def _move_to_stash_tab(self, 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 = self._template_finder.search(
         [*str_to_idx_map],
         self._screen.grab(),
         threshold=0.7,
         best_match=True,
         roi=self._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 = (self._config.ui_pos["stash_personal_btn_x"],
                               self._config.ui_pos["stash_personal_btn_y"])
         stash_btn_width = self._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 = self._screen.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)
示例#2
0
 def enable_no_pickup(self) -> bool:
     """
     Checks the best match between enabled and disabled an retrys if already set.
     :return: Returns True if we succesfully set the nopickup option
     """
     keyboard.send('enter')
     wait(0.1, 0.25)
     keyboard.write('/nopickup', delay=.20)
     keyboard.send('enter')
     wait(0.1, 0.25)
     no_pickup = self._template_finder.search_and_wait(
         ["ITEM_PICKUP_ENABLED", "ITEM_PICKUP_DISABLED"],
         roi=self._config.ui_roi["no_pickup"],
         best_match=True,
         time_out=3)
     if not no_pickup.valid:
         return False
     if no_pickup.name == "ITEM_PICKUP_DISABLED":
         return True
     keyboard.send('enter')
     wait(0.1, 0.25)
     keyboard.send('up')
     wait(0.1, 0.25)
     keyboard.send('enter')
     wait(0.1, 0.25)
     return True
示例#3
0
 def use_wp(self, act: int, idx: int):
     """
     Use Waypoint. The menu must be opened when calling the function.
     :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
     }
     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:
         pos_act_btn = (self._config.ui_pos["wp_act_i_btn_x"] +
                        self._config.ui_pos["wp_act_btn_width"] * (act - 1),
                        self._config.ui_pos["wp_act_i_btn_y"])
         x, y = self._screen.convert_screen_to_monitor(pos_act_btn)
         mouse.move(x, y, randomize=8)
         mouse.click(button="left")
         wait(0.3, 0.4)
     pos_wp_btn = (self._config.ui_pos["wp_first_btn_x"],
                   self._config.ui_pos["wp_first_btn_y"] +
                   self._config.ui_pos["wp_btn_height"] * idx)
     x, y = self._screen.convert_screen_to_monitor(pos_wp_btn)
     mouse.move(x, y, randomize=[60, 9], delay_factor=[0.9, 1.4])
     wait(0.4, 0.5)
     mouse.click(button="left")
     # wait till loading screen is over
     if self.wait_for_loading_screen():
         while 1:
             if not self.wait_for_loading_screen(0.2):
                 return
示例#4
0
 def should_stash(self, num_loot_columns: int):
     """
     Check if there are items that need to be stashed in the inventory
     :param num_loot_columns: Number of columns used for loot from left
     """
     wait(0.2, 0.3)
     keyboard.send(self._config.char["inventory_screen"])
     wait(0.7, 1.0)
     should_stash = self._inventory_has_items(self._screen.grab(),
                                              num_loot_columns)
     keyboard.send(self._config.char["inventory_screen"])
     wait(0.4, 0.6)
     return should_stash
示例#5
0
 def repair_and_fill_up_tp(self) -> bool:
     """
     Repair and fills up TP buy selling tome and buying. Vendor inventory needs to be open!
     :return: Bool if success
     """
     repair_btn = self._template_finder.search_and_wait(
         "REPAIR_BTN", roi=self._config.ui_roi["repair_btn"], time_out=4)
     if not repair_btn.valid:
         return False
     x, y = self._screen.convert_screen_to_monitor(repair_btn.position)
     mouse.move(x, y, randomize=12, delay_factor=[1.0, 1.5])
     wait(0.1, 0.15)
     mouse.click(button="left")
     wait(0.1, 0.15)
     x, y = self._screen.convert_screen_to_monitor(
         (self._config.ui_pos["vendor_misc_x"],
          self._config.ui_pos["vendor_misc_y"]))
     mouse.move(x, y, randomize=[20, 6], delay_factor=[1.0, 1.5])
     wait(0.1, 0.15)
     mouse.click(button="left")
     # another click to dismiss popup message in case you have not enough gold to repair, preventing tome not being bought back
     wait(0.1, 0.15)
     mouse.click(button="left")
     wait(0.5, 0.6)
     tp_tome = self._template_finder.search_and_wait(
         ["TP_TOME", "TP_TOME_RED"],
         roi=self._config.ui_roi["inventory"],
         time_out=3)
     if not tp_tome.valid:
         return False
     x, y = self._screen.convert_screen_to_monitor(tp_tome.position)
     keyboard.send('ctrl', do_release=False)
     mouse.move(x, y, randomize=8, delay_factor=[1.0, 1.5])
     wait(0.1, 0.15)
     mouse.press(button="left")
     wait(0.25, 0.35)
     mouse.release(button="left")
     wait(0.5, 0.6)
     keyboard.send('ctrl', do_press=False)
     tp_tome = self._template_finder.search_and_wait(
         "TP_TOME", roi=self._config.ui_roi["vendor_stash"], time_out=3)
     if not tp_tome.valid:
         return False
     x, y = self._screen.convert_screen_to_monitor(tp_tome.position)
     keyboard.send('ctrl', do_release=False)
     mouse.move(x, y, 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)
     # delay to make sure the tome has time to transfer to other inventory before closing window
     tp_tome = self._template_finder.search_and_wait(
         "TP_TOME", roi=self._config.ui_roi["inventory"], time_out=3)
     if not tp_tome.valid:
         return False
     return True
示例#6
0
    def stash_all_items(self, num_loot_columns: int, item_finder: ItemFinder):
        """
        Stashing all items in inventory. Stash UI must be open when calling the function.
        :param num_loot_columns: Number of columns used for loot from left
        """
        Logger.debug("Searching for inventory gold btn...")
        # Move cursor to center
        x, y = self._screen.convert_abs_to_monitor((0, 0))
        mouse.move(x, y, randomize=[40, 200], delay_factor=[1.0, 1.5])
        # Wait till gold btn is found
        gold_btn = self._template_finder.search_and_wait(
            "INVENTORY_GOLD_BTN",
            roi=self._config.ui_roi["gold_btn"],
            time_out=20)
        if not gold_btn.valid:
            Logger.error(
                "Could not determine to be in stash menu. Continue...")
            return
        Logger.debug("Found inventory gold btn")
        # stash gold
        if self._config.char["stash_gold"]:
            inventory_no_gold = self._template_finder.search(
                "INVENTORY_NO_GOLD",
                self._screen.grab(),
                roi=self._config.ui_roi["inventory_gold"],
                threshold=0.83)
            if inventory_no_gold.valid:
                Logger.debug("Skipping gold stashing")
            else:
                Logger.debug("Stashing gold")
                self._move_to_stash_tab(min(3, self._curr_stash["gold"]))
                x, y = self._screen.convert_screen_to_monitor(
                    gold_btn.position)
                mouse.move(x, y, 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.0, 1.2)
                # move cursor away from button to interfere with screen grab
                mouse.move(-120, 0, absolute=False, randomize=15)
                inventory_no_gold = self._template_finder.search(
                    "INVENTORY_NO_GOLD",
                    self._screen.grab(),
                    roi=self._config.ui_roi["inventory_gold"],
                    threshold=0.83)
                if not inventory_no_gold.valid:
                    Logger.info(
                        "Stash tab is full of gold, selecting next stash tab.")
                    self._curr_stash["gold"] += 1
                    if self._config.general["info_screenshots"]:
                        cv2.imwrite(
                            "./info_screenshots/info_gold_stash_full_" +
                            time.strftime("%Y%m%d_%H%M%S") + ".png",
                            self._screen.grab())
                    if self._curr_stash["gold"] > 3:
                        # turn of gold pickup
                        self._config.char["stash_gold"] = False
                        self._config.items["misc_gold"] = False
                        item_finder.update_items_to_pick(self._config)
                        # inform user about it
                        msg = "All stash tabs and character are full of gold, turn of gold pickup"
                        Logger.info(msg)
                        if self._config.general["custom_message_hook"]:
                            self._messenger.send(
                                msg=f"{self._config.general['name']}: {msg}")
                    else:
                        # move to next stash
                        wait(0.5, 0.6)
                        return self.stash_all_items(num_loot_columns,
                                                    item_finder)
        # stash stuff
        self._move_to_stash_tab(self._curr_stash["items"])
        center_m = self._screen.convert_abs_to_monitor((0, 0))
        for column, row in itertools.product(range(num_loot_columns),
                                             range(4)):
            img = self._screen.grab()
            slot_pos, slot_img = self.get_slot_pos_and_img(
                self._config, img, column, row)
            if self._slot_has_item(slot_img):
                x_m, y_m = self._screen.convert_screen_to_monitor(slot_pos)
                mouse.move(x_m, y_m, randomize=10, delay_factor=[1.0, 1.3])
                # check item again and discard it or stash it
                wait(1.2, 1.4)
                hovered_item = self._screen.grab()
                if self._keep_item(item_finder, hovered_item):
                    keyboard.send('ctrl', do_release=False)
                    wait(0.2, 0.25)
                    mouse.press(button="left")
                    wait(0.2, 0.25)
                    mouse.release(button="left")
                    wait(0.2, 0.25)
                    keyboard.send('ctrl', do_press=False)
                else:
                    # make sure there is actually an item
                    time.sleep(0.3)
                    curr_pos = mouse.get_position()
                    # move mouse away from inventory, for some reason it was sometimes included in the grabed img
                    x, y = self._screen.convert_abs_to_monitor((0, 0))
                    mouse.move(x,
                               y,
                               randomize=[40, 200],
                               delay_factor=[1.0, 1.5])
                    item_check_img = self._screen.grab()
                    mouse.move(*curr_pos, randomize=2)
                    wait(0.4, 0.6)
                    slot_pos, slot_img = self.get_slot_pos_and_img(
                        self._config, item_check_img, column, row)
                    if self._slot_has_item(slot_img):
                        if self._config.general["info_screenshots"]:
                            cv2.imwrite(
                                "./info_screenshots/info_discard_item_" +
                                time.strftime("%Y%m%d_%H%M%S") + ".png",
                                hovered_item)
                        mouse.press(button="left")
                        wait(0.2, 0.4)
                        mouse.release(button="left")
                        mouse.move(*center_m, randomize=20)
                        wait(0.2, 0.3)
                        mouse.press(button="left")
                        wait(0.2, 0.3)
                        mouse.release(button="left")
                        wait(0.5, 0.5)
        Logger.debug("Check if stash is full")
        time.sleep(0.6)
        # move mouse away from inventory, for some reason it was sometimes included in the grabed img
        x, y = self._screen.convert_abs_to_monitor((0, 0))
        mouse.move(x, y, randomize=[40, 200], delay_factor=[1.0, 1.5])
        img = self._screen.grab()
        if self._inventory_has_items(img, num_loot_columns):
            Logger.info("Stash page is full, selecting next stash")
            if self._config.general["info_screenshots"]:
                cv2.imwrite(
                    "./info_screenshots/debug_info_inventory_not_empty_" +
                    time.strftime("%Y%m%d_%H%M%S") + ".png", img)
            self._curr_stash["items"] += 1
            if self._curr_stash["items"] > 3:
                Logger.error("All stash is full, quitting")
                if self._config.general["custom_message_hook"]:
                    self._messenger.send(
                        msg=
                        f"{self._config.general['name']}: all stash is full, quitting"
                    )
                os._exit(1)
            else:
                # move to next stash
                wait(0.5, 0.6)
                return self.stash_all_items(num_loot_columns, item_finder)

        Logger.debug("Done stashing")
        wait(0.4, 0.5)
        keyboard.send("esc")
示例#7
0
    def _keep_item(self, item_finder: ItemFinder, img: np.ndarray) -> bool:
        """
        Check if an item should be kept, the item should be hovered and in own inventory when function is called
        :param item_finder: ItemFinder to check if item is in pickit
        :param img: Image in which the item is searched (item details should be visible)
        :return: Bool if item should be kept
        """
        wait(0.2, 0.3)
        _, w, _ = img.shape
        img = img[:, (w // 2):, :]
        original_list = item_finder.search(img)
        filtered_list = []
        for x in original_list:
            if ("potion" in x.name) or (self._config.items[x.name].pickit_type
                                        == 0):
                continue
            include_props = self._config.items[x.name].include
            exclude_props = self._config.items[x.name].exclude
            if not (include_props or exclude_props):
                Logger.debug(f"{x.name}: Stashing")
                filtered_list.append(x)
                continue
            include = True
            include_logic_type = self._config.items[x.name].include_type
            if include_props:
                include = False
                found_props = []
                for prop in include_props:
                    try:
                        template_match = self._template_finder.search(
                            prop, img, threshold=0.95)
                    except:
                        Logger.error(
                            f"{x.name}: can't find template file for required {prop}, ignore just in case"
                        )
                        template_match = lambda: None
                        template_match.valid = True
                    if template_match.valid:
                        if include_logic_type == "AND":
                            found_props.append(True)
                        else:
                            include = True
                            break
                    else:
                        found_props.append(False)
                if include_logic_type == "AND" and len(
                        found_props) > 0 and all(found_props):
                    include = True
            if not include:
                Logger.debug(
                    f"{x.name}: Discarding. Required {include_logic_type}({include_props})={include}"
                )
                continue
            exclude = False
            exclude_logic_type = self._config.items[x.name].exclude_type
            if exclude_props:
                found_props = []
                for prop in exclude_props:
                    try:
                        template_match = self._template_finder.search(
                            prop, img, threshold=0.97)
                    except:
                        Logger.error(
                            f"{x.name}: can't find template file for exclusion {prop}, ignore just in case"
                        )
                        template_match = lambda: None
                        template_match.valid = False
                    if template_match.valid:
                        if exclude_logic_type == "AND":
                            found_props.append(True)
                        else:
                            exclude = True
                            break
                    else:
                        found_props.append(False)
                if exclude_logic_type == "AND" and len(
                        exclude_props) > 0 and all(found_props):
                    exclude = True
                    break
            if include and not exclude:
                Logger.debug(
                    f"{x.name}: Stashing. Required {include_logic_type}({include_props})={include}, exclude {exclude_logic_type}({exclude_props})={exclude}"
                )
                filtered_list.append(x)

        return len(filtered_list) > 0
示例#8
0
    def start_game(self) -> bool:
        """
        Starting a game. Will wait and retry on server connection issue.
        :return: Bool if action was successful
        """
        Logger.debug("Wait for Play button")
        # To test the start_game() function seperatly, just run:
        # (botty) >> python src/ui_manager.py
        # then go to D2r window -> press "f11", you can exit with "f12"
        while 1:
            # grab img which will be used to search the "play button"
            img = self._screen.grab()
            # the template finder can be used to search for a specific template, in this case the play btn.
            # it returns a bool value (True or False) if the button was found, and the position of it
            # roi = Region of interest. It reduces the search area and can be adapted within game.ini
            # by running >> python src/screen.py you can visualize all of the currently set region of interests
            found_btn = self._template_finder.search(
                ["PLAY_BTN", "PLAY_BTN_GRAY"],
                img,
                roi=self._config.ui_roi["offline_btn"],
                threshold=0.8,
                best_match=True)
            if found_btn.name == "PLAY_BTN":
                # We need to convert the position to monitor coordinates (e.g. if someone is using 2 monitors or windowed mode)
                x, y = self._screen.convert_screen_to_monitor(
                    found_btn.position)
                Logger.debug(f"Found Play Btn")
                mouse.move(x, y, randomize=[35, 7], delay_factor=[1.0, 1.8])
                wait(0.1, 0.15)
                mouse.click(button="left")
                break
            else:
                found_btn = self._template_finder.search(
                    "PLAY_BTN",
                    img,
                    roi=self._config.ui_roi["online_btn"],
                    threshold=0.8)
                if found_btn.valid:
                    Logger.warning(
                        "WARNING CHOPPA GOOCH IN TEH CODEZ - Lets get this party started!"
                    )
                    x, y = self._screen.convert_screen_to_monitor(
                        found_btn.position)
                    mouse.move(x,
                               y,
                               randomize=[35, 7],
                               delay_factor=[1.0, 1.8])
                    wait(0.1, 0.15)
                    mouse.click(button="left")
                    break
            time.sleep(3.0)

        difficulty = self._config.general["difficulty"].upper()
        while 1:
            template_match = self._template_finder.search_and_wait(
                ["LOADING", f"{difficulty}_BTN"],
                time_out=8,
                roi=self._config.ui_roi["difficulty_select"],
                threshold=0.9)
            if not template_match.valid:
                Logger.debug(
                    f"Could not find {difficulty}_BTN, try from start again")
                return self.start_game()
            if template_match.name == "LOADING":
                Logger.debug(f"Found {template_match.name} screen")
                return True
            x, y = self._screen.convert_screen_to_monitor(
                template_match.position)
            mouse.move(x, y, randomize=[50, 9], delay_factor=[1.0, 1.8])
            wait(0.15, 0.2)
            mouse.click(button="left")
            break

        # check for server issue
        wait(2.0)
        server_issue = self._template_finder.search("SERVER_ISSUES",
                                                    self._screen.grab()).valid
        if server_issue:
            Logger.warning("Server connection issue. waiting 20s")
            x, y = self._screen.convert_screen_to_monitor(
                (self._config.ui_pos["issue_occured_ok_x"],
                 self._config.ui_pos["issue_occured_ok_y"]))
            mouse.move(x, y, randomize=10, delay_factor=[2.0, 4.0])
            mouse.click(button="left")
            wait(1, 2)
            keyboard.send("esc")
            wait(18, 22)
            return self.start_game()
        else:
            return True
示例#9
0
 def save_and_exit(self, does_chicken: bool = False) -> bool:
     """
     Performes save and exit action from within game
     :return: Bool if action was successful
     """
     start = time.time()
     while (time.time() - start) < 15:
         templates = [
             "SAVE_AND_EXIT_NO_HIGHLIGHT", "SAVE_AND_EXIT_HIGHLIGHT"
         ]
         if not self._template_finder.search(
                 templates,
                 self._screen.grab(),
                 roi=self._config.ui_roi["save_and_exit"],
                 threshold=0.85).valid:
             keyboard.send("esc")
         wait(0.3)
         exit_btn_pos = (self._config.ui_pos["save_and_exit_x"],
                         self._config.ui_pos["save_and_exit_y"])
         x_m, y_m = self._screen.convert_screen_to_monitor(exit_btn_pos)
         # TODO: Add hardcoded coordinates to ini file
         away_x_m, away_y_m = self._screen.convert_abs_to_monitor((-167, 0))
         while self._template_finder.search_and_wait(
                 templates,
                 roi=self._config.ui_roi["save_and_exit"],
                 time_out=1.5,
                 take_ss=False).valid:
             delay = [0.9, 1.1]
             if does_chicken:
                 delay = [0.3, 0.4]
             mouse.move(x_m, y_m, randomize=[38, 7], delay_factor=delay)
             wait(0.03, 0.06)
             mouse.press(button="left")
             wait(0.06, 0.1)
             mouse.release(button="left")
             if does_chicken:
                 # lets just try again just in case
                 wait(0.05, 0.08)
                 # mouse.click(button="left")
             wait(1.5, 2.0)
             mouse.move(away_x_m,
                        away_y_m,
                        randomize=40,
                        delay_factor=[0.6, 0.9])
             wait(0.1, 0.5)
         return True
     return False