Esempio n. 1
0
    def get_ui_element(self, element, screenshot=None, exit_on_fail=True):
        """
            Find a UI element on the screen
            """
        # Move the mouse so it doesn't obstruct search
        pyautogui.moveTo(400, 400)

        # Get a screenshot
        if screenshot is None:
            screenshot = utils.take_screenshot(False)

        # Try to match the template in the screenshot
        result = cv2.matchTemplate(screenshot, self.templates[element],
                                   cv2.TM_CCORR_NORMED)
        _, max_val, _, max_loc = cv2.minMaxLoc(result)

        # Found the element, return the location
        if max_val > 0.9:
            return max_loc

        # Failed to find the element with high enough confidence
        if not exit_on_fail:
            return False

        # Not finding the element is severe enough to quit the game
        utils.log("SEVERE",
                  F"Failed to find {element}, max confidence was {max_val}")
        utils.quit_game()
Esempio n. 2
0
    def step(self, new_position):
        """
            Given a set of coordinates, calculate what direction to step
            and send that key to the game
            """
        # Calculate direction of movement
        x_diff = self.game_map.player_position[0] - new_position[0]
        y_diff = self.game_map.player_position[1] - new_position[1]

        # Determine what key to press
        direction = ''
        if x_diff == 1:
            direction = 'a'
        elif x_diff == -1:
            direction = 'd'
        elif y_diff == 1:
            direction = 'w'
        elif y_diff == -1:
            direction = 's'
        else:
            utils.log(
                'SEVERE',
                F"Invalid step difference. xDiff: {x_diff}, yDiff: {y_diff}")
            utils.quit_game()

        # Move along path
        pyautogui.press(direction)
        sleep(0.1)

        # Player moved, re-detect environment
        screenshot = utils.take_screenshot()
        self.game_map.update_player_position(screenshot)
        self.game_map.update_map()
Esempio n. 3
0
    def get_weight(self):
        """
            Gets the player's current and max weight
            """
        # Find the current and total weight
        screenshot = utils.take_screenshot(False)
        weight = self.get_ui_element('weight', screenshot)
        weight = screenshot[weight[1]:(weight[1] + 12),
                            (weight[0] + 40):(weight[0] + 84)]

        # Resize and parse the image to a string
        weight = cv2.resize(weight, (0, 0), fx=3, fy=3)
        weight = cv2.cvtColor(weight, cv2.COLOR_BGR2GRAY)
        weight = cv2.bitwise_not(weight)
        weight = cv2.fastNlMeansDenoising(weight, None, 9, 13)
        _, weight = cv2.threshold(weight, 180, 255, cv2.THRESH_BINARY)
        weight = cv2.blur(weight, (4, 2))

        weight_text = ''
        try:
            weight_text = pytesseract.image_to_string(
                weight, config=utils.TESSERACT_CONF)
        except UnicodeDecodeError:
            utils.log(
                "SEVERE",
                "Tesseract failed to parse player weight from screenshot")
            utils.quit_game()

        # Split the equation and calculate the difference
        current_weight, max_weight = weight_text.split("/")[0::1]
        return int(current_weight), int(max_weight)
Esempio n. 4
0
    def get_health(self):
        """
            Gets the player's current health
            """
        # Reduce the screenshot to include only the player's health
        screenshot = utils.take_screenshot(False)
        health = self.get_ui_element('health', screenshot)
        health = screenshot[health[1]:(health[1] + 12),
                            (health[0] + 36):(health[0] + 92)]

        # Resize and parse the image to a string
        health = cv2.resize(health, (0, 0), fx=3, fy=3)
        health = cv2.cvtColor(health, cv2.COLOR_BGR2GRAY)
        health = cv2.bitwise_not(health)
        health = cv2.fastNlMeansDenoising(health, None, 9, 13)
        _, health = cv2.threshold(health, 180, 255, cv2.THRESH_BINARY)
        health = cv2.blur(health, (4, 2))

        health_text = ''
        try:
            health_text = pytesseract.image_to_string(
                health, config=utils.TESSERACT_CONF)
        except UnicodeDecodeError:
            utils.log(
                "SEVERE",
                "Tesseract failed to parse player health from screenshot")
            utils.quit_game()

        # Split the equation and calculate the difference
        current = health_text.split("/")[0]
        return int(current)
Esempio n. 5
0
    def wait_for_ui_element(self, element, exit_on_fail=True):
        """
            Some elements take time to appear after an event, typically a click, was sent.
            This method will attempt to find a UI element on the screen 10 times. After each
            failed attempt, it will delay 0.5 seconds and try again.

            If the element is not found after 10 attempts, it will either quit the game or
            return false depending on the value of exit_on_fail.

            An example of when to use this method is after clicking on a merchant, the "Buy
            or sell" window can take some time to appear on screen.
            """
        attempts = 0
        element_loc = None
        while attempts < 20:
            # Try and click on the merchant
            element_loc = self.get_ui_element(element, exit_on_fail=False)

            # Found the element
            if element_loc:
                return element_loc

            sleep(0.25)
            attempts += 1

        # Failed to find the UI element after 10 attempts
        if not element_loc and exit_on_fail:
            utils.log(
                "SEVERE",
                F"Failed to find {element} after waiting and searching 20 times"
            )
            utils.quit_game()

        # Failed to find element but failure will be handled elsewhere
        return False
Esempio n. 6
0
    def check_health(self):
        """
            Checks player's HP and uses a potion if it is low.
            If no potions are found, game will quit
            """
        # Check if HP is low
        if self.user_interface.get_health() < self.LOW_HEALTH:
            # Attempt to use a potion
            utils.log("INFO", F"Health dropped below {self.LOW_HEALTH}")
            used_potion = self.backpack.use_item('potion', offset=(4, 9))

            # No potions were found
            if not used_potion:
                # Potions may be obscurred by items, try selling
                self.forge.sell_items()
                # Try using a potion again
                used_potion = self.backpack.use_item('potion', offset=(4, 9))

                # Still can't use potions, likely do not have any
                if not used_potion:
                    utils.log("SEVERE", "No potions found")
                    utils.quit_game()

            # Sleep so that there is no issue using the next item
            utils.log("INFO", F"Used a potion")
            sleep(6)
Esempio n. 7
0
    def move_to_merchant(self, merchant_type):
        """
            Move to a merchant
            """
        # Move to the appropriate merchant
        merchant_tile = None
        # Weapons shopkeeper
        if merchant_type == self.MERCHANTS.WEAPONS:
            merchant_tile = self.game_map.TILES.WEAPON_SHOPKEEPER.value
            self.move.move_to(self.move.WEAPON_SHOPKEEPER)
        # Blacksmith
        elif merchant_type == self.MERCHANTS.BLACKSMITH:
            merchant_tile = self.game_map.TILES.BLACKSMITH.value
            self.move.move_to(self.move.FURNACE)
        # Potions shopkeeper
        elif merchant_type == self.MERCHANTS.POTIONS:
            merchant_tile = self.game_map.TILES.POTIONS_SHOPKEEPER.value
            self.move.move_to(self.move.POTIONS_SHOPKEEPER)
        # Items shopkeeper
        elif merchant_type == self.MERCHANTS.ITEMS:
            merchant_tile = self.game_map.TILES.ITEM_SHOPKEEPER.value
            self.move.move_to(self.move.ITEMS)
        # Banker
        elif merchant_type == self.MERCHANTS.BANKER:
            merchant_tile = self.game_map.TILES.BANKER.value
            self.move.move_to(self.move.BANKER)
        else:
            utils.log("SEVERE",
                      "Invalid merchant_type supplied to move_to_merchant")
            utils.quit_game()

        # Update map so merchant's position will be current
        self.game_map.update_map()

        # Get the 5x5 matrix of tiles surrounding the player
        clickable_tiles = self.game_map.game_map[(
            self.game_map.player_position[0] -
            2):(self.game_map.player_position[0] +
                3), (self.game_map.player_position[1] -
                     2):(self.game_map.player_position[1] + 3)]

        # Find the index where merchant is located
        merchant_indices = np.argwhere(clickable_tiles == merchant_tile)
        if merchant_indices.size == 0:
            return False

        # Get the merchant index
        merchant_index = merchant_indices[0]

        # Calculate where to click (2 because player is in center of 5x5 matrix that is 0 indexed)
        x_coord = merchant_index[0] - 2
        y_coord = merchant_index[1] - 2
        return (176 + (x_coord * 16), 176 + (y_coord * 16))
Esempio n. 8
0
    def sell_item(self, item, merchant_type):
        """
            Move to a merchant and sell an item
            """
        # Open the "Buy or Sell" window
        buy_or_sell = self.open_merchant_window(merchant_type)
        if not buy_or_sell:
            utils.log(
                "SEVERE",
                F"Failed to click on {merchant_type} and open 'Buy or Sell' after 10 attempts"
            )
            utils.quit_game()

        # Click the sell button
        sell_button = self.user_interface.wait_for_ui_element('sell')
        pyautogui.moveTo(sell_button[0] + 10, sell_button[1] + 10, 0.15)
        pyautogui.click()

        # Wait for the sell menu to open
        self.user_interface.wait_for_ui_element('sellMenu')

        # Offer up to 12 items
        items_sold = 0
        for _ in range(12):
            # Move the cursor away so it will register an "hover" event when move back
            pyautogui.moveTo(330, 206)

            # Find a item to sell
            item_loc = self.user_interface.get_ui_element(item,
                                                          exit_on_fail=False)

            # No item_locs left to sell
            if not item_loc:
                utils.log("INFO", F"No {item} left to offer shopkeeper")
                break

            items_sold += 1
            pyautogui.moveTo(item_loc[0] + 6, item_loc[1] + 12, 0.15)
            pyautogui.doubleClick()
            sleep(0.5)

        # Confirm the sale
        check_mark = self.user_interface.wait_for_ui_element('checkMark')
        pyautogui.moveTo(check_mark[0] + 5, check_mark[1] + 5, 0.15)
        pyautogui.click()

        # Click cancel to leave the window
        cancel = self.user_interface.wait_for_ui_element('cancel')
        pyautogui.moveTo(cancel[0] + 5, cancel[1] + 5, 0.15)
        pyautogui.click()

        utils.log("INFO", F"Sold {items_sold} {item}(s)")
        return items_sold
Esempio n. 9
0
    def get_backpack(self):
        """
            Find and return the player's backpack. Returns a tuple
            containing the backpack and the coordinates
            """
        # Find backpack in screenshot
        screenshot = utils.take_screenshot(False)
        result = cv2.matchTemplate(screenshot, self.backpack_template,
                                   cv2.TM_CCORR_NORMED)
        _, max_val, _, backpack_loc = cv2.minMaxLoc(result)

        # Failed to find the backpack with high confidence
        if max_val < 0.9:
            utils.log("SEVERE", "Unable to find backpack in screenshot")
            utils.quit_game()

        # Restrict screenshot to just include the player's backpack
        #backpack_loc = (backpack_loc[0] + 11, backpack_loc[1] + 33)
        backpack = screenshot[(backpack_loc[1]):(backpack_loc[1] + 144),
                              (backpack_loc[0]):(backpack_loc[0] + 128)]

        return (backpack, backpack_loc)
Esempio n. 10
0
    def buy_item(self, item, merchant_type):
        """
            Move to a merchant and buy an item
            """
        # Open the "Buy or Sell" window
        buy_or_sell = self.open_merchant_window(merchant_type)
        if not buy_or_sell:
            utils.log(
                "SEVERE",
                F"Failed to click on {merchant_type} and open 'Buy or Sell' after 10 attempts"
            )
            utils.quit_game()

        # Click the buy button
        buy_button = self.user_interface.wait_for_ui_element('buy')
        pyautogui.moveTo(buy_button[0] + 10, buy_button[1] + 10, 0.15)
        pyautogui.click()

        # Wait for the buy menu to open
        self.user_interface.wait_for_ui_element('buyMenu')

        # Find the item to buy
        item_loc = self.user_interface.wait_for_ui_element(item)
        pyautogui.moveTo(item_loc[0] + 6, item_loc[1] + 6, 0.15)
        pyautogui.doubleClick()

        # Confirm the sale
        check_mark = self.user_interface.wait_for_ui_element('checkMark')
        pyautogui.moveTo(check_mark[0] + 5, check_mark[1] + 5, 0.15)
        pyautogui.click()

        # Click cancel to leave the window
        cancel = self.user_interface.wait_for_ui_element('cancel')
        pyautogui.moveTo(cancel[0] + 5, cancel[1] + 5, 0.15)
        pyautogui.click()

        pyautogui.moveTo(400, 400)
        utils.log("INFO", F"Bought a {item}")
Esempio n. 11
0
    def get_item(self, item, exit_on_failure=False):
        """
            Find an item in the player's backpack and return it's pixel coordinates
            """
        # Get the player's backpack
        backpack, backpack_loc = self.get_backpack()

        # Search the backpack for the item
        result = cv2.matchTemplate(backpack, self.item_templates[item],
                                   cv2.TM_CCORR_NORMED)
        _, max_val, _, item_loc = cv2.minMaxLoc(result)

        # Failed to find item in backpack with high confidence
        if max_val < 0.9:
            if exit_on_failure:
                utils.log(
                    "SEVERE",
                    F"Unable to find {item} in backpack. max_val: {max_val:3.2f}"
                )
                utils.quit_game()
            else:
                return False

        return (backpack_loc[0] + item_loc[0], backpack_loc[1] + item_loc[1])
Esempio n. 12
0
    def forge(self):
        """
            Forge all ingots into Battle Axes
            """
        # Get player's weight
        if self.player.is_weight_below_threshold(15):
            utils.log("INFO",
                      F"Weight is below threshold, selling {self.ITEM}s")
            self.sell_items()

        # Buy a hammer if player has none
        if not self.player.backpack.get_item('hammer'):
            # Hammer hay be obscured by a forged weapon, sell first
            self.sell_items()

            # Still no hammer, buy one
            if not self.player.backpack.get_item('hammer'):
                utils.log("INFO", "No hammers remain, buying a hammer")
                self.merchant.buy_item('hammer',
                                       self.merchant.MERCHANTS.BLACKSMITH)

        # Move to the anvil, if already there, nothing happens
        self.move.go_to_anvil()

        # Find the ingots in the player's backpack
        ingot = self.player.backpack.get_item('ingot')
        if not ingot:
            utils.log("INFO", F"No ingots remain, selling {self.ITEM}s")
            self.sell_items()
            self.move.go_to_anvil()

            # Check again to see if there are ingots, may have been obscurred
            ingot = self.player.backpack.get_item('ingot')
            if not ingot:
                return self.player.TASKS.MINE

        # Use the hammer on the ingots
        self.player.backpack.use_item('hammer', (ingot[0] + 6, ingot[1] + 6),
                                      (7, 3))

        # Allow the blacksmith menu to fail a few times
        if not self.user_interface.wait_for_ui_element('blacksmithMenu',
                                                       False):
            self.errors += 1

            # Something must actually be wrong
            if self.errors == 5:
                utils.log("SEVERE",
                          "Failed to open blacksmith menu after 5 attempts")
                utils.quit_game()

            # Start the forge task over again
            return self.player.TASKS.FORGE

        # Found the menu, reset error count
        self.errors = 0

        # Forge a Battle Axe
        weapon = self.user_interface.wait_for_ui_element(self.ITEM)
        pyautogui.moveTo(weapon[0] + 9, weapon[1] + 10, 0.15)
        pyautogui.doubleClick()

        # Continue forging
        return self.player.TASKS.FORGE
Esempio n. 13
0
    def move_to(self, destination, mining=False):
        """
            Finds a path from the players position to the destination using the aStar
            shortest path algorithm.  Once a path is found, the player takes a step.
            After each step, the surroundings are examined again and a new path is
            calculated. This extra work is required because obstacles such as the shopkeepers
            can move and impede the players movement.
            """
        error = 0
        turns_without_moving = 0
        while not self.game_map.player_position == destination:
            # Find a path to the destination
            path = astar.get_path(self.game_map.game_map,
                                  self.game_map.player_position, destination)

            # Failed to get a path, retry up to 3 times
            if not path:
                error += 1
                utils.log(
                    "WARN",
                    F"Failed to get path from {self.game_map.player_position} to {destination}"
                )

                # Update map incase something was mislabeled
                self.game_map.update_map()

                # Try mining at a different mountain
                if mining:
                    return False

                if error == 10:
                    utils.log("SEVERE", "Failed to get path after 10 attempts")
                    utils.debug_print_matrix(self.game_map.game_map.T)
                    utils.quit_game()

                # Wait for merchant to move
                blacksmith_errors = 0
                while self.game_map.game_map[
                        destination] == self.game_map.TILES.BLACKSMITH.value:
                    # Don't wait forever for him to move
                    blacksmith_errors += 1
                    if blacksmith_errors == 20:
                        utils.log(
                            "SEVERE",
                            "Waited 100 seconds for blacksmith to move, suspect error"
                        )
                        utils.quit_game()

                    utils.log(
                        "INFO",
                        "Blacksmith is blocking spot, waiting for him to move")
                    sleep(5)
                continue

            # Reset error count
            error = 0

            # Get the next move from the path
            step_to = path[0]
            destination = path[-1]

            # Take a step towards the destination
            previous_position = self.game_map.player_position
            self.step(step_to)

            # Check if the player has gone several turns without movement
            if previous_position == self.game_map.player_position:
                turns_without_moving += 1
            else:
                turns_without_moving = 0

            if turns_without_moving == 10:
                utils.log("SEVERE", "Failed to move after 10 attempts")
                utils.debug_print_matrix(self.game_map.game_map.T)
                utils.quit_game()
        return True
Esempio n. 14
0
screenshot = utils.take_screenshot(False)
result = cv2.matchTemplate(screenshot, user_interface.templates['sponsored'], cv2.TM_CCORR_NORMED)
_, max_val, _, max_loc = cv2.minMaxLoc(result)

# Found the blocking window window with high confidence
if max_val > 0.9:
    click_at = (max_loc[0] + 428, max_loc[1] + 144)
    utils.log("INIT", "Closed blocking window")
    pyautogui.moveTo(click_at[0], click_at[1], 0.15)
    pyautogui.click()
    sleep(5)

# Bring game to foreground
utils.bring_game_to_foreground()

# Detect environment
screenshot = utils.take_screenshot()
game_map.update_player_position(screenshot)
utils.log("INIT", F"Player location initialized")
game_map.update_map()
utils.log("INIT", "Field of view mapped")
utils.log("INIT", "Initialization complete")
utils.log("INIT", "====================================================")
try:
    while utils.bring_game_to_foreground():
        player.perform_task()
except Exception as exception:
    utils.log("SEVERE", exception)
    utils.log("SEVERE", traceback.format_exc())
    utils.quit_game()
Esempio n. 15
0
    def update_player_position(self, screenshot):
        """To update the player position the following steps are taken.
            1. The sextant is location and used
            2. The coordinates are parsed and normalized
            3. Old player position is removed from map
            4. New player position is added to the map

            If any of these operations fails, the game will quit.

            Returns tuple containing player position as well as the tile map
            """

        sextant_x = None
        sextant_y = None
        errors = 0

        while sextant_x is None:
            # Move mouse to a neutral position that won't obstruct template matching
            pyautogui.moveTo(400, 400)

            # Find and use the sextant
            self.backpack.use_item('sextant', None, (5, 2), True)

            # Take a new screenshot that includes the location
            screenshot = utils.take_screenshot()

            # Find the current position
            position = screenshot[450:465, 120:180]

            # Resize and parse the image to a string
            position = cv2.resize(position, (0, 0), fx=5, fy=5)
            position = cv2.bitwise_not(position)
            position = cv2.blur(position, (8, 8))

            text = ''

            try:
                text = pytesseract.image_to_string(position, config='--psm 8')
                # Split the text into coordinates
                sextant_x, sextant_y = text.split(", ")[0::1]
            except UnicodeDecodeError:
                utils.log("SEVERE",
                          F"Unable to parse sextant coordinates string")
                utils.quit_game()
            except ValueError as value_error:
                utils.log("WARN", F"{value_error}")
                # Move mouse to a neutral position that won't obstruct template matching
                pyautogui.moveTo(400, 400)
                errors += 1

            if errors == 10:
                utils.log("SEVERE", F"Sextant failed 10 times")
                utils.quit_game()

        # Normalize the coordinates to fit the map indicies
        normalized_x = int(sextant_x) - utils.NORMALIZATION_CONSTANT
        normalized_y = int(sextant_y) - utils.NORMALIZATION_CONSTANT

        # Remove old player position from the map
        self.game_map[self.game_map ==
                      self.TILES.PLAYER.value] = self.TILES.ACCESSIBLE.value
        self.player_position = (normalized_x, normalized_y)
        self.game_map[self.player_position] = self.TILES.PLAYER.value