Exemplo n.º 1
0
    def meow_menu_close(self, skip_first_screenshot=True):
        """
        Exit from any meowfficer menu popups

        Pages:
            in: MEOWFFICER_FORT_CHECK, MEOWFFICER_BUY, MEOWFFICER_TRAIN_START, etc
            out: page_meowfficer
        """
        logger.hr('Meowfficer menu close')
        click_timer = Timer(3)
        while 1:
            if skip_first_screenshot:
                skip_first_screenshot = False
            else:
                self.device.screenshot()

            # End
            if self.appear(MEOWFFICER_CHECK, offset=(20, 20)) \
                    and MEOWFFICER_CHECK.match_appear_on(self.device.image):
                break
            else:
                if click_timer.reached():
                    # MEOWFFICER_CHECK is safe to click
                    self.device.click(MEOWFFICER_CHECK)
                    click_timer.reset()
                    continue

            # Fort
            if self.appear(MEOWFFICER_FORT_CHECK, offset=(20, 20), interval=3):
                self.device.click(MEOWFFICER_CHECK)
                click_timer.reset()
                continue
            # Buy
            if self.appear(MEOWFFICER_BUY, offset=(20, 20), interval=3):
                self.device.click(MEOWFFICER_CHECK)
                click_timer.reset()
                continue
            # Train
            if self.appear(MEOWFFICER_TRAIN_FILL_QUEUE,
                           offset=(20, 20),
                           interval=3):
                self.device.click(MEOWFFICER_CHECK)
                click_timer.reset()
                continue
            if self.appear(MEOWFFICER_TRAIN_FINISH_ALL,
                           offset=(20, 20),
                           interval=3):
                self.device.click(MEOWFFICER_CHECK)
                click_timer.reset()
                continue
            # Popups
            if self.appear(MEOWFFICER_CONFIRM, offset=(40, 20), interval=3):
                self.device.click(MEOWFFICER_CHECK)
                click_timer.reset()
                continue
            if self.appear(MEOWFFICER_CANCEL, offset=(40, 20), interval=3):
                self.device.click(MEOWFFICER_CHECK)
                click_timer.reset()
                continue
            if self.appear_then_click(GET_ITEMS_1, offset=5, interval=3):
                click_timer.reset()
                continue
Exemplo n.º 2
0
    def ui_goto(self, destination, offset=(20, 20), confirm_wait=0, skip_first_screenshot=True):
        """
        Args:
            destination (Page):
            offset:
            confirm_wait:
            skip_first_screenshot:
        """
        # Reset connection
        for page in self.ui_pages:
            page.parent = None

        # Create connection
        visited = [destination]
        visited = set(visited)
        while 1:
            new = visited.copy()
            for page in visited:
                for link in self.ui_pages:
                    if link in visited:
                        continue
                    if page in link.links:
                        link.parent = page
                        new.add(link)
            if len(new) == len(visited):
                break
            visited = new

        logger.hr(f'UI goto {destination}')
        confirm_timer = Timer(confirm_wait, count=int(confirm_wait // 0.5)).start()
        while 1:
            GOTO_MAIN.clear_offset()
            if skip_first_screenshot:
                skip_first_screenshot = False
            else:
                self.device.screenshot()

            # Destination page
            if self.appear(destination.check_button, offset=offset):
                if confirm_timer.reached():
                    break
            else:
                confirm_timer.reset()

            # Other pages
            clicked = False
            for page in visited:
                if page.parent is None or page.check_button is None:
                    continue
                if self.appear(page.check_button, offset=offset, interval=5):
                    self.device.click(page.links[page.parent])
                    confirm_timer.reset()
                    clicked = True
                    break
            if clicked:
                continue

            # Additional
            if self.ui_additional():
                continue

        # Reset connection
        for page in self.ui_pages:
            page.parent = None
Exemplo n.º 3
0
    def handle_in_map_with_enemy_searching(self, drop=None):
        """
        Args:
            drop (DropImage):

        Returns:
            bool: If handled.
        """
        if not self.is_in_map():
            return False

        timeout = Timer(self.MAP_ENEMY_SEARCHING_TIMEOUT_SECOND)
        appeared = False
        while 1:
            self.device.screenshot()
            if self.is_event_animation():
                continue
            if self.is_in_map():
                timeout.start()
            else:
                timeout.reset()

            # Stage might ends,
            # although here expects an enemy searching animation.
            if self.handle_in_stage():
                return True
            if self.handle_auto_search_exit(drop=drop):
                continue

            # Popups
            if self.handle_vote_popup():
                timeout.limit = 10
                timeout.reset()
                continue
            if self.handle_story_skip():
                self.ensure_no_story()
                timeout.limit = 10
                timeout.reset()
            if self.handle_guild_popup_cancel():
                timeout.limit = 10
                timeout.reset()
                continue
            if self.handle_urgent_commission(drop=drop):
                timeout.limit = 10
                timeout.reset()
                continue

            # End
            if self.enemy_searching_appear():
                appeared = True
            else:
                if appeared:
                    self.handle_enemy_flashing()
                    self.device.sleep(0.3)
                    logger.info('Enemy searching appeared.')
                    break
                self.enemy_searching_color_initial()
            if timeout.reached():
                logger.info('Enemy searching timeout.')
                break

        self.device.screenshot()
        return True
Exemplo n.º 4
0
    def _reward_mission(self):
        """
        Returns:
            bool: If rewarded.
        """
        if not self.config.ENABLE_MISSION_REWARD:
            return False

        logger.hr('Mission reward')
        if not self.appear(MISSION_NOTICE):
            logger.info('No mission reward')
            return False

        self.ui_goto(page_mission, skip_first_screenshot=True)

        reward = False
        exit_timer = Timer(2)
        click_timer = Timer(1)
        timeout = Timer(10)
        exit_timer.start()
        timeout.start()
        while 1:
            self.device.screenshot()

            for button in [GET_ITEMS_1, GET_ITEMS_2]:
                if self.appear_then_click(button, offset=(30, 30), interval=1):
                    exit_timer.reset()
                    timeout.reset()
                    reward = True
                    continue

            for button in [MISSION_MULTI, MISSION_SINGLE]:
                if not click_timer.reached():
                    continue
                if self.appear_then_click(button, interval=1):
                    exit_timer.reset()
                    click_timer.reset()
                    timeout.reset()
                    continue

            if not self.appear(MISSION_CHECK):
                if self.appear_then_click(GET_SHIP, interval=1):
                    click_timer.reset()
                    exit_timer.reset()
                    timeout.reset()
                    continue

            if self.handle_mission_popup_ack():
                click_timer.reset()
                exit_timer.reset()
                timeout.reset()
                continue

            if self.story_skip():
                click_timer.reset()
                exit_timer.reset()
                timeout.reset()
                continue

            # End
            if reward and exit_timer.reached():
                break
            if timeout.reached():
                logger.warning('Wait get items timeout.')
                break

        self.ui_goto(page_main, skip_first_screenshot=True)
        return reward
Exemplo n.º 5
0
    def research_receive(self, skip_first_screenshot=True):
        """
        Args:
            skip_first_screenshot:

        Pages:
            in: page_research, stable, with project finished.
            out: page_research

        Returns:
            bool: True if success to receive rewards.
                  False if project requirements are not satisfied.
        """
        logger.hr('Research receive', level=2)

        def get_items():
            for b in [GET_ITEMS_3, GET_ITEMS_2, GET_ITEMS_1]:
                if self.appear(b, offset=(5, 0)):
                    return b
            return None

        with self.stat.new(
                genre='research',
                save=self.config.DropRecord_SaveResearch,
                upload=self.config.DropRecord_UploadResearch) as record:
            # Take screenshots of project list
            record.add(self.device.image)

            # Click finished project, to GET_ITEMS_*
            confirm_timer = Timer(1.5, count=5)
            record_button = None
            while 1:
                if skip_first_screenshot:
                    skip_first_screenshot = False
                else:
                    self.device.screenshot()

                if self.appear(RESEARCH_CHECK, interval=10):
                    if self._research_has_finished_at(
                            self._research_finished_index):
                        self.device.click(
                            RESEARCH_ENTRANCE[self._research_finished_index])

                if self.appear(RESEARCH_STOP, offset=(20, 20)):
                    logger.info(
                        'The research time is up, but requirements are not satisfied'
                    )
                    self.research_project_started = None
                    self.research_detail_quit()
                    return False

                appear_button = get_items()
                if appear_button is not None:
                    if appear_button == record_button:
                        if confirm_timer.reached():
                            break
                    else:
                        logger.info(f'{appear_button} appeared')
                        record_button = appear_button
                        confirm_timer.reset()

            # Take screenshots of items
            if record:
                button = get_items()
                if button == GET_ITEMS_1 or button == GET_ITEMS_2:
                    record.add(self.device.image)
                elif button == GET_ITEMS_3:
                    self.device.sleep(1.5)
                    self.device.screenshot()
                    record.add(self.device.image)
                    self.device.swipe((0, 250),
                                      box=ITEMS_3_SWIPE.area,
                                      random_range=(-10, -10, 10, 10),
                                      padding=0)
                    self.device.sleep(2)
                    self.device.screenshot()
                    record.add(self.device.image)

        # Close GET_ITEMS_*, to project list
        self.ui_click(appear_button=get_items,
                      click_button=GET_ITEMS_RESEARCH_SAVE,
                      check_button=self._in_research,
                      skip_first_screenshot=True)
        return True
Exemplo n.º 6
0
    def enter_map(self, button, mode='normal', skip_first_screenshot=True):
        """Enter a campaign.

        Args:
            button: Campaign to enter.
            mode (str): 'normal' or 'hard' or 'cd'
            skip_first_screenshot (bool):
        """
        logger.hr('Enter map')
        campaign_timer = Timer(5)
        map_timer = Timer(5)
        fleet_timer = Timer(5)
        campaign_click = 0
        map_click = 0
        fleet_click = 0
        checked_in_map = False
        self.stage_entrance = button

        with self.stat.new(
                genre=self.config.campaign_name, save=self.config.DropRecord_SaveCombat, upload=False
        ) as drop:
            while 1:
                if skip_first_screenshot:
                    skip_first_screenshot = False
                else:
                    self.device.screenshot()

                # Check errors
                if campaign_click > 5:
                    logger.critical(f"Failed to enter {button}, too many click on {button}")
                    logger.critical("Possible reason #1: You haven't reached the commander level to unlock this stage.")
                    raise RequestHumanTakeover
                if fleet_click > 5:
                    logger.critical(f"Failed to enter {button}, too many click on FLEET_PREPARATION")
                    logger.critical("Possible reason #1: "
                                    "Your fleets haven't satisfied the stat restrictions of this stage.")
                    logger.critical("Possible reason #2: "
                                    "This stage can only be farmed once a day, "
                                    "but it's the second time that you are entering")
                    raise RequestHumanTakeover

                # Already in map
                if not checked_in_map and self.is_in_map():
                    logger.info('Already in map, skip enter_map.')
                    return False
                else:
                    checked_in_map = True

                # Map preparation
                if map_timer.reached() and self.handle_map_preparation():
                    self.map_get_info()
                    self.handle_fast_forward()
                    self.handle_auto_search()
                    if self.triggered_map_stop():
                        self.enter_map_cancel()
                        self.handle_map_stop()
                        raise ScriptEnd(f'Reach condition: {self.config.StopCondition_MapAchievement}')
                    self.device.click(MAP_PREPARATION)
                    map_click += 1
                    map_timer.reset()
                    campaign_timer.reset()
                    continue

                # Fleet preparation
                if fleet_timer.reached() and self.appear(FLEET_PREPARATION, offset=(20, 20)):
                    if mode == 'normal' or mode == 'hard':
                        self.handle_2x_book_setting(mode='prep')
                        self.fleet_preparation()
                        self.handle_auto_submarine_call_disable()
                        self.handle_auto_search_setting()
                        self.map_fleet_checked = True
                    self.device.click(FLEET_PREPARATION)
                    fleet_click += 1
                    fleet_timer.reset()
                    campaign_timer.reset()
                    continue

                # Auto search continue
                if self.handle_auto_search_continue():
                    campaign_timer.reset()
                    continue

                # Retire
                if self.handle_retirement():
                    campaign_timer.reset()
                    map_timer.reset()
                    fleet_timer.reset()
                    continue

                # Use Data Key
                if self.handle_use_data_key():
                    continue

                # Emotion
                if self.handle_combat_low_emotion():
                    continue

                # Urgent commission
                if self.handle_urgent_commission(drop=drop):
                    continue

                # 2X book popup
                if self.handle_2x_book_popup():
                    continue

                # Story skip
                if self.handle_story_skip():
                    campaign_timer.reset()
                    continue

                # Enter campaign
                if campaign_timer.reached() and self.appear_then_click(button):
                    campaign_click += 1
                    campaign_timer.reset()
                    continue

                # End
                if self.map_is_auto_search:
                    if self.is_auto_search_running():
                        break
                else:
                    if self.handle_in_map_with_enemy_searching():
                        self.handle_map_after_combat_story()
                        break

        return True
Exemplo n.º 7
0
    def _guild_operations_boss_preparation(self, skip_first_screenshot=True):
        """
        Execute preperation sequence for guild raid boss

        Pages:
            in: GUILD_OPERATIONS_BOSS
            out: IN_BATTLE
        """
        # Ensure in dispatch for Guild Raid Boss
        self.ui_click(GUILD_BOSS_ENTER,
                      check_button=GUILD_DISPATCH_RECOMMEND_2,
                      skip_first_screenshot=True)

        # If configured, auto recommend fleet composition
        if self.config.ENABLE_GUILD_OPERATIONS_BOSS_RECOMMEND:
            self.device.click(GUILD_DISPATCH_RECOMMEND_2)

        is_loading = False
        empty_timeout = Timer(3, count=6)
        dispatch_count = 0
        while 1:
            if skip_first_screenshot:
                skip_first_screenshot = False
            else:
                self.device.screenshot()

            if self.appear(GUILD_DISPATCH_EMPTY_2):
                # Account for loading lag especially if using
                # guild support
                if not empty_timeout.started():
                    empty_timeout.reset()
                    continue
                elif empty_timeout.reached():
                    logger.warning(
                        'Fleet composition is empty, cannot auto-battle Guild Raid Boss'
                    )
                    return False

            if self.appear(GUILD_DISPATCH_FLEET, interval=3):
                # Button does not appear greyed out even
                # when empty fleet composition
                if not self.appear(GUILD_DISPATCH_EMPTY_2):
                    if dispatch_count < 3:
                        self.device.click(GUILD_DISPATCH_FLEET)
                        dispatch_count += 1
                    else:
                        logger.warning(
                            'Fleet cannot be dispatched for auto-battle Guild Raid Boss, verify composition manually'
                        )
                        return False
                continue

            # Only print once when detected
            if not is_loading:
                if self.is_combat_loading():
                    is_loading = True
                continue

            if self.handle_combat_automation_confirm():
                continue

            # End
            if self.is_combat_executing():
                return True
Exemplo n.º 8
0
    def _guild_operations_ensure(self, skip_first_screenshot=True):
        """
        Ensure guild operation is loaded
        After entering guild operation, background loaded first, then dispatch/boss
        """
        logger.attr('Guild master/official',
                    self.config.GuildOperation_SelectNewOperation)
        confirm_timer = Timer(1.5, count=3).start()
        click_count = 0
        while 1:
            if skip_first_screenshot:
                skip_first_screenshot = False
            else:
                self.device.screenshot()

            # End
            if click_count > 5:
                # Info bar showing `none4302`.
                # Probably because guild operation has been started by another guild officer already.
                # Enter guild page again should fix the issue.
                logger.warning(
                    'Unable to start/join guild operation, '
                    'probably because guild operation has been started by another guild officer already'
                )
                raise GameBugError('Unable to start/join guild operation')

            if self._handle_guild_operations_start():
                confirm_timer.reset()
                continue
            if self.appear(GUILD_OPERATIONS_JOIN, interval=3):
                if self.image_color_count(GUILD_OPERATIONS_MONTHLY_COUNT,
                                          color=(255, 93, 90),
                                          threshold=221,
                                          count=20):
                    logger.info(
                        'Unable to join operation, no more monthly attempts left'
                    )
                    self.device.click(GUILD_OPERATIONS_CLICK_SAFE_AREA)
                else:
                    current, remain, total = GUILD_OPERATIONS_PROGRESS.ocr(
                        self.device.image)
                    threshold = total * self.config.GuildOperation_JoinThreshold
                    if current <= threshold:
                        logger.info(
                            'Joining Operation, current progress less than '
                            f'threshold ({threshold:.2f})')
                        self.device.click(GUILD_OPERATIONS_JOIN)
                    else:
                        logger.info(
                            'Refrain from joining operation, current progress exceeds '
                            f'threshold ({threshold:.2f})')
                        self.device.click(GUILD_OPERATIONS_CLICK_SAFE_AREA)
                confirm_timer.reset()
                continue
            if self.handle_popup_confirm('JOIN_OPERATION'):
                click_count += 1
                confirm_timer.reset()
                continue
            if self.handle_popup_single('FLEET_UPDATED'):
                logger.info(
                    'Fleet composition altered, may still be dispatch-able. However '
                    'fellow guild members have updated their support line up. '
                    'Suggestion: Enable Boss Recommend')
                confirm_timer.reset()
                continue

            # End
            if self.appear(GUILD_BOSS_ENTER) or self.appear(
                    GUILD_OPERATIONS_ACTIVE_CHECK, offset=(20, 20)):
                if not self.info_bar_count() and confirm_timer.reached():
                    break
Exemplo n.º 9
0
    def _shipyard_buy_confirm(self, text, skip_first_screenshot=True):
        """
        Handles screen transitions to use/buy BPs

        Args:
            text (str): for handle_popup_confirm
            skip_first_screenshot (bool):
        """
        success = False
        append = self._shipyard_get_append()
        button = globals()[f'SHIPYARD_CONFIRM_{append}']
        ocr_timer = Timer(10, count=10).start()
        confirm_timer = Timer(1, count=2).start()
        self.interval_clear(button)

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

            if ocr_timer.reached():
                logger.warning(
                    'Failed to detect for normal exit routine, resort to OCR check'
                )
                _, _, current = self._shipyard_get_total()
                if not current:
                    logger.info(
                        'Confirm action has completed, setting flag for exit')
                    self.interval_reset(button)
                    success = True
                ocr_timer.reset()
                continue

            if self.appear_then_click(button, offset=(20, 20), interval=3):
                continue

            if self.handle_popup_confirm(text):
                self.interval_reset(button)
                ocr_timer.reset()
                confirm_timer.reset()
                continue

            if self.story_skip():
                self.interval_reset(button)
                success = True
                ocr_timer.reset()
                confirm_timer.reset()
                continue

            if self.handle_info_bar():
                self.interval_reset(button)
                success = True
                ocr_timer.reset()
                confirm_timer.reset()
                continue

            # End
            if success and \
                self._shipyard_in_ui():
                if confirm_timer.reached():
                    break
            else:
                confirm_timer.reset()
Exemplo n.º 10
0
    def os_map_goto_globe(self, unpin=True, skip_first_screenshot=True):
        """
        Args:
            unpin (bool):
            skip_first_screenshot (bool):

        Pages:
            in: is_in_map
            out: is_in_globe
        """
        click_count = 0
        while 1:
            if skip_first_screenshot:
                skip_first_screenshot = False
            else:
                self.device.screenshot()

            if self.appear_then_click(MAP_GOTO_GLOBE,
                                      offset=(200, 5),
                                      interval=5):
                click_count += 1
                if click_count >= 5:
                    # When there's zone exploration reward, AL just don't let you go.
                    logger.warning(
                        'Unable to goto globe, '
                        'there might be uncollected zone exploration rewards preventing exit'
                    )
                    raise GameTooManyClickError(
                        f'Too many click for a button: {MAP_GOTO_GLOBE}')
                continue
            if self.handle_map_event():
                continue
            # Popup: AUTO_SEARCH_REWARD appears slowly
            if self.appear_then_click(AUTO_SEARCH_REWARD,
                                      offset=(50, 50),
                                      interval=5):
                continue
            # Popup: Leaving current zone will terminate meowfficer searching.
            # Popup: Leaving current zone will retreat submarines
            # Searching reward will be shown after entering another zone.
            if self.handle_popup_confirm('GOTO_GLOBE'):
                continue

            # End
            if self.is_in_globe():
                break

        skip_first_screenshot = True
        confirm_timer = Timer(1, count=2).start()
        unpinned = 0
        while 1:
            if skip_first_screenshot:
                skip_first_screenshot = False
            else:
                self.device.screenshot()

            if unpin:
                if self.handle_zone_pinned():
                    unpinned += 1
                    confirm_timer.reset()
                else:
                    if unpinned and confirm_timer.reached():
                        break
            else:
                if self.is_zone_pinned():
                    break
Exemplo n.º 11
0
    def meow_get(self, skip_first_screenshot=True):
        """
        Transition through all the necessary screens
        to acquire each trained meowfficer
        Animation is waited for as the amount can vary
        Only gold variant meowfficer will prompt for
        confirmation

        Args:
            skip_first_screenshot (bool): Skip first
            screen shot or not

        Pages:
            in: MEOWFFICER_GET_CHECK
            out: MEOWFFICER_TRAIN
        """
        # Loop through possible screen transitions
        confirm_timer = Timer(1.5, count=3).start()
        count = 0
        while 1:
            if skip_first_screenshot:
                skip_first_screenshot = False
            else:
                self.device.screenshot()

            if self.handle_meow_popup_dismiss():
                confirm_timer.reset()
                continue
            if self.appear(MEOWFFICER_GET_CHECK, offset=(40, 40), interval=3):
                count += 1
                logger.attr('Meow_get', count)
                with self.stat.new(genre="meowfficer_talent",
                                   method=self.config.
                                   DropRecord_MeowfficerTalent) as drop:
                    drop.add(self.device.image)
                    list_talent_btn, special_talent = self._get_meow_talent_grid(
                    )
                    if self.config.DropRecord_MeowfficerTalent != 'do_not':
                        self._meow_talent_cap_handle(list_talent_btn, drop)
                    if self.appear(MEOWFFICER_GOLD_CHECK, offset=(40, 40)):
                        if not self.config.MeowfficerTrain_RetainTalentedGold or not special_talent:
                            self._meow_skip_lock()
                            skip_first_screenshot = True
                            confirm_timer.reset()
                            continue
                        self._meow_apply_lock()

                    if self.appear(MEOWFFICER_PURPLE_CHECK, offset=(40, 40)):
                        if self.config.MeowfficerTrain_RetainTalentedPurple and special_talent:
                            self._meow_apply_lock()

                    # Susceptible to exception when collecting multiple
                    # Mitigate by popping click_record
                    self.device.click(MEOWFFICER_TRAIN_CLICK_SAFE_AREA)
                    self.device.click_record.pop()
                    confirm_timer.reset()
                    self.interval_reset(MEOWFFICER_GET_CHECK)
                    continue

            # End
            if self.appear(MEOWFFICER_TRAIN_START, offset=(20, 20)):
                if confirm_timer.reached():
                    break
            else:
                confirm_timer.reset()
Exemplo n.º 12
0
    def wait_until_walk_stable(self,
                               confirm_timer=None,
                               skip_first_screenshot=False,
                               walk_out_of_step=True,
                               drop=None):
        """
        Wait until homo_loca stabled.
        DETECTION_BACKEND must be 'homography'.

        Args:
            confirm_timer (Timer):
            skip_first_screenshot (bool):
            walk_out_of_step (bool): If catch walk_out_of_step error.
                Default to True, use False in abyssal zones.
            drop (DropImage):

        Returns:
            str: Things that fleet met on its way,
                'event', 'search', 'akashi', 'combat',
                or their combinations like 'event_akashi', 'event_combat',
                or an empty string '' if nothing met.

        Raises:
            MapWalkError: If unable to goto such grid.
        """
        logger.hr('Wait until walk stable')
        record = None
        enemy_searching_appear = False
        self.device.screenshot_interval_set(0.35)
        if confirm_timer is None:
            confirm_timer = Timer(0.8, count=2)
        result = set()

        confirm_timer.reset()
        while 1:
            if skip_first_screenshot:
                skip_first_screenshot = False
            else:
                self.device.screenshot()

            # Map event
            if self.handle_map_event(drop=drop):
                confirm_timer.reset()
                result.add('event')
                continue
            if self.handle_retirement():
                confirm_timer.reset()
                continue
            if self.handle_walk_out_of_step():
                if walk_out_of_step:
                    raise MapWalkError('walk_out_of_step')
                else:
                    continue

            # Accident click
            if self.is_in_globe():
                self.os_globe_goto_map()
                confirm_timer.reset()
                continue
            if self.is_in_storage():
                self.storage_quit()
                confirm_timer.reset()
                continue
            if self.is_in_os_mission():
                self.os_mission_quit()
                confirm_timer.reset()
                continue
            if self.handle_os_game_tips():
                confirm_timer.reset()
                continue

            # Enemy searching
            if not enemy_searching_appear and self.enemy_searching_appear():
                enemy_searching_appear = True
                confirm_timer.reset()
                continue
            else:
                if enemy_searching_appear:
                    self.handle_enemy_flashing()
                    self.device.sleep(0.3)
                    logger.info('Enemy searching appeared.')
                    enemy_searching_appear = False
                    result.add('search')
                if self.is_in_map():
                    self.enemy_searching_color_initial()

            # Combat
            if self.combat_appear():
                # Use ui_back() for testing, because there are too few abyssal loggers every month.
                # self.ui_back(check_button=self.is_in_map)
                self.combat(expected_end=self.is_in_map,
                            fleet_index=self.fleet_show_index,
                            save_get_items=drop)
                confirm_timer.reset()
                result.add('event')
                continue

            # Akashi shop
            if self.appear(PORT_SUPPLY_CHECK, offset=(20, 20)):
                self.interval_clear(PORT_SUPPLY_CHECK)
                self.handle_akashi_supply_buy(CLICK_SAFE_AREA)
                confirm_timer.reset()
                result.add('akashi')
                continue

            # Arrive
            # Check colors, because screen goes black when something is unlocking.
            if self.is_in_map() and IN_MAP.match_appear_on(self.device.image):
                self.update_os()
                current = self.view.backend.homo_loca
                logger.attr('homo_loca', current)
                if record is None or (current is not None and np.linalg.norm(
                        np.subtract(current, record)) < 3):
                    if confirm_timer.reached():
                        break
                else:
                    confirm_timer.reset()
                record = current
            else:
                confirm_timer.reset()

        result = '_'.join(result)
        logger.info(f'Walk stabled, result: {result}')
        self.device.screenshot_interval_set()
        return result
Exemplo n.º 13
0
    def set(self,
            main,
            left=None,
            right=None,
            upper=None,
            bottom=None,
            skip_first_screenshot=True):
        """
        Set nav bar from 1 direction.

        Args:
            main (ModuleBase):
            left (int): Index of nav item counted from left. Start from 1.
            right (int): Index of nav item counted from right. Start from 1.
            upper (int): Index of nav item counted from upper. Start from 1.
            bottom (int): Index of nav item counted from bottom. Start from 1.
            skip_first_screenshot (bool):

        Returns:
            bool: If success
        """
        if left is None and right is None and upper is None and bottom is None:
            logger.warning(
                'Invalid index to set, must set an index from 1 direction')
            return False
        text = ''
        if left is None and upper is not None:
            left = upper
        if right is None and bottom is not None:
            right = bottom
        for k in ['left', 'right', 'upper', 'bottom']:
            if locals().get(k, None) is not None:
                text += f'{k}={locals().get(k, None)} '
        logger.info(f'{self.name} set to {text.strip()}')

        interval = Timer(2, count=4)
        timeout = Timer(10, count=20).start()
        while 1:
            if skip_first_screenshot:
                skip_first_screenshot = False
            else:
                main.device.screenshot()

            if timeout.reached():
                logger.warning(f'{self.name} failed to set {text.strip()}')
                return False

            active, minimum, maximum = self.get_info(main=main)
            logger.info(
                f'Nav item active: {active} from range ({minimum}, {maximum})')
            # if active is None:
            #     continue
            index = minimum + left - 1 if left is not None else maximum - right + 1
            if not minimum <= index <= maximum:
                logger.warning(
                    f'Index to set ({index}) is not within the nav items that appears ({minimum}, {maximum})'
                )
                continue

            # End
            if active == index:
                return True

            if interval.reached():
                main.device.click(self.grids.buttons[index])
                main.device.sleep((0.1, 0.2))
                interval.reset()
Exemplo n.º 14
0
    def wait_until_walk_stable(self,
                               confirm_timer=None,
                               skip_first_screenshot=False,
                               walk_out_of_step=True,
                               drop=None):
        """
        Wait until homo_loca stabled.
        DETECTION_BACKEND must be 'homography'.

        Args:
            confirm_timer (Timer):
            skip_first_screenshot (bool):
            walk_out_of_step (bool): If catch walk_out_of_step error.
                Default to True, use False in abyssal zones.
            drop (DropImage):

        Raises:
            MapWalkError: If unable to goto such grid.
        """
        logger.hr('Wait until walk stable')
        record = None
        enemy_searching_appear = False
        self.device.screenshot_interval_set(0.35)
        if confirm_timer is None:
            confirm_timer = Timer(0.8, count=2)

        confirm_timer.reset()
        while 1:
            if skip_first_screenshot:
                skip_first_screenshot = False
            else:
                self.device.screenshot()

            # Map event
            if self.handle_map_event(drop=drop):
                confirm_timer.reset()
                continue
            if self.handle_retirement():
                confirm_timer.reset()
                continue
            if self.handle_walk_out_of_step():
                if walk_out_of_step:
                    raise MapWalkError('walk_out_of_step')
                else:
                    continue

            # Enemy searching
            if not enemy_searching_appear and self.enemy_searching_appear():
                enemy_searching_appear = True
                confirm_timer.reset()
                continue
            else:
                if enemy_searching_appear:
                    self.handle_enemy_flashing()
                    self.device.sleep(0.3)
                    logger.info('Enemy searching appeared.')
                    enemy_searching_appear = False
                if self.is_in_map():
                    self.enemy_searching_color_initial()

            # Combat
            if self.combat_appear():
                # Use ui_back() for testing, because there are too few abyssal loggers every month.
                # self.ui_back(check_button=self.is_in_map)
                self.combat(expected_end=self.is_in_map,
                            fleet_index=self.fleet_show_index,
                            save_get_items=drop)
                confirm_timer.reset()
                continue

            # Arrive
            # Check colors, because screen goes black when something is unlocking.
            if self.is_in_map() and IN_MAP.match_appear_on(self.device.image):
                self.update_os()
                current = self.view.backend.homo_loca
                logger.attr('homo_loca', current)
                if record is None or (current is not None and np.linalg.norm(
                        np.subtract(current, record)) < 3):
                    if confirm_timer.reached():
                        break
                else:
                    confirm_timer.reset()
                record = current
            else:
                confirm_timer.reset()

        logger.info('Walk stabled')
        self.device.screenshot_interval_set()
Exemplo n.º 15
0
    def _goto(self, location, expected=''):
        """Goto a grid directly and handle ambush, air raid, mystery picked up, combat.

        Args:
            location (tuple, str, GridInfo): Destination.
            expected (str): Expected result on destination grid, such as 'combat', 'combat_siren', 'mystery'.
                Will give a waring if arrive with unexpected result.
        """
        location = location_ensure(location)
        result_mystery = ''
        self.movable_before = self.map.select(is_siren=True)
        self.movable_before_normal = self.map.select(is_enemy=True)
        if self.hp_retreat_triggered():
            self.withdraw()
        is_portal = self.map[location].is_portal
        # The upper grid is submarine, may mess up predict_fleet()
        may_submarine_icon = self.map.grid_covered(self.map[location],
                                                   location=[(0, -1)])
        may_submarine_icon = may_submarine_icon and self.fleet_submarine_location == may_submarine_icon[
            0].location

        while 1:
            self.in_sight(location, sight=self._walk_sight)
            self.focus_to_grid_center()
            grid = self.convert_global_to_local(location)

            self.ambush_color_initial()
            self.enemy_searching_color_initial()
            grid.__str__ = location
            result = 'nothing'

            self.device.click(grid)
            arrived = False
            # Wait to confirm fleet arrived. It does't appear immediately if fleet in combat.
            extra = 0
            if self.config.Submarine_Mode == 'hunt_only':
                extra += 4.5
            if self.config.MAP_HAS_LAND_BASED and grid.is_mechanism_trigger:
                extra += grid.mechanism_wait
            arrive_timer = Timer(0.5 + self.round_wait + extra, count=2)
            arrive_unexpected_timer = Timer(1.5 + self.round_wait + extra,
                                            count=6)
            # Wait after ambushed.
            ambushed_retry = Timer(0.5)
            # If nothing happens, click again.
            walk_timeout = Timer(20)
            walk_timeout.start()

            while 1:
                self.device.screenshot()
                self.view.update(image=self.device.image)
                if is_portal:
                    self.update()
                    grid = self.view[self.view.center_loca]

                # Combat
                if self.config.Campaign_UseFleetLock and not self.is_in_map():
                    if self.handle_retirement():
                        self.map_offensive()
                        walk_timeout.reset()
                    if self.handle_combat_low_emotion():
                        walk_timeout.reset()
                if self.combat_appear():
                    self.combat(expected_end=self._expected_end(expected),
                                fleet_index=self.fleet_show_index,
                                submarine_mode=self._submarine_mode(expected))
                    self.hp_get()
                    self.lv_get(after_battle=True)
                    arrived = True if not self.config.MAP_HAS_MOVABLE_ENEMY else False
                    result = 'combat'
                    self.battle_count += 1
                    self.fleet_ammo -= 1
                    if 'siren' in expected or (
                            self.config.MAP_HAS_MOVABLE_ENEMY
                            and not expected):
                        self.siren_count += 1
                    elif self.map[location].may_enemy:
                        self.map[location].is_cleared = True

                    if self.catch_camera_repositioning(self.map[location]):
                        self.handle_boss_appear_refocus()
                    if self.config.MAP_FOCUS_ENEMY_AFTER_BATTLE:
                        self.camera = location
                        self.update()
                    grid = self.convert_global_to_local(location)
                    arrive_timer = Timer(0.5 + extra, count=2)
                    arrive_unexpected_timer = Timer(1.5 + extra, count=6)
                    walk_timeout.reset()
                    if not (grid.predict_fleet()
                            and grid.predict_current_fleet()):
                        ambushed_retry.start()

                # Ambush
                if self.handle_ambush():
                    self.hp_get()
                    self.lv_get(after_battle=True)
                    walk_timeout.reset()
                    self.view.update(image=self.device.image)
                    if not (grid.predict_fleet()
                            and grid.predict_current_fleet()):
                        ambushed_retry.start()

                # Mystery
                mystery = self.handle_mystery(button=grid)
                if mystery:
                    self.mystery_count += 1
                    result = 'mystery'
                    result_mystery = mystery

                # Cat attack animation
                if self.handle_map_cat_attack():
                    walk_timeout.reset()
                    continue

                # Guild popup
                # Usually handled in combat_status, but sometimes delayed until after battle on slow PCs.
                if self.handle_guild_popup_cancel():
                    walk_timeout.reset()
                    continue

                if self.handle_walk_out_of_step():
                    raise MapWalkError('walk_out_of_step')

                # Arrive
                arrive_predict = ''
                arrive_checker = False
                if self.is_in_map():
                    if not may_submarine_icon and grid.predict_fleet():
                        arrive_predict = '(is_fleet)'
                        arrive_checker = True
                    elif may_submarine_icon and grid.predict_current_fleet():
                        arrive_predict = '(may_submarine_icon, is_current_fleet)'
                        arrive_checker = True
                    elif self.config.MAP_WALK_USE_CURRENT_FLEET and grid.predict_current_fleet(
                    ):
                        arrive_predict = '(MAP_WALK_USE_CURRENT_FLEET, is_current_fleet)'
                        arrive_checker = True
                    elif walk_timeout.reached() and grid.predict_current_fleet(
                    ):
                        arrive_predict = '(walk_timeout, is_current_fleet)'
                        arrive_checker = True
                if arrive_checker:
                    if not arrive_timer.started():
                        logger.info(
                            f'Arrive {location2node(location)} {arrive_predict}'
                            .strip())
                    arrive_timer.start()
                    arrive_unexpected_timer.start()
                    if result == 'nothing' and not arrive_timer.reached():
                        continue
                    if expected and result not in expected:
                        if arrive_unexpected_timer.reached():
                            logger.warning('Arrive with unexpected result')
                        else:
                            continue
                    if is_portal:
                        location = self.map[location].portal_link
                        self.camera = location
                    logger.info(
                        f'Arrive {location2node(location)} confirm. Result: {result}. Expected: {expected}'
                    )
                    arrived = True
                    break
                else:
                    if arrive_timer.started():
                        arrive_timer.reset()
                    if arrive_unexpected_timer.started():
                        arrive_unexpected_timer.reset()

                # Story
                if expected == 'story':
                    if self.handle_story_skip():
                        result = 'story'
                        continue

                # End
                if ambushed_retry.started() and ambushed_retry.reached():
                    break
                if walk_timeout.reached():
                    logger.warning('Walk timeout. Retrying.')
                    self.predict()
                    self.ensure_edge_insight(skip_first_update=False)
                    break

            # End
            if arrived:
                # Ammo grid needs to click again, otherwise the next click doesn't work.
                if self.map[location].may_ammo:
                    self.device.click(grid)
                break

        self.map[self.fleet_current].is_fleet = False
        self.map[location].wipe_out()
        self.map[location].is_fleet = True
        self.__setattr__('fleet_%s_location' % self.fleet_current_index,
                         location)
        if result_mystery == 'get_carrier':
            self.full_scan_carrier()
        if result == 'combat':
            self.round_battle(after_battle=True)
            self.predict()
        self.round_next()
        if self.round_is_new:
            if result != 'combat':
                self.predict()
            self.full_scan_movable(enemy_cleared=result == 'combat')
            self.find_path_initial()
            raise MapEnemyMoved
        if self.round_maze_changed:
            self.find_path_initial()
            raise MapEnemyMoved
        self.find_path_initial()
Exemplo n.º 16
0
    def os_mission_overview_accept(self):
        """
        Accept all missions in mission overview.

        Returns:
            bool: True if all missions accepted or no mission found.
                  False if unable to accept more missions.

        Pages:
            in: is_in_map
            out: is_in_map
        """
        logger.hr('OS mission overview accept', level=1)
        # is_in_map
        self.os_map_goto_globe(unpin=False)
        # is_in_globe
        self.ui_click(MISSION_OVERVIEW_ENTER,
                      check_button=MISSION_OVERVIEW_CHECK,
                      offset=(200, 20),
                      retry_wait=3,
                      skip_first_screenshot=True)

        # MISSION_OVERVIEW_CHECK
        confirm_timer = Timer(1, count=3).start()
        skip_first_screenshot = True
        success = True
        while 1:
            if skip_first_screenshot:
                skip_first_screenshot = False
            else:
                self.device.screenshot()

            if self.info_bar_count():
                logger.info(
                    'Unable to accept missions, because reached the maximum number of missions'
                )
                success = False
                break
            if self.appear_then_click(MISSION_OVERVIEW_ACCEPT,
                                      offset=(20, 20),
                                      interval=0.2):
                confirm_timer.reset()
                continue
            else:
                # End
                if confirm_timer.reached():
                    success = True
                    break
            if self.appear_then_click(MISSION_OVERVIEW_ACCEPT_SINGLE,
                                      offset=(20, 20),
                                      interval=0.2):
                confirm_timer.reset()
                continue

        # is_in_globe
        self.ui_back(appear_button=MISSION_OVERVIEW_CHECK,
                     check_button=self.is_in_globe,
                     skip_first_screenshot=True)
        # is_in_map
        self.os_globe_goto_map()
        return success
Exemplo n.º 17
0
    def shop_get_items(self, skip_first_screenshot=True):
        """
        Args:
            skip_first_screenshot (bool):

        Returns:
            list[Item]:
        """
        # Retrieve ShopItemGrid
        shop_items = self.shop_items()
        if shop_items is None:
            logger.warning('Expected type \'ShopItemGrid\' but was None')
            return []

        # Loop on predict to ensure items
        # have loaded and can accurately
        # be read
        record = 0
        timeout = Timer(3, count=9).start()
        while 1:
            if skip_first_screenshot:
                skip_first_screenshot = False
            else:
                self.device.screenshot()

            shop_items.predict(
                self.device.image,
                name=True,
                amount=False,
                cost=True,
                price=True,
                tag=False
            )

            if timeout.reached():
                logger.warning('Items loading timeout; continue and assumed has loaded')
                break

            # Check unloaded items, because AL loads items too slow.
            items = shop_items.items
            known = len([item for item in items if item.is_known_item])
            logger.attr('Item detected', known)
            if known == 0 or known != record:
                record = known
                continue
            else:
                record = known

            # End
            if self.shop_has_loaded(items):
                break

        # Log final result on predicted items
        items = shop_items.items
        grids = shop_items.grids
        if len(items):
            min_row = grids[0, 0].area[1]
            row = [str(item) for item in items if item.button[1] == min_row]
            logger.info(f'Shop row 1: {row}')
            row = [str(item) for item in items if item.button[1] != min_row]
            logger.info(f'Shop row 2: {row}')
            return items
        else:
            logger.info('No shop items found')
            return []
Exemplo n.º 18
0
    def _reward_mission_collect(self, interval=1):
        """
        Streamline handling of mission rewards for
        both 'all' and 'weekly' pages

        Args:
            interval (int): Configure the interval for
                            assets involved

        Returns:
            bool, if encountered at least 1 GET_ITEMS_*
        """
        # Reset any existing interval for the following assets
        [
            self.interval_clear(asset) for asset in [
                GET_ITEMS_1, GET_ITEMS_2, MISSION_MULTI, MISSION_SINGLE,
                GET_SHIP
            ]
        ]

        # Basic timers for certain scenarios
        exit_timer = Timer(2)
        click_timer = Timer(1)
        timeout = Timer(10)
        exit_timer.start()
        timeout.start()

        reward = False
        while 1:
            self.device.screenshot()

            for button in [GET_ITEMS_1, GET_ITEMS_2]:
                if self.appear_then_click(button,
                                          offset=(30, 30),
                                          interval=interval):
                    exit_timer.reset()
                    timeout.reset()
                    reward = True
                    continue

            for button in [MISSION_MULTI, MISSION_SINGLE]:
                if not click_timer.reached():
                    continue
                if self.appear(button, offset=(0, 200), interval=interval) \
                        and button.match_appear_on(self.device.image):
                    self.device.click(button)
                    exit_timer.reset()
                    click_timer.reset()
                    timeout.reset()
                    continue

            if not self.appear(MISSION_CHECK):
                if self.appear_then_click(GET_SHIP, interval=interval):
                    exit_timer.reset()
                    click_timer.reset()
                    timeout.reset()
                    continue

            if self.handle_mission_popup_ack():
                exit_timer.reset()
                click_timer.reset()
                timeout.reset()
                continue

            # Story
            if self.handle_vote_popup():
                exit_timer.reset()
                click_timer.reset()
                timeout.reset()
                continue
            if self.story_skip():
                exit_timer.reset()
                click_timer.reset()
                timeout.reset()
                continue

            if self.handle_popup_confirm('MISSION_REWARD'):
                exit_timer.reset()
                click_timer.reset()
                timeout.reset()
                continue

            # End
            if reward and exit_timer.reached():
                break
            if timeout.reached():
                logger.warning('Wait get items timeout.')
                break

        return reward
Exemplo n.º 19
0
    def _guild_operations_dispatch(self, skip_first_screenshot=True):
        """
        Executes the dispatch sequence

        Pages:
            in: GUILD_OPERATIONS_DISPATCH
            out: GUILD_OPERATIONS_MAP
        """
        confirm_timer = Timer(1.5, count=3).start()
        add_timer = Timer(1.5, count=3)
        close_timer = Timer(3, count=6).start()
        while 1:
            if skip_first_screenshot:
                skip_first_screenshot = False
            else:
                self.device.screenshot()

            if self.appear_then_click(GUILD_DISPATCH_QUICK, interval=5):
                confirm_timer.reset()
                close_timer.reset()
                continue

            if self.appear(GUILD_DISPATCH_EMPTY, interval=5):
                self.device.click(GUILD_DISPATCH_RECOMMEND)
                self.device.sleep((0.5, 0.8))
                self.device.click(GUILD_DISPATCH_FLEET)
                confirm_timer.reset()
                close_timer.reset()
                continue

            # Pseudo interval timer for template match_result calls
            if not add_timer.started() or add_timer.reached():
                sim, point = TEMPLATE_OPERATIONS_ADD.match_result(
                    self.device.image)
                if sim > 0.85:
                    # Use small area to reduce random click point
                    button = area_offset(area=(-2, -2, 24, 12), offset=point)
                    dispatch_add = Button(area=button,
                                          color=(),
                                          button=button,
                                          name='GUILD_DISPATCH_ADD')
                    self.device.click(dispatch_add)
                    confirm_timer.reset()
                    add_timer.reset()
                    close_timer.reset()
                    continue
                add_timer.reset()

            if self.handle_popup_confirm('GUILD_DISPATCH'):
                # Explicit click since GUILD_DISPATCH_FLEET
                # does not automatically turn into
                # GUILD_DISPATCH_IN_PROGRESS after confirm
                self.device.sleep((0.5, 0.8))
                self.device.click(GUILD_DISPATCH_CLOSE)
                confirm_timer.reset()
                close_timer.reset()
                continue

            if self.appear(GUILD_DISPATCH_IN_PROGRESS):
                # Independent timer used instead of interval
                # Since can appear if at least 1 fleet already
                # dispatched, don't want to exit prematurely
                if close_timer.reached_and_reset():
                    self.device.click(GUILD_DISPATCH_CLOSE)
                confirm_timer.reset()
                continue

            # End
            if self.appear(GUILD_OPERATIONS_ACTIVE_CHECK):
                if not self.info_bar_count() and confirm_timer.reached():
                    break
            else:
                confirm_timer.reset()
                close_timer.reset()
Exemplo n.º 20
0
    def _tactical_class_receive(self, skip_first_screenshot=True):
        """
        Receive tactical rewards and fill books.

        Args:
            skip_first_screenshot (bool):

        Returns:
            bool: If rewarded.

        Pages:
            in: page_reward, TACTICAL_CLASS_START
            out: page_tactical
        """
        logger.hr('Tactical class receive', level=1)
        tactical_class_timout = Timer(10, count=10).start()
        tactical_animation_timer = Timer(2, count=3).start()
        while 1:
            if skip_first_screenshot:
                skip_first_screenshot = False
            else:
                self.device.screenshot()

            if self.appear_then_click(REWARD_2, interval=1):
                tactical_class_timout.reset()
                tactical_animation_timer.reset()
                continue
            if self.appear_then_click(REWARD_GOTO_TACTICAL,
                                      offset=(20, 20),
                                      interval=1):
                tactical_class_timout.reset()
                tactical_animation_timer.reset()
                continue
            if self.handle_popup_confirm('TACTICAL'):
                tactical_class_timout.reset()
                tactical_animation_timer.reset()
                continue
            if self.handle_urgent_commission():
                # Only one button in the middle, when skill reach max level.
                tactical_class_timout.reset()
                tactical_animation_timer.reset()
                continue
            if self.appear(TACTICAL_CLASS_CANCEL, offset=(30, 30), interval=2) \
                    and self.appear(TACTICAL_CLASS_START, offset=(30, 30)):
                self.device.sleep(0.3)
                self._tactical_books_choose()
                self.interval_reset(TACTICAL_CLASS_CANCEL)
                tactical_class_timout.reset()
                tactical_animation_timer.reset()
                continue

            # End
            if self.appear(TACTICAL_CHECK, offset=(20, 20)):
                self.ui_current = page_tactical
                if not self._tactical_animation_running():
                    if tactical_animation_timer.reached():
                        logger.info('Tactical reward end.')
                        break
                else:
                    tactical_animation_timer.reset()
            if tactical_class_timout.reached():
                logger.info('Tactical reward timeout.')
                break

        return True
Exemplo n.º 21
0
    def _handle_app_login(self):
        """
        Pages:
            in: Any page
            out: page_main
        """
        logger.hr('App login')

        confirm_timer = Timer(1.5, count=4).start()
        orientation_timer = Timer(5)
        login_success = False

        while 1:
            self.device.screenshot()
            if not login_success and orientation_timer.reached():
                # Screen may rotate after starting an app
                self.device.get_orientation()
                orientation_timer.reset()

            if self.handle_get_items():
                continue
            if self.handle_get_ship():
                continue
            if self.appear_then_click(LOGIN_ANNOUNCE,
                                      offset=(30, 30),
                                      interval=5):
                continue
            if self.appear(EVENT_LIST_CHECK, offset=(30, 30), interval=5):
                self.device.click(BACK_ARROW)
                continue
            if self.appear_then_click(MAINTENANCE_ANNOUNCE,
                                      offset=(30, 30),
                                      interval=5):
                continue
            if self.appear_then_click(LOGIN_GAME_UPDATE,
                                      offset=(30, 30),
                                      interval=5):
                continue
            if self.appear_then_click(LOGIN_RETURN_SIGN,
                                      offset=(30, 30),
                                      interval=5):
                continue
            if self.appear_then_click(LOGIN_RETURN_INFO,
                                      offset=(30, 30),
                                      interval=5):
                continue
            if server.server == 'cn' and not login_success:
                if self.handle_cn_user_agreement():
                    continue
            if self.handle_popup_confirm('LOGIN'):
                continue
            if self.handle_guild_popup_cancel():
                continue
            if self.handle_urgent_commission():
                continue
            if self.appear_then_click(GOTO_MAIN, offset=(30, 30), interval=5):
                continue

            if self.appear_then_click(LOGIN_CHECK, interval=5):
                if not login_success:
                    logger.info('Login success')
                    login_success = True

            if self.appear(MAIN_CHECK):
                if confirm_timer.reached():
                    logger.info('Login to main confirm')
                    break
            else:
                confirm_timer.reset()

        self.config.start_time = datetime.now()
        return True
Exemplo n.º 22
0
    def enter_map(self, button, mode='normal'):
        """Enter a campaign.

        Args:
            button: Campaign to enter.
            mode (str): 'normal' or 'hard' or 'cd'
        """
        logger.hr('Enter map')
        campaign_timer = Timer(2)
        map_timer = Timer(1)
        fleet_timer = Timer(1)
        checked_in_map = False
        while 1:
            self.device.screenshot()

            if not checked_in_map and self.is_in_map():
                logger.info('Already in map, skip enter_map.')
                return False
            else:
                checked_in_map = True

            # Map preparation
            if map_timer.reached() and self.appear(MAP_PREPARATION):
                self.device.sleep(0.3)  # Wait for map information.
                self.device.screenshot()
                if self.handle_map_clear_mode_stop():
                    self.enter_map_cancel()
                    raise ScriptEnd(
                        f'Reach condition: {self.config.CLEAR_MODE_STOP_CONDITION}'
                    )
                self.handle_fast_forward()
                self.device.click(MAP_PREPARATION)
                map_timer.reset()
                campaign_timer.reset()
                continue

            # Fleet preparation
            if fleet_timer.reached() and self.appear(FLEET_PREPARATION):
                if self.config.ENABLE_FLEET_CONTROL:
                    if mode == 'normal' or mode == 'hard':
                        self.fleet_preparation()
                self.device.click(FLEET_PREPARATION)
                fleet_timer.reset()
                campaign_timer.reset()
                continue

            # Retire
            if self.handle_retirement():
                continue

            # Emotion
            if self.handle_combat_low_emotion():
                continue

            # Urgent commission
            if self.handle_urgent_commission():
                continue

            # Story skip
            if self.handle_story_skip():
                campaign_timer.reset()
                continue

            # Enter campaign
            if campaign_timer.reached() and self.is_in_stage():
                self.device.click(button)
                campaign_timer.reset()
                continue

            # End
            if self.handle_in_map_with_enemy_searching():
                break

        return True
    def _set_2x_book_status(self,
                            status,
                            check_button,
                            box_button,
                            skip_first_screenshot=True):
        """
        Set appropriate 2x book setting
        with corresponding status and buttons
        Built with retry mechanism that limits to 3
        attempts that span 3 second intervals each

        Args:
            status (string):
                on or off
            check_button (Button):
                button to check before attempting to click
            box_button (Button):
                button to click and image color count against
            skip_first_screenshot (bool):
                namesake

        Returns:
            bool:
                True if detected having set correctly
                False can occur for 2 reasons either
                assets insufficient to detect properly
                or 2x book setting is absent

        """
        confirm_timer = Timer(1).start()
        clicked_threshold = 0
        while 1:
            if skip_first_screenshot:
                skip_first_screenshot = False
            else:
                self.device.screenshot()

            if clicked_threshold > 3:
                break

            if self.appear(check_button,
                           offset=self._auto_search_menu_offset,
                           interval=3):
                box_button.load_offset(check_button)
                enabled = self.image_color_count(box_button.button,
                                                 color=(156, 255, 82),
                                                 threshold=221,
                                                 count=20)
                if (status == 'on' and enabled) or (status == 'off'
                                                    and not enabled):
                    return True
                if (status == 'on' and not enabled) or (status == 'off'
                                                        and enabled):
                    self.device.click(box_button)

                clicked_threshold += 1

            if not clicked_threshold and confirm_timer.reached():
                logger.info('Map do not have 2x book setting')
                return False

        logger.warning(f'Wait time has expired; Cannot set 2x book setting')
        return False
Exemplo n.º 24
0
class Scroll:
    color_threshold = 221
    drag_threshold = 0.05
    edge_add = (0.1, 0.2)

    def __init__(self, area, color, is_vertical=True, name='Scroll'):
        """
        Args:
            area (Button, tuple): A button or area of the whole scroll.
            color (tuple): RGB of the scroll
            is_vertical (bool): True if vertical, false if horizontal.
            name (str):
        """
        if isinstance(area, Button):
            name = area.name
            area = area.area
        self.area = area
        self.color = color
        self.is_vertical = is_vertical
        self.name = name

        if self.is_vertical:
            self.total = self.area[3] - self.area[1]
        else:
            self.total = self.area[2] - self.area[0]
        # Just default value, will change in match_color()
        self.length = self.total / 2
        self.drag_interval = Timer(1)

    def match_color(self, main):
        """
        Args:
            main (ModuleBase):

        Returns:
            np.ndarray: Shape (n,), dtype bool.
        """
        image = main.image_crop(self.area)
        image = color_similarity_2d(image, color=self.color)
        mask = np.max(image,
                      axis=1 if self.is_vertical else 0) > self.color_threshold
        self.length = np.sum(mask)
        return mask

    def cal_position(self, main):
        """
        Args:
            main (ModuleBase):

        Returns:
            float: 0 to 1.
        """
        mask = self.match_color(main)
        middle = np.mean(np.where(mask)[0])

        position = (middle - self.length / 2) / (self.total - self.length)
        position = position if position > 0 else 0.0
        position = position if position < 1 else 1.0
        logger.attr(
            self.name,
            f'{position:.2f} ({middle}-{self.length / 2})/({self.total}-{self.length})'
        )
        return position

    def position_to_screen(self, position, random_range=(-0.05, 0.05)):
        """
        Convert scroll position to screen coordinates.
        Call cal_position() or match_color() to get length, before calling this.

        Args:
            position (int, float):
            random_range (tuple):

        Returns:
            tuple[int]: (upper_left_x, upper_left_y, bottom_right_x, bottom_right_y)
        """
        position = np.add(position, random_range)
        middle = position * (self.total - self.length) + self.length / 2
        middle = middle.astype(int)
        if self.is_vertical:
            middle += self.area[1]
            area = (self.area[0], middle[0], self.area[2], middle[1])
        else:
            middle += self.area[0]
            area = (middle[0], self.area[1], middle[1], self.area[3])
        return area

    def appear(self, main):
        """
        Args:
            main (ModuleBase):

        Returns:
            bool
        """
        return np.mean(self.match_color(main)) > 0.1

    def at_top(self, main):
        return self.cal_position(main) < 0.05

    def at_bottom(self, main):
        return self.cal_position(main) > 0.95

    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()

    def set_top(self,
                main,
                random_range=(-0.05, 0.05),
                skip_first_screenshot=True):
        return self.set(0.00,
                        main=main,
                        random_range=random_range,
                        skip_first_screenshot=skip_first_screenshot)

    def set_bottom(self,
                   main,
                   random_range=(-0.05, 0.05),
                   skip_first_screenshot=True):
        return self.set(1.00,
                        main=main,
                        random_range=random_range,
                        skip_first_screenshot=skip_first_screenshot)

    def drag_page(self,
                  page,
                  main,
                  random_range=(-0.05, 0.05),
                  skip_first_screenshot=True):
        """
        Drag scroll forward or backward.

        Args:
            page (int, float): Relative position to drag. 1.0 means next page, -1.0 means previous page.
            main (ModuleBase):
            random_range (tuple[int]):
            skip_first_screenshot:
        """
        if not skip_first_screenshot:
            main.device.screenshot()
        current = self.cal_position(main)

        multiply = self.length / (self.total - self.length)
        target = current + page * multiply
        target = round(min(max(target, 0), 1), 3)
        self.set(target,
                 main=main,
                 random_range=random_range,
                 skip_first_screenshot=True)

    def next_page(self,
                  main,
                  random_range=(-0.01, 0.01),
                  skip_first_screenshot=True):
        self.drag_page(0.8,
                       main=main,
                       random_range=random_range,
                       skip_first_screenshot=skip_first_screenshot)

    def prev_page(self,
                  main,
                  random_range=(-0.01, 0.01),
                  skip_first_screenshot=True):
        self.drag_page(-0.8,
                       main=main,
                       random_range=random_range,
                       skip_first_screenshot=skip_first_screenshot)
Exemplo n.º 25
0
    def _goto(self, location, expected=''):
        """Goto a grid directly and handle ambush, air raid, mystery picked up, combat.

        Args:
            location (tuple, str, GridInfo): Destination.
        """
        location = location_ensure(location)
        result_mystery = ''
        self.movable_before = self.map.select(is_siren=True)
        if self.hp_withdraw_triggered():
            self.withdraw()

        while 1:
            sight = self.map.camera_sight
            self.in_sight(location, sight=(sight[0], 0, sight[2], sight[3]))
            self.focus_to_grid_center()
            grid = self.convert_map_to_grid(location)

            self.ambush_color_initial()
            self.enemy_searching_color_initial()
            grid.__str__ = location
            result = 'nothing'
            self.device.click(grid)
            arrived = False
            # Wait to confirm fleet arrived. It does't appear immediately if fleet in combat .
            arrive_timer = Timer(0.5 + self.round_wait, count=2)
            arrive_unexpected_timer = Timer(1.5 + self.round_wait, count=6)
            # Wait after ambushed.
            ambushed_retry = Timer(0.5)
            # If nothing happens, click again.
            walk_timeout = Timer(20)
            walk_timeout.start()

            while 1:
                self.device.screenshot()
                grid.image = np.array(self.device.image)

                # Ambush
                if self.handle_ambush():
                    self.hp_get()
                    ambushed_retry.start()
                    walk_timeout.reset()

                # Mystery
                mystery = self.handle_mystery(button=grid)
                if mystery:
                    self.mystery_count += 1
                    result = 'mystery'
                    result_mystery = mystery

                # Combat
                if self.config.ENABLE_MAP_FLEET_LOCK and not self.is_in_map():
                    if self.handle_retirement():
                        self.map_offensive()
                        walk_timeout.reset()
                    if self.handle_combat_low_emotion():
                        walk_timeout.reset()
                if self.combat_appear():
                    self.combat(
                        expected_end=self._expected_combat_end(expected),
                        fleet_index=self.fleet_current_index)
                    self.hp_get()
                    arrived = True if not self.config.MAP_HAS_MOVABLE_ENEMY else False
                    result = 'combat'
                    self.battle_count += 1
                    self.fleet_ammo -= 1
                    if 'siren' in expected or (
                            self.config.MAP_HAS_MOVABLE_ENEMY
                            and not expected):
                        self.siren_count += 1
                    elif self.map[location].may_enemy:
                        self.map[location].is_cleared = True

                    self.handle_boss_appear_refocus()
                    grid = self.convert_map_to_grid(location)
                    walk_timeout.reset()

                # Cat attack animation
                if self.handle_map_cat_attack():
                    walk_timeout.reset()
                    continue

                if self.handle_walk_out_of_step():
                    raise MapWalkError('walk_out_of_step')

                # Arrive
                if self.is_in_map() and \
                        (grid.predict_fleet() or
                         (walk_timeout.reached() and grid.predict_current_fleet())):
                    if not arrive_timer.started():
                        logger.info(f'Arrive {location2node(location)}')
                    arrive_timer.start()
                    arrive_unexpected_timer.start()
                    if not arrive_timer.reached():
                        continue
                    if expected and result not in expected:
                        if arrive_unexpected_timer.reached():
                            logger.warning('Arrive with unexpected result')
                        else:
                            continue
                    logger.info(
                        f'Arrive {location2node(location)} confirm. Result: {result}. Expected: {expected}'
                    )
                    arrived = True
                    break

                # End
                if ambushed_retry.started() and ambushed_retry.reached():
                    break
                if walk_timeout.reached():
                    logger.warning('Walk timeout. Retrying.')
                    self.ensure_edge_insight()
                    break

            # End
            if arrived:
                # Ammo grid needs to click again, otherwise the next click doesn't work.
                if self.map[location].may_ammo:
                    self.device.click(grid)
                break

        self.map[self.fleet_current].is_fleet = False
        self.map[location].wipe_out()
        self.map[location].is_fleet = True
        self.__setattr__('fleet_%s_location' % self.fleet_current_index,
                         location)
        if result_mystery == 'get_carrier':
            self.full_scan_carrier()
        if result == 'combat':
            self.round_battle()
        self.round_next()
        if self.round_is_new:
            self.full_scan_movable(enemy_cleared=result == 'combat')
            self.find_path_initial()
            raise MapEnemyMoved
        self.find_path_initial()
Exemplo n.º 26
0
    def _guild_logistics_collect(self, skip_first_screenshot=True):
        """
        Execute collect/accept screen transitions within
        logistics

        Args:
            skip_first_screenshot (bool):

        Returns:
            bool: If all guild logistics are check, no need to check them today.

        Pages:
            in: GUILD_LOGISTICS
            out: GUILD_LOGISTICS
        """
        logger.hr('Guild logistics')
        logger.attr('Guild master/official',
                    self.config.GuildOperation_SelectNewOperation)
        confirm_timer = Timer(1.5, count=3).start()
        exchange_interval = Timer(1.5, count=3)
        click_interval = Timer(0.5, count=1)
        supply_checked = False
        mission_checked = False
        exchange_checked = False
        exchange_count = 0

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

            # Handle all popups
            if self.handle_popup_confirm('GUILD_LOGISTICS'):
                confirm_timer.reset()
                exchange_interval.reset()
                continue
            if self.appear_then_click(GET_ITEMS_1, interval=2):
                confirm_timer.reset()
                exchange_interval.reset()
                continue
            if self._handle_guild_fleet_mission_start():
                confirm_timer.reset()
                continue

            if self._is_in_guild_logistics():
                # Supply
                if not supply_checked and self._guild_logistics_supply_available(
                ):
                    if click_interval.reached():
                        self.device.click(GUILD_SUPPLY)
                        click_interval.reset()
                    confirm_timer.reset()
                    continue
                else:
                    supply_checked = True
                # Mission
                if not mission_checked and self._guild_logistics_mission_available(
                ):
                    if click_interval.reached():
                        self.device.click(GUILD_MISSION)
                        click_interval.reset()
                    confirm_timer.reset()
                    continue
                else:
                    mission_checked = True
                # Exchange
                if not exchange_checked and exchange_interval.reached():
                    if self._guild_exchange():
                        confirm_timer.reset()
                        exchange_interval.reset()
                        exchange_count += 1
                        continue
                    else:
                        exchange_checked = True
                # End
                if not self.info_bar_count() and confirm_timer.reached():
                    break
                # if supply_checked and mission_checked and exchange_checked:
                #     break
                if exchange_count >= 5:
                    # If you run AL across days, then do guild exchange.
                    # There will show an error, said time is not up.
                    # Restart the game can't fix the problem.
                    # To fix this, you have to enter guild logistics once, then restart.
                    # If exchange for 5 times, this bug is considered to be triggered.
                    logger.warning(
                        'Unable to do guild exchange, probably because the timer in game was bugged'
                    )
                    raise GameBugError('Triggered guild logistics refresh bug')

            else:
                confirm_timer.reset()

        logger.info(
            f'supply_checked: {supply_checked}, mission_checked: {mission_checked}, '
            f'exchange_checked: {exchange_checked}, mission_finished: {self._guild_logistics_mission_finished}'
        )
        # Azur Lane receives new guild missions now
        # No longer consider `self._guild_logistics_mission_finished` as a check
        return all([supply_checked, mission_checked, exchange_checked])
Exemplo n.º 27
0
    def ui_get_current_page(self, skip_first_screenshot=True):
        """
        Args:
            skip_first_screenshot:

        Returns:
            Page:
        """
        logger.info('UI get current page')

        @run_once
        def app_check():
            if not self.device.app_is_running():
                raise GameNotRunningError('Game not running')

        @run_once
        def minicap_check():
            if self.config.Emulator_ControlMethod == 'uiautomator2':
                self.device.uninstall_minicap()

        timeout = Timer(5, count=10).start()
        while 1:
            if skip_first_screenshot:
                skip_first_screenshot = False
                if not hasattr(self.device, 'image') or self.device.image is None:
                    self.device.screenshot()
            else:
                self.device.screenshot()

            # End
            if timeout.reached():
                break

            # 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), interval=2) or self.ui_additional():
                timeout.reset()
                continue

            app_check()
            minicap_check()

        # Unknown page, need manual switching
        logger.warning('Unknown ui page')
        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')
        logger.critical('Please switch to a supported page before starting Alas')
        raise GamePageUnknownError
Exemplo n.º 28
0
    def _submarine_goto(self, location):
        """
        Move submarine to given location.

        Args:
            location (tuple, str, GridInfo): Destination.

        Returns:
            bool: If submarine moved.

        Pages:
            in: SUBMARINE_MOVE_CONFIRM
            out: SUBMARINE_MOVE_CONFIRM
        """
        location = location_ensure(location)
        moved = True
        while 1:
            self.in_sight(location, sight=self._walk_sight)
            self.focus_to_grid_center()
            grid = self.convert_global_to_local(location)
            grid.__str__ = location

            self.device.click(grid)
            arrived = False
            # Usually no need to wait
            arrive_timer = Timer(0.1, count=0)
            # If nothing happens, click again.
            walk_timeout = Timer(2, count=6).start()

            while 1:
                self.device.screenshot()
                self.view.update(image=self.device.image)

                # Arrive
                arrive_checker = grid.predict_submarine_move()
                if grid.predict_submarine() or (walk_timeout.reached()
                                                and grid.predict_fleet()):
                    arrive_checker = True
                    moved = False
                if arrive_checker:
                    if not arrive_timer.started():
                        logger.info(f'Arrive {location2node(location)}')
                    arrive_timer.start()
                    if not arrive_timer.reached():
                        continue
                    logger.info(
                        f'Submarine arrive {location2node(location)} confirm.')
                    if not moved:
                        logger.info(
                            f'Submarine already at {location2node(location)}')
                    arrived = True
                    break

                # End
                if walk_timeout.reached():
                    logger.warning('Walk timeout. Retrying.')
                    self.predict()
                    self.ensure_edge_insight(skip_first_update=False)
                    break

            # End
            if arrived:
                break

        return moved
Exemplo n.º 29
0
    def enter_map(self, button, mode='normal'):
        """Enter a campaign.

        Args:
            button: Campaign to enter.
            mode (str): 'normal' or 'hard' or 'cd'
        """
        logger.hr('Enter map')
        campaign_timer = Timer(5)
        map_timer = Timer(5)
        fleet_timer = Timer(5)
        checked_in_map = False
        self.stage_entrance = button

        while 1:
            self.device.screenshot()

            if not checked_in_map and self.is_in_map():
                logger.info('Already in map, skip enter_map.')
                return False
            else:
                checked_in_map = True

            # Map preparation
            if map_timer.reached() and self.handle_map_preparation():
                self.map_get_info()
                self.handle_fast_forward()
                if self.handle_map_stop():
                    self.enter_map_cancel()
                    raise ScriptEnd(
                        f'Reach condition: {self.config.STOP_IF_MAP_REACH}')
                self.device.click(MAP_PREPARATION)
                map_timer.reset()
                campaign_timer.reset()
                continue

            # Fleet preparation
            if fleet_timer.reached() and self.appear(FLEET_PREPARATION):
                if self.config.ENABLE_FLEET_CONTROL:
                    if mode == 'normal' or mode == 'hard':
                        self.fleet_preparation()
                self.device.click(FLEET_PREPARATION)
                fleet_timer.reset()
                campaign_timer.reset()
                continue

            # Retire
            if self.handle_retirement():
                continue

            # Use Data Key
            if self.handle_use_data_key():
                continue

            # Emotion
            if self.handle_combat_low_emotion():
                continue

            # Urgent commission
            if self.handle_urgent_commission():
                continue

            # Story skip
            if self.handle_story_skip():
                campaign_timer.reset()
                continue

            # Enter campaign
            if campaign_timer.reached() and self.appear_then_click(button):
                campaign_timer.reset()
                continue

            # End
            if self.handle_in_map_with_enemy_searching():
                self.handle_map_after_combat_story()
                break

        return True
Exemplo n.º 30
0
    def tactical_class_receive(self, skip_first_screenshot=True):
        """
        Receive tactical rewards and fill books.

        Args:
            skip_first_screenshot (bool):

        Returns:
            bool: If rewarded.

        Pages:
            in: page_reward, TACTICAL_CLASS_START
            out: page_reward
        """
        logger.hr('Tactical class receive', level=1)
        received = False
        # tactical cards can't be loaded that fast, confirm if it's empty.
        empty_confirm = Timer(0.6, count=2).start()
        while 1:
            if skip_first_screenshot:
                skip_first_screenshot = False
            else:
                self.device.screenshot()

            # End
            if received and self.appear(REWARD_CHECK, offset=(20, 20)):
                break

            # Get finish time
            if self.appear(TACTICAL_CHECK, offset=(20, 20), interval=2):
                self.interval_clear([POPUP_CONFIRM, POPUP_CANCEL, GET_MISSION])
                if self._tactical_get_finish():
                    self.device.click(BACK_ARROW)
                    self.interval_reset(TACTICAL_CHECK)
                    empty_confirm.reset()
                    received = True
                    continue
                else:
                    self.interval_clear(TACTICAL_CHECK)
                    if empty_confirm.reached():
                        self.device.click(BACK_ARROW)
                        empty_confirm.reset()
                        received = True
                        continue
            else:
                empty_confirm.reset()

            # Popups
            if self.appear_then_click(REWARD_2, offset=(20, 20), interval=3):
                continue
            if self.appear_then_click(REWARD_GOTO_TACTICAL,
                                      offset=(20, 20),
                                      interval=3):
                continue
            if self.handle_popup_confirm('TACTICAL'):
                continue
            if self.handle_urgent_commission():
                # Only one button in the middle, when skill reach max level.
                continue
            if self.ui_page_main_popups():
                continue
            if self.appear(TACTICAL_CLASS_CANCEL, offset=(30, 30), interval=2) \
                    and self.appear(TACTICAL_CLASS_START, offset=(30, 30)):
                if self._tactical_books_choose():
                    self.interval_reset(TACTICAL_CLASS_CANCEL)
                    self.interval_clear(
                        [POPUP_CONFIRM, POPUP_CANCEL, GET_MISSION])
                continue
            if self.appear(DOCK_CHECK, offset=(20, 20), interval=3):
                # Entered dock accidentally
                self.device.click(BACK_ARROW)
                continue
            if self.appear(SKILL_CONFIRM, offset=(20, 20), interval=3):
                # Game auto pops up the next skill to learn, close it
                self.device.click(BACK_ARROW)
                continue

        return True