Пример #1
0
    def lv_triggered(self):
        limit = self.config.StopCondition_ReachLevel
        if not limit:
            return False

        for i in range(6):
            before, after = self._lv_before_battle[i], self.lv[i]
            if after > before > 0:
                logger.info(f'Position {i} LV.{before} -> LV.{after}')
            if after >= limit > before > 0:
                if after - before == 1 or after < 35:
                    logger.info(f'Position {i} LV.{limit} Reached')
                    self.config.LV_TRIGGERED = True
                    return True
                else:
                    logger.warning(f'Level gap between {before} and {after} is too large. '
                                   f'This will not be considered as a trigger')

        return False
Пример #2
0
    def shop_get_choice(self, item):
        """
        Gets the configuration saved in
        for the appropriate variant shop
        i.e. GuildShop_X

        Args:
            item (Item):

        Returns:
            str

        Raises:
            ScriptError
        """
        group = item.group
        if group == 'pr':
            postfix = None
            for _ in range(3):
                if _:
                    self.device.sleep((0.3, 0.5))
                    self.device.screenshot()

                for idx, btn in enumerate(SHOP_SELECT_PR):
                    if self.appear(btn, offset=(20, 20)):
                        postfix = f'{idx + 1}'
                        break

                if postfix is not None:
                    break
                logger.warning('Failed to detect PR series, '
                               'app may be lagging or frozen')
        else:
            postfix = f'_{item.tier.upper()}'

        ugroup = group.upper()
        class_name = self.__class__.__name__
        try:
            return getattr(self.config, f'{class_name}_{ugroup}{postfix}')
        except Exception:
            logger.critical(f'No configuration with name '
                            f'\'{class_name}_{ugroup}{postfix}\'')
            raise ScriptError
Пример #3
0
    def get_info(self, main):
        """
        Args:
            main (ModuleBase):

        Returns:
            int, int, int: Index of the active nav item, leftmost nav item, and rightmost nav item.
        """
        total = []
        active = []
        for index, button in enumerate(self.grids.buttons):
            if main.image_color_count(button,
                                      color=self.active_color,
                                      threshold=self.active_threshold,
                                      count=self.active_count):
                total.append(index)
                active.append(index)
            elif main.image_color_count(button,
                                        color=self.inactive_color,
                                        threshold=self.inactive_threshold,
                                        count=self.inactive_count):
                total.append(index)

        if len(active) == 0:
            # logger.warning(f'No active nav item found in {self.name}')
            active = None
        elif len(active) == 1:
            active = active[0]
        else:
            logger.warning(
                f'Too many active nav items found in {self.name}, items: {active}'
            )
            active = active[0]

        if len(total) < 2:
            logger.warning(
                f'Too few nav items found in {self.name}, items: {total}')
        if len(total) == 0:
            left, right = None, None
        else:
            left, right = min(total), max(total)

        return active, left, right
Пример #4
0
    def wait_until(self, future):
        """
        Wait until a specific time.

        Args:
            future (datetime):
        """
        seconds = future.timestamp() - datetime.now().timestamp() + 1
        if seconds <= 0:
            logger.warning(f'Wait until {str(future)}, but sleep length < 0, skip waiting')
        
        if self.stop_event is not None:
            self.stop_event.wait(seconds)
            if self.stop_event.is_set():
                logger.info("Update event detected")
                logger.info(f"Alas [{self.config_name}] exited.")
                exit(0)
        else:
            time.sleep(seconds)
Пример #5
0
    def connect(self, serial):
        """Connect to a device.

        Args:
            serial (str): device serial or device address.

        Returns:
            uiautomator2.UIAutomatorServer: Device.
        """
        self._adb_connect(serial)
        try:
            device = u2.connect(serial)
            return device
        except AssertionError:
            logger.warning(
                'AssertionError when connecting emulator with uiautomator2.')
            logger.warning(
                'If you are using BlueStacks, you need to enable ADB in the settings of your emulator.'
            )
    def parse_time(self, string):
        """
        Args:
            string (str): Such as 01:00:00, 05:47:10, 17:50:51.

        Returns:
            timedelta: datetime.timedelta instance.
        """
        string = string.replace('D', '0')  # Poor OCR
        result = re.search('(\d+):(\d+):(\d+)', string)
        if not result:
            logger.warning(f'Invalid time string: {string}')
            self.valid = False
            return None
        else:
            result = [int(s) for s in result.groups()]
            return timedelta(hours=result[0],
                             minutes=result[1],
                             seconds=result[2])
    def _commission_ensure_mode(self, mode):
        if self.appear(COMMISSION_DAILY):
            current = 'daily'
        elif self.appear(COMMISSION_URGENT):
            current = 'urgent'
        else:
            logger.warning('Unknown Commission mode')
            return False
        if current == mode:
            return False

        if mode == 'daily':
            self.device.click(COMMISSION_DAILY)
        if mode == 'urgent':
            self.device.click(COMMISSION_URGENT)

        self.device.sleep(0.3)
        self.device.screenshot()
        return True
Пример #8
0
    def _handle_ambush_evade(self):
        logger.info('Map ambushed')
        _ = self._load_ambush_template
        self.wait_until_appear_then_click(MAP_AMBUSH_EVADE)

        self.wait_until_appear(INFO_BAR_1)
        image = info_letter_preprocess(
            np.array(self.device.image.crop(INFO_BAR_DETECT.area)))

        if TEMPLATE_AMBUSH_EVADE_SUCCESS.match(image):
            logger.attr('Ambush_evade', 'success')
        elif TEMPLATE_AMBUSH_EVADE_FAILED.match(image):
            logger.attr('Ambush_evade', 'failed')
            self.combat(expected_end='no_searching')
        else:
            logger.warning('Unrecognised info when ambush evade.')
            self.ensure_no_info_bar()
            if self.combat_appear():
                self.combat()
Пример #9
0
    def _dorm_feed_long_tap(self, button, count):
        timeout = Timer(count // 5 + 5).start()
        x, y = random_rectangle_point(button.button)
        self.device.u2.touch.down(x, y)

        while 1:
            self.device.u2.touch.move(x, y)
            time.sleep(.01)
            self.device.screenshot()

            if not self._dorm_has_food(button) \
                    or self.handle_info_bar() \
                    or self.handle_popup_cancel('dorm_feed'):
                break
            if timeout.reached():
                logger.warning('Wait dorm feed timeout')
                break

        self.device.u2.touch.up(x, y)
Пример #10
0
    def set(self,
            position,
            main,
            random_range=(-0.05, 0.05),
            skip_first_screenshot=True):
        """
        Set scroll to a specific position.

        Args:
            position (float, int): 0 to 1.
            main (ModuleBase):
            random_range (tuple(int, float)):
            skip_first_screenshot:
        """
        logger.info(f'{self.name} set to {position}')
        self.drag_interval.clear()
        if position == 0:
            random_range = np.subtract(0, self.edge_add)
        if position == 1:
            random_range = self.edge_add

        while 1:
            if skip_first_screenshot:
                skip_first_screenshot = False
            else:
                main.device.screenshot()

            current = self.cal_position(main)
            if abs(position - current) < self.drag_threshold:
                break
            if not self.length:
                logger.warning('Scroll disappeared, assume scroll set')
                break

            if self.drag_interval.reached():
                p1 = random_rectangle_point(self.position_to_screen(current),
                                            n=1)
                p2 = random_rectangle_point(self.position_to_screen(
                    position, random_range=random_range),
                                            n=1)
                main.device.swipe(p1, p2, name=self.name)
                main.device.sleep(0.3)
                self.drag_interval.reset()
Пример #11
0
    def commission_name_parse(self, string):
        """
        Args:
            string (str): Commission name, such as 'NYB要员护卫'.

        Returns:
            str: Commission genre, such as 'urgent_gem'.
        """
        # string = string.replace(' ', '').replace('-', '')
        if self.is_doa_commission():
            return 'doa_daily'
        for key, value in dictionary_en.items():
            for keyword in value:
                if keyword in string:
                    return key

        logger.warning(f'Name with unknown genre: {string}')
        self.valid = False
        return ''
Пример #12
0
    def clear_boss(self):
        grids = self.map.select(is_boss=True)
        grids = grids.add(self.map.select(may_boss=True, is_enemy=True))
        logger.info('May boss: %s' % self.map.select(may_boss=True))
        logger.info('May boss and is enemy: %s' %
                    self.map.select(may_boss=True, is_enemy=True))
        logger.info('Is boss: %s' % self.map.select(is_boss=True))
        # logger.info('Grids: %s' % grids)
        if grids:
            logger.hr('Clear BOSS')
            grids = grids.sort('weight', 'cost')
            logger.info('Grids: %s' % str(grids))
            self._goto(grids[0], expected='boss')
            raise CampaignEnd('BOSS Clear.')

        logger.warning('BOSS not detected, trying all boss spawn point.')
        self.clear_potential_boss()

        return False
Пример #13
0
    def _campaign_separate_name(name):
        """
        Args:
            name (str): Stage name in lowercase, such as 7-2, d3, sp3.

        Returns:
            tuple[str]: Campaign_name and stage index in lowercase, Such as ['7', '2'], ['d', '3'], ['sp', '3'].
        """
        if name == 'sp':
            return 'ex_sp', '1'
        elif '-' in name:
            return name.split('-')
        elif name.startswith('sp'):
            return 'sp', name[-1]
        elif name[-1].isdigit():
            return name[:-1], name[-1]

        logger.warning(f'Unknown stage name: {name}')
        return name[0], name[1:]
    def _tactical_books_choose(self):
        """
        Choose tactical book according to config.
        """
        books = BookGroup([
            Book(self.device.image, button) for button in BOOKS_GRID.buttons()
        ]).select(valid=True)
        logger.attr('Book_count', len(books))
        for index in range(1, 4):
            logger.info(f'Book_T{index}: {books.select(tier=index)}')
        if not books:
            logger.warning('No book found.')
            raise ScriptError('No book found.')

        # if not time_range_active(self.config.TACTICAL_NIGHT_RANGE):
        #     tier = self.config.TACTICAL_BOOK_TIER
        #     exp = self.config.TACTICAL_EXP_FIRST
        # else:
        #     tier = self.config.TACTICAL_BOOK_TIER_NIGHT
        #     exp = self.config.TACTICAL_EXP_FIRST_NIGHT
        # book = books.choose(tier=tier, exp=exp)

        book = books.choose(tier_max=self.config.TACTICAL_BOOK_TIER_MAX,
                            tier_min=self.config.TACTICAL_BOOK_TIER_MIN,
                            exp=self.config.TACTICAL_EXP_FIRST)

        if book is not None:
            while 1:
                self.device.click(book.button)
                self.device.screenshot()
                if book.check_selected(self.device.image):
                    break
            self.device.click(TACTICAL_CLASS_START)
        else:
            # cancel_tactical, use_the_first_book
            if self.config.TACTICAL_IF_NO_BOOK_SATISFIED == 'use_the_first_book':
                logger.info('Choose first book')
                self.device.click(books[0].button)
                self.device.sleep((0.3, 0.5))
                self.device.click(TACTICAL_CLASS_START)
            else:
                logger.info('Cancel tactical')
                self.device.click(TACTICAL_CLASS_CANCEL)
Пример #15
0
    def _research_has_finished_at(self, button):
        """
        Args:
            button (Button):

        Returns:
            bool: True if a research finished
        """
        color = get_color(self.device.image, button.area)
        if np.max(color) - np.min(color) < 40:
            logger.warning(f'Unexpected color: {color}')
        index = np.argmax(color)  # R, G, B
        if index == 1:
            return True  # Green
        elif index == 2:
            return False  # Blue
        else:
            logger.warning(f'Unexpected color: {color}')
            return False
Пример #16
0
    def _sos_signal_select(self, chapter):
        """
        select a SOS signal

        Args:
            chapter (int): 3 to 10.

        Pages:
            in: page_campaign
            out: page_campaign, in target chapter

        Returns:
            bool: whether select successful
        """
        logger.hr(f'Select chapter {chapter} signal ')
        self.ui_click(SIGNAL_SEARCH_ENTER,
                      appear_button=CAMPAIGN_CHECK,
                      check_button=SIGNAL_LIST_CHECK,
                      skip_first_screenshot=True)
        if chapter in [3, 4, 5]:
            positions = [0.0, 0.5, 1.0]
        elif chapter in [6, 7]:
            positions = [0.5, 1.0, 0.0]
        elif chapter in [8, 9, 10]:
            positions = [1.0, 0.5, 0.0]
        else:
            logger.warning(f'Unknown SOS chapter: {chapter}')
            positions = [0.0, 0.5, 1.0]

        for scroll_position in positions:
            if self._sos_scroll.appear(main=self):
                self._sos_scroll.set(scroll_position,
                                     main=self,
                                     distance_check=False)
            else:
                logger.info(
                    'SOS signal scroll not appear, skip setting scroll position'
                )
            target_button = self._find_target_chapter(chapter)
            if target_button is not None:
                self._sos_signal_confirm(entrance=target_button)
                return True
        return False
Пример #17
0
    def _equip_sidebar_click(self, index):
        """
        Args:
            index (int):
                5 for retrofit.
                4 for enhancement.
                3 for limit break.
                2 for gem / equipment.
                1 for detail.

        Returns:
            bool: if changed.
        """
        current = 0
        total = 0

        for idx, button in enumerate(DETAIL_SIDEBAR.buttons()):
            image = np.array(self.device.image.crop(button.area))
            if np.sum(image[:, :, 0] > 235) > 100:
                current = idx + 1
                total = idx + 1
                continue
            if np.sum(color_similarity_2d(image, color=(140, 162,
                                                        181)) > 221) > 100:
                total = idx + 1
            else:
                break
        if not current:
            logger.warning('No ship details sidebar active.')
        if total == 4:
            current = 5 - current
        elif total == 5:
            current = 6 - current
        else:
            logger.warning('Ship details sidebar total count error.')

        logger.attr('Detail_sidebar', f'{current}/{total}')
        if current == index:
            return False

        self.device.click(DETAIL_SIDEBAR[0, total - index])
        return True
Пример #18
0
    def os_init(self):
        """
        Call this method before doing any Operation functions.

        Pages:
            in: IN_MAP or IN_GLOBE or page_os or any page
            out: IN_MAP
        """
        logger.hr('OS init', level=1)
        self.config.override(Submarine_Fleet=1, Submarine_Mode='every_combat')

        # UI switching
        if self.is_in_map():
            logger.info('Already in os map')
        elif self.is_in_globe():
            self.os_globe_goto_map()
        else:
            if self.ui_page_appear(page_os):
                self.ui_goto_main()
            self.ui_ensure(page_os)

        # Init
        self.zone_init()

        # self.map_init()
        self.hp_reset()
        self.handle_fleet_repair(revert=False)

        # Exit from special zones types, only SAFE and DANGEROUS are acceptable.
        if self.is_in_special_zone():
            logger.warning(
                'OS is in a special zone type, while SAFE and DANGEROUS are acceptable'
            )
            self.map_exit()

        # Clear current zone
        if self.zone.is_port:
            logger.info('In port, skip running first auto search')
            self.handle_ash_beacon_attack()
        else:
            self.run_auto_search()
            self.handle_fleet_repair(revert=False)
Пример #19
0
    def find_bluestacks5_hyperv(serial):
        """
        Find dynamic serial of Bluestacks5 Hyper-v.
        Args:
            serial (str): 'bluestacks5-hyperv', 'bluestacks5-hyperv-1' for multi instance, and so on.
        Returns:
            str: 127.0.0.1:{port}
        """
        from winreg import ConnectRegistry, OpenKey, QueryInfoKey, EnumValue, CloseKey, HKEY_LOCAL_MACHINE

        logger.info("Use Bluestacks5 Hyper-v")
        logger.info("Reading Realtime adb port")

        if serial == "bluestacks5-hyperv":
            parameter_name = "bst.instance.Nougat64.status.adb_port"
        else:
            parameter_name = f"bst.instance.Nougat64_{serial[19:]}.status.adb_port"

        reg_root = ConnectRegistry(None, HKEY_LOCAL_MACHINE)
        sub_dir = f"SOFTWARE\\BlueStacks_nxt"
        bs_keys = OpenKey(reg_root, sub_dir)
        bs_keys_count = QueryInfoKey(bs_keys)[1]
        for i in range(bs_keys_count):
            key_name, key_value, key_type = EnumValue(bs_keys, i)
            if key_name == "UserDefinedDir":
                logger.info(f"Configuration file directory: {key_value}")
                with open(f"{key_value}\\bluestacks.conf",
                          'r',
                          encoding='utf-8') as f:
                    content = f.read()
                    port = re.findall(rf'{parameter_name}="(.*?)"\n', content,
                                      re.S)
                    if len(port) > 0:
                        logger.info(f"Match to dynamic port: {port[0]}")
                        serial = f"127.0.0.1:{port[0]}"
                    else:
                        logger.warning(f"Did not match the result: {serial}.")
                break

        CloseKey(bs_keys)
        CloseKey(reg_root)
        return serial
Пример #20
0
    def find_submarine(self):
        if not (self.config.SUBMARINE and self.map.select(is_submarine_spawn_point=True)):
            return False

        fleets = self.map.select(is_submarine=True)
        count = fleets.count
        if count == 1:
            self.fleet_submarine = fleets[0].location
        elif count == 0:
            logger.info('No submarine found')
            # Try spawn points
            spawn_point = self.map.select(is_submarine_spawn_point=True)
            if spawn_point.count == 1:
                logger.info(f'Predict the only submarine spawn point {spawn_point[0]} as submarine')
                self.fleet_submarine = spawn_point[0].location
            else:
                logger.info(f'Having multiple submarine spawn points: {spawn_point}')
                # Try covered grids
                covered = SelectedGrids([])
                for grid in spawn_point:
                    covered = covered.add(self.map.grid_covered(grid, location=[(0, 1)]))
                covered = covered.filter(lambda g: g.is_enemy or g.is_fleet or g.is_siren or g.is_boss)
                if covered.count == 1:
                    spawn_point = self.map.grid_covered(covered[0], location=[(0, -1)])
                    logger.info(f'Submarine {spawn_point[0]} covered by {covered[0]}')
                    self.fleet_submarine = spawn_point[0].location
                else:
                    logger.info('Found multiple submarine spawn points being covered')
                    # Give up
                    self.find_all_submarines()
        else:
            logger.warning('Too many submarines: %s.' % str(fleets))
            self.find_all_submarines()

        if not len(self.fleet_submarine_location):
            logger.warning('Unable to find submarine, assume it is at map center')
            shape = self.map.shape
            center = (shape[0] // 2, shape[1] // 2)
            self.fleet_submarine = self.map.select(is_land=False).sort_by_camera_distance(center)[0].location

        self.show_submarine()
        return self.fleet_submarine_location
Пример #21
0
    def set(self, status, main, skip_first_screenshot=True):
        """
        Args:
            status (str):
            main (ModuleBase):
            skip_first_screenshot (bool):

        Returns:
            bool:
        """
        counter = 0
        changed = False
        warning_show_timer = Timer(5, count=10).start()
        while 1:
            if skip_first_screenshot:
                skip_first_screenshot = False
            else:
                main.device.screenshot()

            current = self.get(main=main)
            logger.attr(self.name, current)
            if current == status:
                return changed

            if current == 'unknown':
                if warning_show_timer.reached():
                    logger.warning(f'Unknown {self.name} switch')
                    warning_show_timer.reset()
                    if counter >= 1:
                        logger.warning(
                            f'{self.name} switch {status} asset has evaluated to unknown too many times, asset should be re-verified'
                        )
                        return False
                    counter += 1
                continue

            click_status = status if self.is_choice else current
            for data in self.status_list:
                if data['status'] == click_status:
                    main.device.click(data['click_button'])
                    main.device.sleep(data['sleep'])
                    changed = True
Пример #22
0
    def convert_radar_to_local(self, location):
        """
        Converts the coordinate on radar to the coordinate of local map view,
        also handles a rare game bug.

        Usually, OPSI camera focus on current fleet, which is (5, 4) in local view.
        The convert should be `local = view[np.add(radar, view.center_loca)]`
        However, Azur Lane may bugged, not focusing current.
        In this case, the convert should base on fleet position.

        Args:
            location: (x, y), Position on radar.

        Returns:
            OSGrid: Grid instance in self.view
        """
        location = location_ensure(location)

        fleets = self.view.select(is_current_fleet=True)
        if fleets.count == 1:
            center = fleets[0].location
        elif fleets.count > 1:
            logger.warning(
                f'Convert radar to local, but found multiple current fleets: {fleets}'
            )
            distance = np.linalg.norm(
                np.subtract(fleets.location, self.view.center_loca))
            center = fleets.grids[np.argmin(distance)].location
            logger.warning(
                f'Assuming the nearest fleet to camera canter is current fleet: {location2node(center)}'
            )
        else:
            logger.warning(
                f'Convert radar to local, but current fleet not found. '
                f'Assuming camera center is current fleet: {location2node(self.view.center_loca)}'
            )
            center = self.view.center_loca

        try:
            local = self.view[np.add(location, center)]
        except KeyError:
            logger.warning(
                f'Convert radar to local, but target grid not in local view. '
                f'Assuming camera center is current fleet: {location2node(self.view.center_loca)}'
            )
            center = self.view.center_loca
            local = self.view[np.add(location, center)]

        logger.info('Radar %s -> Local %s (fleet=%s)' %
                    (str(location), location2node(
                        local.location), location2node(center)))
        return local
Пример #23
0
    def globe_goto(self, zone, types=('SAFE', 'DANGEROUS'), refresh=False):
        """
        Goto another zone in OS.

        Args:
            zone (str, int, Zone): Name in CN/EN/JP, zone id, or Zone instance.
            types (tuple[str], list[str], str): Zone types, or a list of them.
                Available types: DANGEROUS, SAFE, OBSCURE, LOGGER, STRONGHOLD.
                Try the the first selection in type list, if not available, try the next one.
            refresh (bool): If already at target zone,
                set false to skip zone switching,
                set true to re-enter current zone to refresh.

        Pages:
            in: IN_MAP or IN_GLOBE
            out: IN_MAP
        """
        zone = self.name_to_zone(zone)
        logger.hr(f'Globe goto: {zone}')
        if self.zone == zone:
            if refresh:
                logger.info('Goto another zone to refresh current zone')
                self.globe_goto(self.zone_nearest_azur_port(self.zone), types=('SAFE', 'DANGEROUS'), refresh=False)
            else:
                logger.info('Already at target zone')
                return False
        # IN_MAP
        if self.is_in_map():
            self.os_map_goto_globe()
        # IN_GLOBE
        if not self.is_in_globe():
            logger.warning('Trying to move in globe, but not in os globe map')
            raise ScriptError('Trying to move in globe, but not in os globe map')
        # self.ensure_no_zone_pinned()
        self.globe_update()
        self.globe_focus_to(zone)
        self.zone_type_select(types=types)
        self.globe_enter(zone)
        # IN_MAP
        if hasattr(self, 'zone'):
            del self.zone
        self.get_current_zone()
Пример #24
0
    def guild_logistics(self):
        """
        Execute all actions in logistics

        Pages:
            in: GUILD_LOGISTICS
            out: GUILD_LOGISTICS
        """

        # Transition to Logistics
        if not self.guild_sidebar_ensure(3):
            logger.info(
                'Logistics ensurance failed, try again on next reward loop')
            return

        # Last screencapture should contain affiliation
        # color in top-right where guild coins is
        # Determine guild affiliation
        color = get_color(self.device.image,
                          GUILD_AFFILIATION_CHECK_LOGISTICS.area)
        if color_similar(color, (115, 146, 206)):
            is_azur_affiliation = True
        elif color_similar(color, (206, 117, 115)):
            is_azur_affiliation = False
        else:
            logger.warning(f'Unknown guild affiliation color: {color}')
            return

        # Handle logistics actions collect/accept
        # Exchange will be executed separately
        self._guild_logistics_collect(is_azur_affiliation)

        # Limit check whether can exchange to once a day
        if not self.config.record_executed_since(option=RECORD_OPTION,
                                                 since=RECORD_SINCE):
            # Handle action exchange, determine color of digit based on affiliation
            GUILD_EXCHANGE_LIMIT.letter = (
                173, 182, 206) if is_azur_affiliation else (214, 113, 115)
            limit = GUILD_EXCHANGE_LIMIT.ocr(self.device.image)
            if limit > 0:
                self._guild_exchange(limit, is_azur_affiliation)
            self.config.record_save(option=RECORD_OPTION)
Пример #25
0
    def ui_get_current_page(self, skip_first_screenshot=True):
        """
        Args:
            skip_first_screenshot:

        Returns:
            Page:
        """
        if not skip_first_screenshot or not hasattr(
                self.device, 'image') or self.device.image is None:
            self.device.screenshot()

        # Known pages
        for page in self.ui_pages:
            if page.check_button is None:
                continue
            if self.ui_page_appear(page=page):
                logger.attr('UI', page.name)
                self.ui_current = page
                return page

        # Unknown page but able to handle
        logger.info('Unknown ui page')
        if self.appear_then_click(GOTO_MAIN,
                                  offset=(20, 20)) or self.ui_additional():
            logger.info('Goto page_main')
            self.ui_current = page_unknown
            self.ui_goto(page_main, skip_first_screenshot=True)

        # Unknown page, need manual switching
        if hasattr(self, 'ui_current'):
            logger.warning(
                f'Unrecognized ui_current, using previous: {self.ui_current}')
        else:
            logger.info('Unable to goto page_main')
            logger.attr('EMULATOR__SCREENSHOT_METHOD',
                        self.config.Emulator_ScreenshotMethod)
            logger.attr('EMULATOR__CONTROL_METHOD',
                        self.config.Emulator_ControlMethod)
            logger.attr('SERVER', self.config.SERVER)
            logger.warning('Starting from current page is not supported')
            logger.warning(
                f'Supported page: {[str(page) for page in self.ui_pages]}')
            logger.warning(
                f'Supported page: Any page with a "HOME" button on the upper-right'
            )
            if not self.device.app_is_running():
                raise GameNotRunningError('Game not running')
            else:
                logger.critical(
                    'Please switch to a supported page before starting Alas')
                raise RequestHumanTakeover
    def _tactical_books_get(self, skip_first_screenshot=True):
        """
        Get books. Handle loadings, wait 10 times at max.
        When TACTICAL_CLASS_START appears, game may stuck in loading, wait and retry detection.
        If loading still exists, raise ScriptError.

        Returns:
            BookGroup:

        Pages:
            in: TACTICAL_CLASS_START
            out: TACTICAL_CLASS_START
        """
        prev = SelectedGrids([])
        for n in range(1, 16):
            if skip_first_screenshot:
                skip_first_screenshot = False
            else:
                self.device.screenshot()

            self.handle_info_bar(
            )  # info_bar appears when get ship in Launch Ceremony commissions

            books = SelectedGrids([
                Book(self.device.image, button)
                for button in BOOKS_GRID.buttons
            ]).select(valid=True)
            self.books = books
            logger.attr('Book_count', books.count)
            logger.attr('Books', str(books))

            # End
            if books and books.count == prev.count:
                return books
            else:
                prev = books
                if n % 3 == 0:
                    self.device.sleep(3)
                continue

        logger.warning('No book found.')
        raise ScriptError('No book found, after 15 attempts.')
Пример #27
0
    def __init__(self, name, series):
        """
        Args:
            name (str): Such as 'D-057-UL'
            series (int): Such as 1, 2, 3
        """
        self.valid = True
        # self.config = config
        self.name = self.check_name(name)
        self.series = f'S{series}'
        self.genre = ''
        self.duration = '24'
        self.ship = ''
        self.ship_rarity = ''
        self.need_coin = False
        self.need_cube = False
        self.need_part = False

        matched = False
        for data in LIST_RESEARCH_PROJECT:
            if data['name'] == self.name and data['series'] == series:
                matched = True
                self.data = data
                self.genre = data['name'][0]
                self.duration = str(data['time'] / 3600).rstrip('.0')
                for item in data['input']:
                    result = re.search(self.REGEX_INPUT,
                                       item['name'].replace(' ', '').lower())
                    if result:
                        self.__setattr__(f'need_{result.group(1)}', True)
                for item in data['output']:
                    result = re.search(self.REGEX_SHIP,
                                       item['name'].replace(' ', '').lower())
                    if not self.ship:
                        self.ship = result.group(1) if result else ''
                    if self.ship:
                        self.ship_rarity = 'dr' if self.ship in self.DR_SHIP else 'pry'
                break

        if not matched:
            logger.warning(f'Invalid research {self}')
            self.valid = False
Пример #28
0
    def get_next_task(self):
        """
        Returns:
            str: Name of the next task.
        """

        task = self.config.get_next()
        self.config.task = task
        self.config.bind(task)

        from module.base.resource import release_resources
        if self.config.task.command != 'Alas':
            release_resources(next_task=task.command)

        if task.next_run > datetime.now():
            logger.info(
                f'Wait until {task.next_run} for task `{task.command}`')
            method = self.config.Optimization_WhenTaskQueueEmpty
            if method == 'close_game':
                logger.info('Close game during wait')
                self.device.app_stop()
                release_resources()
                self.wait_until(task.next_run)
                self.run('start')
            elif method == 'goto_main':
                logger.info('Goto main page during wait')
                self.run('goto_main')
                release_resources()
                self.wait_until(task.next_run)
            elif method == 'stay_there':
                logger.info('Stay there during wait')
                release_resources()
                self.wait_until(task.next_run)
            else:
                logger.warning(
                    f'Invalid Optimization_WhenTaskQueueEmpty: {method}, fallback to stay_there'
                )
                release_resources()
                self.wait_until(task.next_run)

        AzurLaneConfig.is_hoarding_task = False
        return task.command
Пример #29
0
    def _run_update(self, instances, names):
        self.state = 'run update'
        logger.info("All alas stopped, start updating")

        if self.update():
            if Setting.reload:
                self.state = 'reload'
                with open('./config/reloadalas', mode='w') as f:
                    f.writelines(names)
                from module.webui.app import clearup
                self._trigger_reload(2)
                clearup()
            else:
                self.state = 'finish'
        else:
            self.state = 'failed'
            logger.warning("Update failed")
            self.event.clear()
            ProcessManager.restart_processes(instances, self.event)
            return False
Пример #30
0
    def _tactical_animation_running(self):
        """
        Returns:
            bool: If showing skill points increasing animation.
        """
        color_height = np.mean(self.device.image.crop((922, 0, 1036, 720)).convert('L'), axis=1)
        parameters = {'height': 200}
        peaks, _ = signal.find_peaks(color_height, **parameters)
        peaks = [y for y in peaks if y > 67 + 243]

        if not len(peaks):
            logger.warning('No student card found.')
        for y in peaks:
            student_area = (447, y - 243, 1244, y)
            area = area_offset((677, 172, 761, 183), student_area[0:2])
            # Normal: 160, In skill-increasing animation: 109
            if np.mean(get_color(self.device.image, area)) < 135:
                return True

        return False