예제 #1
0
 def highlightGrid(self, event):
     self.mainWindow.hide()
     combat_grid = Grid(env.COMBAT_R, env.VCELLS, env.HCELLS)
     combat_grid.parse()
     self.overlay = GridOverlay(combat_grid)
     self.overlay.highlightEnded.connect(self.mainWindow.show)
     self.overlay.highlight(2)
예제 #2
0
파일: fight.py 프로젝트: Me0w1ng/bot2pix
class Fighter(Walker):
    def __init__(self, spell, workdir, name="Fighter"):
        super(Fighter, self).__init__(workdir, name)
        self.spell = spell
        self.fightEndObs = CombatEndObs(self)
        self.fightStartObs = CombatStartObs(self)
        self.grid = Grid(dofus.COMBAT_R, dofus.VCELLS, dofus.HCELLS)
        self.mobs_killed = 0
        self.nbr_fights = 0

    def run(self):
        self.fightStartObs.start()
        self.fightEndObs.start()
        super(Fighter, self).run()

    def onCombatEnded(self):
        self.combatEndReached.set()
        self.combatStarted.clear()
        dofus.COMBAT_ENDED_POPUP_CLOSE_R.click()
        dofus.COMBAT_ENDED_POPUP_R.waitVanish(dofus.COMBAT_ENDED_POPUP_P)
        sleep(0.7)

    def onCombatStarted(self, event):
        match = dofus.LVL_UP_INFO_R.find(dofus.CLOSE_POPUP_P)
        if match:
            match.click()
        try:
            logging.info("Combat started")
            self.combatStarted.set()
            self.combatEndReached.clear()
            self.combatEnded.clear()
            self.dead = False
            if event == 'combat':
                pyautogui.press(dofus.SKIP_TURN_SHORTCUT)
            dofus.OUT_OF_COMBAT_R.hover()
            self.combatAlgo()
            sleep(10)
            self.combatEnded.set()
            self.nbr_fights += 1
            logging.debug('Combat ended')
        except Exception:
            logging.error("Fatal error in main run!", exc_info=True)
            self.interrupt()
        logging.info(f"I farmed {self.nbr_fights} fights")
        logging.info('Goodbye cruel world.')

    def waitTurn(self, rate=3, timeout=60 * 2):
        """
        Waits for bot turn.
        :param timeout: timeout in seconds
        :param rate: scan rate
        """
        s = perf_counter()
        while not self.killsig.is_set() and\
                not self.combatEndReached.is_set() and\
                perf_counter() - s < timeout:
            s = perf_counter()
            if self.myTurn():
                logging.debug('Bot turn started')
                return True
            elapsed = perf_counter() - s
            sleep((1 / rate) - elapsed)
        raise WaitTurnTimedOut

    def interrupt(self):
        """
        interrupt thread.
        """
        dofus.READY_R.stopWait.set()
        self.combatEnded.set()
        super(Fighter, self).interrupt()

    def combatAlgo(self):
        nbr_errors = 0
        self.mobs_killed = 0
        while not self.combatEndReached.wait(1):
            self.checkCreatureMode()
            try:
                if not self.myTurn():
                    self.waitTurn()
                self.playTurn()
                nbr_errors = 0
            except (FindPathFailed, ParseGridFailed, MoveToCellFailed,
                    UseSpellFailed, WaitTurnTimedOut) as e:
                if self.combatEndReached.is_set() or self.killsig.is_set():
                    return True
                if self.disconnected.is_set():
                    self.connected.wait()
                elif dofus.COMBAT_R.find(dofus.REDUCE_BOX_P):
                    dofus.COMBAT_R.find(dofus.REDUCE_BOX_P).click()
                else:
                    logging.error(str(e), exc_info=True)
                    nbr_errors += 1
                    logging.debug(
                        f"Will skip bots turn for the '{nbr_errors}'th time!")
                    if nbr_errors == 10:
                        logging.debug(
                            "Reached maximum of turns skip on error. Will resign combat."
                        )
                        self.dead = True
                        self.resign()
                        return False
            self.skipTurn()

    @staticmethod
    def skipTurn():
        pyautogui.press(dofus.SKIP_TURN_SHORTCUT)

    @staticmethod
    def myTurn():
        return dofus.MY_TURN_CHECK_L.getpixel() == dofus.MY_TURN_C

    def playTurn(self):
        """
        Play turn loop
        """
        usedSpells = 0
        while not self.combatEndReached.wait(
                3) and usedSpells < self.spell['nbr']:
            self.parseCombatGrid()
            if not self.mobs_killed:
                self.mobs_killed = len(self.grid.mobs)
            if self.combatEndReached.wait(0.5):
                return
            mob, path = self.findPathToTarget(self.grid.bot,
                                              self.spell['range'],
                                              self.grid.mobs)
            if not mob:
                mob, path = self.findPathToTarget(self.grid.bot,
                                                  self.spell['range'],
                                                  self.grid.invoke)
            if not mob:
                raise FindPathFailed(self.grid)
            if path:
                cell = self.cellToTarget(path)
                if cell:
                    self.moveToCell(cell)
                    if cell == path[-1]:
                        if self.combatEndReached.wait(0.5):
                            return
                        self.useSpell(self.spell, mob)
                    else:
                        break
            else:
                self.useSpell(self.spell, mob)
            usedSpells += 1

    def parseCombatGrid(self, timeout=5):
        """
        Parse combat grid.
        :param timeout:  time out in seconds
        :return: True if all good else raise ParseGridFailed
        """
        s = perf_counter()
        while not self.killsig.is_set() and\
                not self.combatEndReached.is_set() and\
                perf_counter() - s < timeout:
            if self.grid.parse():
                return True

    @staticmethod
    def cellToTarget(path):
        """
        Get nearest reachable cell to target
        :param path: path to targeted cell
        :return: Reachable cell if any else None
        """
        if path[-1].reachable():
            return path[-1]
        for idx, cell in enumerate(path):
            if not cell.reachable():
                if idx == 0:
                    return None
                return path[idx - 1]
        return None

    def moveToCell(self, cell, timeout=10):
        """
        Move to a target cell.
        :param cell: cell object
        :param timeout: time out in seconds
        :return: True if all good else raise MoveToCellFailed
        """
        s = perf_counter()
        while not self.killsig.is_set() and \
                not self.combatEndReached.is_set() and \
                perf_counter() - s < timeout:
            cell.click()
            dofus.OUT_OF_COMBAT_R.hover()
            if cell.waitAppear(dofus.ObjType.BOT, 1):
                return True
        raise MoveToCellFailed(cell)

    @staticmethod
    def useSpell(spell, target, timeout=5):
        """
        Cast given spell on the target
        :param spell: spell dictionary
        :param target: targeted cell
        :param timeout: time out in seconds
        :return: True if all good else raise UseSpellFailed
        """
        pyautogui.press(spell['shortcut'])
        target.click()
        dofus.OUT_OF_COMBAT_R.hover()
        if target.waitAnimation(timeout):
            return True

        raise UseSpellFailed(target)

    def findPathToTarget(self, start_cell, po, targets):
        """
        Find path to the closest ldv to hit a mob.
        :param start_cell: position of the character
        :param po: range of the ldv
        :param targets: positions of the mobs
        :return: cell of the mob, path to the ldv if any else None
        """
        logging.debug("searching path to mobs")
        queue = collections.deque([[start_cell]])
        seen = {start_cell.indexes()}
        while not self.killsig.is_set() and not self.combatEndReached.is_set(
        ) and queue:
            path = queue.popleft()
            curr = path[-1]
            for mob in targets:
                if curr.inLDV(mob, po):
                    return mob, path[1:]
            for cell in curr.neighbors():
                if (cell.i, cell.j) not in seen and not cell.occupied():
                    queue.append(path + [cell])
                    seen.add(cell.indexes())
        return None, None

    @staticmethod
    def checkCreatureMode():
        """
        Check creature mode at the start of the fight if its not checked.
        """
        if dofus.CREATURE_MODE_R.find(dofus.CREATURE_MODE_OFF_P,
                                      grayscale=False):
            dofus.CREATURE_MODE_R.click()
            dofus.OUT_OF_COMBAT_R.hover()

    def harvestCombats(self, mobs_patterns, max_tries=10, shuffle=False):
        """
        Look for mobs patterns and try to enter combats.
        :param mobs_patterns: list of images
        :param max_tries: max number of clicks on mobs group
        :param shuffle: if you want to shuffle mobs patterns before matching
        :return: nbr of mobs farmed and the matched patterns with nbr of times matched
        """
        nbr_fails = 0
        result = {"farmed": 0, "matched": {}}
        while not self.killsig.is_set() and nbr_fails < max_tries:
            logging.debug("Searching for mobs group...")
            tgt, idx = dofus.COMBAT_R.findAny(mobs_patterns,
                                              threshold=0.8,
                                              shuffle=shuffle)
            if tgt:
                logging.debug("I found a mob group")
                if self.enterCombat(tgt):
                    nbr_fails = 0
                    if idx not in result['matched']:
                        result['matched'][idx] = 0
                    result['matched'][idx] += 1
                    self.combatEnded.wait()
                    result["farmed"] += self.mobs_killed
                    if self.dead:
                        logging.debug("Walker dead during combat!")
                        break
                else:
                    nbr_fails += 1
            else:
                logging.debug("No mobs found")
                break
        return result

    def enterCombat(self, tgt, timeout=3.5):
        """
        Click on a mob group and wait for the combat to start.
        :param tgt: pos of mobs group in the screen
        :param timeout: timeout in seconds
        :return: True if all good else False
        """
        tgt.click()
        logging.debug("Clicked on mobs group")
        s = perf_counter()
        if self.combatStarted.wait(timeout):
            logging.debug(f"Enter combat took: {perf_counter() - s}")
            return True
        logging.debug("Couldn't open combat!")
        return False

    @staticmethod
    def resign():
        """
        Abandon combat.
        """
        dofus.RESIGN_BUTTON_LOC.click()
        dofus.RESIGN_POPUP_R.waitAppear(dofus.RESIGN_POPUP_P)
        dofus.RESIGN_CONFIRM_L.click()
        dofus.RESIGN_POPUP_R.waitVanish(dofus.RESIGN_POPUP_P)
        dofus.DEFEAT_POPUP_R.waitAppear(dofus.DEFEAT_POPUP_P)
        dofus.DEFEAT_POPUP_CLOSE_L.click()
        dofus.DEFEAT_POPUP_R.waitVanish(dofus.DEFEAT_POPUP_P)