예제 #1
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')
        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.appear_then_click(GUILD_MISSION_SELECT,
                                      offset=(20, 20),
                                      interval=2):
                # Select guild mission for guild leader
                # Hard-coded to select mission: Siren Subjugation III, defeat 300 enemies
                # This mission has the most guild supply and it's the easiest one for members to finish
                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('Triggered guild logistics refresh bug')
                    raise LogisticsRefreshBugHandler(
                        '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}'
        )
        return all([
            supply_checked, mission_checked, exchange_checked,
            self._guild_logistics_mission_finished
        ])
예제 #2
0
 def os_shop_get_coins(self):
     self._shop_yellow_coins = OCR_SHOP_YELLOW_COINS.ocr(self.device.image)
     self._shop_purple_coins = OCR_SHOP_PURPLE_COINS.ocr(self.device.image)
     logger.info(
         f'Yellow coins: {self._shop_yellow_coins}, purple coins: {self._shop_purple_coins}'
     )
예제 #3
0
    def update(self, camera=True):
        """Update map image

        Args:
            camera: True to update camera position and perspective data.
        """
        self.device.screenshot()
        if not camera:
            self.grids.update(image=self.device.image)
            return True

        try:
            self.grids = Grids(self.device.image, config=self.config)
        except Exception as e:
            if self.info_bar_count():
                logger.info('Perspective error cause by info bar. Waiting.')
                self.handle_info_bar()
                return self.update(camera=camera)
            else:
                raise e

        # Catch perspective error
        known_exception = self.info_bar_count()
        if len(self.grids.horizontal) > self.map.shape[1] + 2 or len(
                self.grids.vertical) > self.map.shape[0] + 2:
            if not known_exception:
                logger.info('Perspective Error. Too many lines')
            self.grids.correct = False
        if len(self.grids.horizontal) <= 3 or len(self.grids.vertical) <= 3:
            if not known_exception:
                logger.info('Perspective Error. Too few lines')
            self.grids.correct = False

        if not self.grids.correct:
            if self.info_bar_count():
                logger.info('Perspective error cause by info bar. Waiting.')
                self.handle_info_bar()
                return self.update(camera=camera)
            else:
                self.grids.save_error_image()

        # Set camera position
        if self.grids.left_edge:
            x = 0 + self.grids.center_grid[0]
        elif self.grids.right_edge:
            x = self.map.shape[0] - self.grids.shape[
                0] + self.grids.center_grid[0]
        else:
            x = self.camera[0]
        if self.grids.lower_edge:
            y = 0 + self.grids.center_grid[1]
        elif self.grids.upper_edge:
            y = self.map.shape[1] - self.grids.shape[
                1] + self.grids.center_grid[1]
        else:
            y = self.camera[1]

        if self.camera != (x, y):
            logger.info(
                f'      camera corrected: {location2node(self.camera)} -> {location2node((x, y))}'
            )
        self.camera = (x, y)
        self.show_camera()
예제 #4
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():
                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
예제 #5
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.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
예제 #6
0
 def prev(self):
     self.daily_current -= 1
     logger.info('Switch to %s' % str(self.daily_current))
     self.device.click(DAILY_PREV)
     self._wait_daily_switch()
     self.device.screenshot()
예제 #7
0
    def _commission_choose(self, daily, urgent):
        """
        Args:
            daily (SelectedGrids):
            urgent (SelectedGrids):

        Returns:
            SelectedGrids, SelectedGrids: Chosen daily commission, Chosen urgent commission
        """
        # Count Commission
        total = daily.add_by_eq(urgent)
        self.max_commission = 4
        for comm in total:
            if comm.genre == 'event_daily':
                self.max_commission = 5
        running_count = int(np.sum([1 for c in total
                                    if c.status == 'running']))
        logger.attr('Running', f'{running_count}/{self.max_commission}')
        if running_count >= self.max_commission:
            return SelectedGrids([]), SelectedGrids([])

        # Filter
        COMMISSION_FILTER.load(self.config.Commission_CommissionFilter)
        run = COMMISSION_FILTER.apply(total.grids, func=self._commission_check)
        logger.attr('Filter_sort', ' > '.join([str(c) for c in run]))
        run = SelectedGrids(run)

        # Add shortest
        no_shortest = run.delete(SelectedGrids(['shortest']))
        if no_shortest.count + running_count < self.max_commission:
            if no_shortest.count < run.count:
                logger.info(
                    'Not enough commissions to run, add shortest daily commissions'
                )
                COMMISSION_FILTER.load(SHORTEST_FILTER)
                shortest = COMMISSION_FILTER.apply(daily,
                                                   func=self._commission_check)
                run = no_shortest.add_by_eq(SelectedGrids(shortest))
                logger.attr('Filter_sort', ' > '.join([str(c) for c in run]))
            else:
                logger.info('Not enough commissions to run')

        # Separate daily and urgent
        run = run[:self.max_commission - running_count]
        daily_choose = run.intersect_by_eq(daily)
        urgent_choose = run.intersect_by_eq(urgent)
        if daily_choose:
            logger.info('Choose daily commission')
            for comm in daily_choose:
                logger.info(comm)
        if urgent_choose:
            logger.info('Choose urgent commission')
            for comm in urgent_choose:
                logger.info(comm)

        return daily_choose, urgent_choose
예제 #8
0
    def daily_execute(self, remain=3, stage=1, fleet=1):
        """
        Args:
            remain (int): Remain daily challenge count.
            stage (int): Index of stage counted from top, 1 to 3.
            fleet (int): Index of fleet to use.

        Returns:
            bool: True if success, False if daily locked.

        Pages:
            in: page_daily
            out: page_daily
        """
        logger.hr(f'Daily {self.daily_current}')
        logger.info(f'remain={remain}, stage={stage}, fleet={fleet}')

        def daily_enter_check():
            return self.appear(DAILY_ENTER_CHECK)

        def daily_end():
            if self.appear(BATTLE_PREPARATION, interval=2):
                self.device.click(BACK_ARROW)
            return self.appear(DAILY_ENTER_CHECK) or self.appear(BACK_ARROW)

        self.ui_click(click_button=DAILY_ENTER,
                      check_button=daily_enter_check,
                      appear_button=DAILY_CHECK,
                      skip_first_screenshot=True)
        if self.appear(DAILY_LOCKED):
            logger.info('Daily locked')
            self.ui_click(click_button=BACK_ARROW, check_button=DAILY_CHECK)
            self.device.sleep((1, 1.2))
            return False

        button = DAILY_MISSION_LIST[stage - 1]
        for n in range(remain):
            logger.hr(f'Count {n + 1}')
            result = self.daily_enter(button)
            if not result:
                break
            if self.daily_current == 3:
                logger.info('Submarine daily skip not unlocked, skip')
                self.ui_click(click_button=BACK_ARROW,
                              check_button=daily_enter_check,
                              skip_first_screenshot=True)
                break
            # Execute classic daily run
            self.ui_ensure_index(fleet,
                                 letter=OCR_DAILY_FLEET_INDEX,
                                 prev_button=DAILY_FLEET_PREV,
                                 next_button=DAILY_FLEET_NEXT,
                                 fast=False,
                                 skip_first_screenshot=True)
            self.combat(emotion_reduce=False,
                        save_get_items=False,
                        expected_end=daily_end,
                        balance_hp=False)

        self.ui_click(click_button=BACK_ARROW,
                      check_button=DAILY_CHECK,
                      additional=self.handle_daily_additional,
                      skip_first_screenshot=True)
        self.device.sleep((1, 1.2))
        return True
예제 #9
0
    def auto_search_combat(self, drop=None):
        """
        Args:
            drop (DropImage):

        Returns:
            bool: True if enemy cleared, False if fleet died.

        Pages:
            in: is_combat_loading()
            out: combat status
        """
        logger.info('Auto search combat loading')
        self.device.screenshot_interval_set('combat')
        while 1:
            self.device.screenshot()

            if self.handle_combat_automation_confirm():
                continue

            # End
            if self.handle_os_auto_search_map_option(drop=drop):
                break
            if self.is_combat_executing():
                break
            if self.is_in_map():
                break

        logger.info('Auto Search combat execute')
        self.submarine_call_reset()
        submarine_mode = 'do_not_use'
        if self.config.Submarine_Fleet:
            submarine_mode = self.config.Submarine_Mode

        success = True
        while 1:
            self.device.screenshot()

            if self.handle_submarine_call(submarine_mode):
                continue
            if self.handle_os_auto_search_map_option(drop=drop,
                                                     enable=success):
                continue

            # End
            if self.is_combat_executing():
                continue
            if self.handle_auto_search_battle_status():
                success = False
                continue
            if self.handle_auto_search_exp_info():
                success = False
                continue
            if self.handle_map_event():
                continue
            if self.is_in_map():
                self.device.screenshot_interval_set()
                break

        logger.info('Combat end.')
        return success
예제 #10
0
def main(ini_name=''):
    if not ini_name:
        ini_name = pyw_name
    ini_name = ini_name.lower()

    # Load default value from .ini file.
    config_file = f'./config/{ini_name}.ini'
    config = configparser.ConfigParser(interpolation=None)
    try:
        config.read_file(codecs.open(config_file, "r", "utf8"))
    except FileNotFoundError:
        logger.info('Config file not exists, copy from ./config/template.ini')
        shutil.copy('./config/template.ini', config_file)
        config.read_file(codecs.open(config_file, "r", "utf8"))

    config = update_config_from_template(config, file=config_file)

    event_folder = [f for f in os.listdir('./campaign') if f.startswith('event_') and f.split('_')[-1] == server.server]
    event_latest = sorted([f for f in event_folder], reverse=True)[0]
    event_folder = [dic_eng_to_true_eng.get(f, f) for f in event_folder][::-1]
    event_latest = dic_eng_to_true_eng.get(event_latest, event_latest)

    saved_config = {}
    for opt, option in config.items():
        for key, value in option.items():
            key = dic_eng_to_true_eng.get(key, key)
            if value in dic_eng_to_true_eng:
                value = dic_eng_to_true_eng.get(value, value)
            if value == 'None':
                value = ''

            saved_config[key] = value

    def default(name):
        """Get default value in .ini file.
        Args:
            name (str): option, in chinese.

        Returns:
            str: Default value, in chinese.
        """
        name = name.strip('-')
        return saved_config.get(name, '')

    def choice_list(total):
        return [str(index) for index in range(1, total + 1)]

    # Don't use checkbox in gooey, use drop box instead.
    # https://github.com/chriskiehl/Gooey/issues/148
    # https://github.com/chriskiehl/Gooey/issues/485

    parser = GooeyParser(description=f'AzurLaneAutoScript, An Azur Lane automation tool. Config: {config_file}')
    subs = parser.add_subparsers(help='commands', dest='command')

    # ==========setting==========
    setting_parser = subs.add_parser('setting')

    # 选择关卡
    stage = setting_parser.add_argument_group('Level settings', 'Need to run once to save options')
    stage.add_argument('--enable_stop_condition', default=default('--enable_stop_condition'), choices=['yes', 'no'])
    stage.add_argument('--enable_fast_forward', default=default('--enable_fast_forward'), choices=['yes', 'no'], help='Enable or disable clearing mode')

    stop = stage.add_argument_group('Stop condition', 'After triggering, it will not stop immediately. It will complete the current attack first, and fill in 0 if it is not needed.')
    stop.add_argument('--if_count_greater_than', default=default('--if_count_greater_than'), help='The previous setting will be used, and the number\n of deductions will be deducted after completion of the attack until it is cleared.')
    stop.add_argument('--if_time_reach', default=default('--if_time_reach'), help='Use the time within the next 24 hours, the previous setting will be used, and it will be cleared\n after the trigger. It is recommended to advance about\n 10 minutes to complete the current attack. Format 14:59')
    stop.add_argument('--if_oil_lower_than', default=default('--if_oil_lower_than'))
    stop.add_argument('--if_trigger_emotion_control', default=default('--if_trigger_emotion_control'), choices=['yes', 'no'], help='If yes, wait for reply, complete this time, stop \nIf no, wait for reply, complete this time, continue')
    stop.add_argument('--if_dock_full', default=default('--if_dock_full'), choices=['yes', 'no'])

    # 出击舰队
    fleet = setting_parser.add_argument_group('Attack fleet', 'No support for alternate lane squadrons, inactive map or weekly mode will ignore the step setting')
    fleet.add_argument('--enable_fleet_control', default=default('--enable_fleet_control'), choices=['yes', 'no'])
    fleet.add_argument('--enable_map_fleet_lock', default=default('--enable_map_fleet_lock'), choices=['yes', 'no'])

    f1 = fleet.add_argument_group('Mob Fleet', 'Players can choose a formation before battle. Though it has no effect appearance-wise, the formations applies buffs to certain stats.\nLine Ahead: Increases Firepower and Torpedo by 15%, but reduces Evasion by 10% (Applies only to Vanguard fleet)\nDouble Line: Increases Evasion by 30%, but decreases Firepower and Torpedo by 5% (Applies only to Vanguard fleet)\nDiamond: Increases Anti-Air by 20% (no penalties, applies to entire fleet)')
    f1.add_argument('--fleet_index_1', default=default('--fleet_index_1'), choices=['1', '2', '3', '4', '5', '6'])
    f1.add_argument('--fleet_formation_1', default=default('--fleet_formation_1'), choices=['Line Ahead', 'Double Line', 'Diamond'])
    f1.add_argument('--fleet_step_1', default=default('--fleet_step_1'), choices=['1', '2', '3', '4', '5', '6'], help='In event map, fleet has limit on moving, so fleet_step is how far can a fleet goes in one operation, if map cleared, it will be ignored')

    f2 = fleet.add_argument_group('Boss Fleet')
    f2.add_argument('--fleet_index_2', default=default('--fleet_index_2'), choices=['do_not_use', '1', '2', '3', '4', '5', '6'])
    f2.add_argument('--fleet_formation_2', default=default('--fleet_formation_2'), choices=['Line Ahead', 'Double Line', 'Diamond'])
    f2.add_argument('--fleet_step_2', default=default('--fleet_step_2'), choices=['1', '2', '3', '4', '5', '6'], help='In event map, fleet has limit on moving, so fleet_step is how far can a fleet goes in one operation, if map cleared, it will be ignored')

    f3 = fleet.add_argument_group('Alternate Mob Fleet')
    f3.add_argument('--fleet_index_3', default=default('--fleet_index_3'), choices=['do_not_use', '1', '2', '3', '4', '5', '6'])
    f3.add_argument('--fleet_formation_3', default=default('--fleet_formation_3'), choices=['Line Ahead', 'Double Line', 'Diamond'])
    f3.add_argument('--fleet_step_3', default=default('--fleet_step_3'), choices=['1', '2', '3', '4', '5', '6'], help='In event map, fleet has limit on moving, so fleet_step is how far can a fleet goes in one operation, if map cleared, it will be ignored')

    f4 = fleet.add_argument_group('Auto-mode')
    f4.add_argument('--combat_auto_mode', default=default('--combat_auto_mode'), choices=['combat_auto', 'combat_manual', 'stand_still_in_the_middle'])

    # 潜艇设置
    submarine = setting_parser.add_argument_group('Submarine settings', 'Only supported: hunt_only, do_not_use and every_combat')
    submarine.add_argument('--fleet_index_4', default=default('--fleet_index_4'), choices=['do_not_use', '1', '2'])
    submarine.add_argument('--submarine_mode', default=default('--submarine_mode'), choices=['do_not_use', 'hunt_only', 'every_combat', 'when_no_ammo', 'when_boss_combat', 'when_boss_combat_boss_appear'])

    # 心情控制
    emotion = setting_parser.add_argument_group('Mood control')
    emotion.add_argument('--enable_emotion_reduce', default=default('--enable_emotion_reduce'), choices=['yes', 'no'])
    emotion.add_argument('--ignore_low_emotion_warn', default=default('--ignore_low_emotion_warn'), choices=['yes', 'no'])

    e1 = emotion.add_argument_group('Mob Fleet')
    e1.add_argument('--emotion_recover_1', default=default('--emotion_recover_1'), choices=['not_in_dormitory', 'dormitory_floor_1', 'dormitory_floor_2'])
    e1.add_argument('--emotion_control_1', default=default('--emotion_control_1'), choices=['keep_high_emotion', 'avoid_green_face', 'avoid_yellow_face', 'avoid_red_face'])
    e1.add_argument('--hole_fleet_married_1', default=default('--hole_fleet_married_1'), choices=['yes', 'no'])

    e2 = emotion.add_argument_group('BOSS Fleet')
    e2.add_argument('--emotion_recover_2', default=default('--emotion_recover_2'), choices=['not_in_dormitory', 'dormitory_floor_1', 'dormitory_floor_2'])
    e2.add_argument('--emotion_control_2', default=default('--emotion_control_2'), choices=['keep_high_emotion', 'avoid_green_face', 'avoid_yellow_face', 'avoid_red_face'])
    e2.add_argument('--hole_fleet_married_2', default=default('--hole_fleet_married_2'), choices=['yes', 'no'])

    e3 = emotion.add_argument_group('Alternate Mob Fleet', 'Will be used when the first team triggers mood control')
    e3.add_argument('--emotion_recover_3', default=default('--emotion_recover_3'), choices=['not_in_dormitory', 'dormitory_floor_1', 'dormitory_floor_2'])
    e3.add_argument('--emotion_control_3', default=default('--emotion_control_3'), choices=['keep_high_emotion', 'avoid_green_face', 'avoid_yellow_face', 'avoid_red_face'])
    e3.add_argument('--hole_fleet_married_3', default=default('--hole_fleet_married_3'), choices=['yes', 'no'])

    # 血量平衡
    hp = setting_parser.add_argument_group('HP control', 'Fleet lock must be turned off to take effect')
    hp.add_argument('--enable_hp_balance', default=default('--enable_hp_balance'), choices=['yes', 'no'])
    hp.add_argument('--enable_low_hp_withdraw', default=default('--enable_low_hp_withdraw'), choices=['yes', 'no'])
    hp_balance = hp.add_argument_group('HP Balance', '')
    hp_balance.add_argument('--scout_hp_difference_threshold', default=default('--scout_hp_difference_threshold'), help='When the difference in HP volume is greater than the threshold, transpose')
    hp_balance.add_argument('--scout_hp_weights', default=default('--scout_hp_weights'), help='Should be repaired when there is a difference in Vanguard, format 1000,1000,1000')
    hp_add = hp.add_argument_group('Emergency repair', '')
    hp_add.add_argument('--emergency_repair_single_threshold', default=default('--emergency_repair_single_threshold'), help='Used when single shipgirl is below the threshold')
    hp_add.add_argument('--emergency_repair_hole_threshold', default=default('--emergency_repair_hole_threshold'), help='Used when all front rows or all back rows are below the threshold')
    hp_withdraw = hp.add_argument_group('Low HP volume withdrawal', '')
    hp_withdraw.add_argument('--low_hp_withdraw_threshold', default=default('--low_hp_withdraw_threshold'), help='When HP is below the threshold, retreat')

    # 退役选项
    retire = setting_parser.add_argument_group('Retirement settings', '')
    retire.add_argument('--enable_retirement', default=default('--enable_retirement'), choices=['yes', 'no'])
    retire.add_argument('--retire_method', default=default('--retire_method'), choices=['enhance', 'one_click_retire', 'old_retire'])
    retire.add_argument('--retire_amount', default=default('--retire_amount'), choices=['retire_all', 'retire_10'])
    retire.add_argument('--enhance_favourite', default=default('--enhance_favourite'), choices=['yes', 'no'])

    rarity = retire.add_argument_group('Retirement rarity', 'The ship type selection is not supported yet. Ignore the following options when using one-key retirement')
    rarity.add_argument('--retire_n', default=default('--retire_n'), choices=['yes', 'no'], help='N')
    rarity.add_argument('--retire_r', default=default('--retire_r'), choices=['yes', 'no'], help='R')
    rarity.add_argument('--retire_sr', default=default('--retire_sr'), choices=['yes', 'no'], help='SR')
    rarity.add_argument('--retire_ssr', default=default('--retire_ssr'), choices=['yes', 'no'], help='SSR')

    # 掉落记录
    drop = setting_parser.add_argument_group('Drop record', 'Save screenshots of dropped items, which will slow down the click speed when settlement is enabled')
    drop.add_argument('--enable_drop_screenshot', default=default('--enable_drop_screenshot'), choices=['yes', 'no'])
    drop.add_argument('--drop_screenshot_folder', default=default('--drop_screenshot_folder'))

    clear = setting_parser.add_argument_group('Wasteland mode', 'Unopened maps will stop after completion. Opened maps will ignore options, and its done if you do not open up')
    clear.add_argument('--enable_map_clear_mode', default=default('--enable_map_clear_mode'), choices=['yes', 'no'])
    clear.add_argument('--clear_mode_stop_condition', default=default('--clear_mode_stop_condition'), choices=['map_100', 'map_3_star', 'map_green'])
    clear.add_argument('--map_star_clear_all', default=default('--map_star_clear_all'), choices=['index_1', 'index_2', 'index_3', 'do_not_use'], help='The first few stars are to destroy all enemy ships')

    # ==========reward==========
    reward_parser = subs.add_parser('reward')
    reward_condition = reward_parser.add_argument_group('Triggering conditions', 'Need to run once to save the options, after running it will enter the on-hook vegetable collection mode')
    reward_condition.add_argument('--enable_reward', default=default('--enable_reward'), choices=['yes', 'no'])
    reward_condition.add_argument('--reward_interval', default=default('--reward_interval'), choices=['20', '30', '60'], help='How many minutes to trigger collection')

    reward_oil = reward_parser.add_argument_group('Oil supplies', '')
    reward_oil.add_argument('--enable_oil_reward', default=default('--enable_oil_reward'), choices=['yes', 'no'])
    reward_oil.add_argument('--enable_coin_reward', default=default('--enable_coin_reward'), choices=['yes', 'no'])

    reward_mission = reward_parser.add_argument_group('mission rewards', '')
    reward_mission.add_argument('--enable_mission_reward', default=default('--enable_mission_reward'), choices=['yes', 'no'])

    reward_commission = reward_parser.add_argument_group('Commission settings', '')
    reward_commission.add_argument('--enable_commission_reward', default=default('--enable_commission_reward'), choices=['yes', 'no'])
    reward_commission.add_argument('--commission_time_limit', default=default('--commission_time_limit'), help='Ignore orders whose completion time exceeds the limit, Format: 23:30. Fill in 0 if it is not needed')

    priority1 = reward_commission.add_argument_group('Commission priority by time duration', '')
    priority1.add_argument('--duration_shorter_than_2', default=default('--duration_shorter_than_2'), help='')
    priority1.add_argument('--duration_longer_than_6', default=default('--duration_longer_than_6'), help='')
    priority1.add_argument('--expire_shorter_than_2', default=default('--expire_shorter_than_2'), help='')
    priority1.add_argument('--expire_longer_than_6', default=default('--expire_longer_than_6'), help='')

    priority2 = reward_commission.add_argument_group('Daily Commission priority', '')
    priority2.add_argument('--daily_comm', default=default('--daily_comm'), help='Daily resource development, high-level tactical research and development')
    priority2.add_argument('--major_comm', default=default('--major_comm'), help='1200 oil / 1000 oil commission')

    priority3 = reward_commission.add_argument_group('Additional commission priority', '')
    priority3.add_argument('--extra_drill', default=default('--extra_drill'), help='Short-range Sailing Training, Coastal Defense Patrol')
    priority3.add_argument('--extra_part', default=default('--extra_part'), help='Small Merchant Escort, Forest Protection Commission')
    priority3.add_argument('--extra_cube', default=default('--extra_cube'), help='Fleet Exercise Ⅲ, Fleet Escort ExerciseFleet Exercise Ⅲ')
    priority3.add_argument('--extra_oil', default=default('--extra_oil'), help='Small-scale Oil Extraction, Large-scale Oil Extraction')
    priority3.add_argument('--extra_book', default=default('--extra_book'), help='Small Merchant Escort, Large Merchant Escort')

    priority4 = reward_commission.add_argument_group('Urgent commission priority', '')
    priority4.add_argument('--urgent_drill', default=default('--urgent_drill'), help='Defend the transport troops, annihilate the enemy elite troops')
    priority4.add_argument('--urgent_part', default=default('--urgent_part'), help='Support Vila Vela Island, support terror Banner')
    priority4.add_argument('--urgent_cube', default=default('--urgent_cube'), help='Rescue merchant ship, enemy attack')
    priority4.add_argument('--urgent_book', default=default('--urgent_book'), help='Support Tuhaoer Island, support Moe Island')
    priority4.add_argument('--urgent_box', default=default('--urgent_box'), help='BIW Gear Transport, NYB Gear Transport')
    priority4.add_argument('--urgent_gem', default=default('--urgent_gem'), help='BIW VIP Escort, NYB VIP Escort')
    priority4.add_argument('--urgent_ship', default=default('--urgent_ship'), help='Small Launch Ceremony, Fleet Launch Ceremony, Alliance Launch Ceremony')

    reward_tactical = reward_parser.add_argument_group('Classroom', 'Only support continuation of skill books, not new skills')
    reward_tactical.add_argument('--enable_tactical_reward', default=default('--enable_tactical_reward'), choices=['yes', 'no'])
    reward_tactical.add_argument('--tactical_night_range', default=default('--tactical_night_range'), help='Format 23:30-06:30')
    reward_tactical.add_argument('--tactical_book_tier', default=default('--tactical_book_tier'), choices=['3', '2', '1'], help='Wich skill book will use first\nT3 is a gold book, T2 is a purple book, T1 is a blue book')
    reward_tactical.add_argument('--tactical_exp_first', default=default('--tactical_exp_first'), choices=['yes', 'no'], help='Choose Yes, give priority to the 150% bonus \nSelect No, give priority to the skills book with the same rarity')
    reward_tactical.add_argument('--tactical_book_tier_night', default=default('--tactical_book_tier_night'), choices=['3', '2', '1'])
    reward_tactical.add_argument('--tactical_exp_first_night', default=default('--tactical_exp_first_night'), choices=['yes', 'no'])

    # ==========emulator==========
    emulator_parser = subs.add_parser('emulator')
    emulator = emulator_parser.add_argument_group('Emulator', 'Need to run once to save the options, it will check whether the game is started \nIf the game has not started, it will be started')
    emulator.add_argument('--serial', default=default('--serial'), help='Bluestacks 127.0.0.1:5555 \nNox 127.0.0.1:62001')
    emulator.add_argument('--package_name', default='com.YoStarEN.AzurLane', help='')

    debug = emulator_parser.add_argument_group('Debug settings', '')
    debug.add_argument('--enable_error_log_and_screenshot_save', default=default('--enable_error_log_and_screenshot_save'), choices=['yes', 'no'])
    debug.add_argument('--enable_perspective_error_image_save', default=default('--enable_perspective_error_image_save'), choices=['yes', 'no'])

    adb = emulator_parser.add_argument_group('ADB settings', '')
    adb.add_argument('--use_adb_screenshot', default=default('--use_adb_screenshot'), choices=['yes', 'no'], help='It is recommended to enable it to reduce CPU usage')
    adb.add_argument('--use_adb_control', default=default('--use_adb_control'), choices=['yes', 'no'], help='Recommended off, can speed up the click speed')
    adb.add_argument('--combat_screenshot_interval', default=default('--combat_screenshot_interval'), help='Slow down the screenshot speed during battle and reduce CPU')

    # ==========每日任务==========
    daily_parser = subs.add_parser('daily')

    # 选择每日
    daily = daily_parser.add_argument_group('Choose daily', 'Daily tasks, exercises, difficulty charts')
    daily.add_argument('--enable_daily_mission', default=default('--enable_daily_mission'), help='If there are records on the day, skip', choices=['yes', 'no'])
    daily.add_argument('--enable_hard_campaign', default=default('--enable_hard_campaign'), help='If there are records on the day, skip', choices=['yes', 'no'])
    daily.add_argument('--enable_exercise', default=default('--enable_exercise'), help='If there is a record after refreshing, skip', choices=['yes', 'no'])

    # 每日设置
    daily_task = daily_parser.add_argument_group('Daily settings', 'Does not support submarine daily')
    daily_task.add_argument('--tactical_training', default=default('--tactical_training'), choices=['daily_air', 'daily_gun', 'daily_torpedo'])
    daily_task.add_argument('--fierce_assault', default=default('--fierce_assault'), choices=['index_1', 'index_2', 'index_3'])
    daily_task.add_argument('--escort_mission', default=default('--escort_mission'), choices=['index_1', 'index_2', 'index_3'])
    daily_task.add_argument('--advance_mission', default=default('--advance_mission'), choices=['index_1', 'index_2', 'index_3'])
    daily_task.add_argument('--daily_fleet', default=default('--daily_fleet'), choices=['1', '2', '3', '4', '5', '6'])
    daily_task.add_argument('--daily_equipment', default=default('--daily_equipment'), help='Change equipment before playing, unload equipment after playing, do not need to fill in 0 \ncomma, such as 3, 1, 0, 1, 1, 0')

    # 困难设置
    hard = daily_parser.add_argument_group('Difficult setting', 'Need to turn on weekly mode, only support 10-1, 10-2 and 10-4 temporarily')
    hard.add_argument('--hard_campaign', default=default('--hard_campaign'), help='For example 10-4')
    hard.add_argument('--hard_fleet', default=default('--hard_fleet'), choices=['1', '2'])
    hard.add_argument('--hard_equipment', default=default('--hard_equipment'), help='Change equipment before playing, unload equipment after playing, do not need to fill in 0 \ncomma, such as 3, 1, 0, 1, 1, 0')

    # 演习设置
    exercise = daily_parser.add_argument_group('Exercise settings', 'Only support the most experience for the time being')
    exercise.add_argument('--exercise_choose_mode', default=default('--exercise_choose_mode'), choices=['max_exp', 'max_ranking', 'good_opponent'], help='Only support the most experience for the time being')
    exercise.add_argument('--exercise_preserve', default=default('--exercise_preserve'), help='Only 0 are temporarily reserved')
    exercise.add_argument('--exercise_try', default=default('--exercise_try'), help='The number of attempts by each opponent')
    exercise.add_argument('--exercise_hp_threshold', default=default('--exercise_hp_threshold'), help='HHP <Retreat at Threshold')
    exercise.add_argument('--exercise_low_hp_confirm', default=default('--exercise_low_hp_confirm'), help='After HP is below the threshold, it will retreat after a certain period of time \nRecommended 1.0 ~ 3.0')
    exercise.add_argument('--exercise_equipment', default=default('--exercise_equipment'), help='Change equipment before playing, unload equipment after playing, do not need to fill in 0 \ncomma, such as 3, 1, 0, 1, 1, 0')

    # ==========event_daily_ab==========
    event_ab_parser = subs.add_parser('event_daily_bonus')
    event_name = event_ab_parser.add_argument_group('Choose an event', 'bonus for first clear each day')
    event_name.add_argument('--event_name_ab', default=event_latest, choices=event_folder, help='There a dropdown menu with many options')
    # event_name.add_argument('--enable_hard_bonus', default=default('--enable_hard_bonus'), choices=['yes', 'no'], help='Will enable Daily bonus for Event hard maps') # Trying implement all event maps

    # ==========main==========
    main_parser = subs.add_parser('Main_campaign')
    # 选择关卡
    stage = main_parser.add_argument_group('Choose a level', 'Main campaign, Currently, not all maps are being supported, check the folder /doc/development_en.md to know how add new maps')
    stage.add_argument('--main_stage', default=default('--main_stage'), help='E.g 7-2')

    # ==========event==========
    event_parser = subs.add_parser('event')

    description = """
    Support "Iris of Light and Dark Rerun" (event_20200521_en), optimized for D2
    """
    event = event_parser.add_argument_group(
        'Choose a level', '\n'.join([line.strip() for line in description.strip().split('\n')]))
    event.add_argument('--event_stage', default=default('--event_stage'),
                             choices=['a1', 'a2', 'a3', 'b1', 'b2', 'b3', 'c1', 'c2', 'c3', 'd1', 'd2', 'd3'],
                             help='E.g d3')
    event.add_argument('--sp_stage', default=default('--sp_stage'),
                             choices=['sp1', 'sp2', 'sp3'],
                             help='E.g sp3')
    event.add_argument('--event_name', default=event_latest, choices=event_folder, help='There a dropdown menu with many options')

    # ==========半自动==========
    semi_parser = subs.add_parser('semi_auto')
    semi = semi_parser.add_argument_group('Semi-automatic mode', 'Manual selection of enemies, automatic settlement, used to attack unsuited pictures')
    semi.add_argument('--enable_semi_map_preparation', default=default('--enable_semi_map_preparation'), help='', choices=['yes', 'no'])
    semi.add_argument('--enable_semi_story_skip', default=default('--enable_semi_story_skip'), help='Note that this will automatically confirm all the prompt boxes, including the red face attack', choices=['yes', 'no'])

    # ==========c72_mystery_farming==========
    c_7_2_parser = subs.add_parser('c7-2_mystery_farming')
    c_7_2 = c_7_2_parser.add_argument_group('c72_mystery_farming', '')
    c_7_2.add_argument('--boss_fleet_step_on_a3', default=default('--boss_fleet_step_on_a3'), choices=['yes', 'no'], help='A3 has enemies, G3, C3, E3')

    # ==========c122_leveling==========
    c_12_2_parser = subs.add_parser('c12-2_leveling')
    c_12_2 = c_12_2_parser.add_argument_group('12-2 enemy search settings', '')
    c_12_2.add_argument('--s3_enemy_tolerance', default=default('--s3_enemy_tolerance'), choices=['0', '1', '2', '10'], help='The maximum number of battles to fight against large enemies')

    # ==========c124_leveling==========
    c_12_4_parser = subs.add_parser('c12-4_leveling')
    c_12_4 = c_12_4_parser.add_argument_group('12-4 Search enemy settings', 'Need to ensure that the team has a certain strength')
    c_12_4.add_argument('--non_s3_enemy_enter_tolerance', default=default('--non_s3_enemy_enter_tolerance'), choices=['0', '1', '2'], help='Avoid enemy too strong')
    c_12_4.add_argument('--non_s3_enemy_withdraw_tolerance', default=default('--non_s3_enemy_withdraw_tolerance'), choices=['0', '1', '2', '10'], help='How many battles will be fought after there is no large scale')
    c_12_4.add_argument('--ammo_pick_up_124', default=default('--ammo_pick_up_124'), choices=['2', '3', '4', '5'], help='How much ammunition to pick after the battle')

    args = parser.parse_args()

    # Convert option from chinese to english.
    out = {}
    for key, value in vars(args).items():
        key = dic_true_eng_to_eng.get(key, key)
        value = dic_true_eng_to_eng.get(value, value)
        out[key] = value
    args = out

    # Update option to .ini file.
    command = args['command'].capitalize()
    config['Command']['command'] = command
    for key, value in args.items():
        config[command][key] = str(value)
    config.write(codecs.open(config_file, "w+", "utf8"))

    # Call AzurLaneAutoScript
    alas = AzurLaneAutoScript(ini_name=ini_name)
    alas.run(command=command)
예제 #11
0
 def show(self):
     for y in range(self.shape[1] + 1):
         text = ' '.join([self[(x, y)].str if (x, y) in self else '..' for x in range(self.shape[0] + 1)])
         logger.info(text)
예제 #12
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 = globals()[f'OCR_SHIPYARD_TOTAL_{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 = ocr.ocr(self.device.image)
                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.appear(SHIPYARD_UI_CHECK, offset=(20, 20)) or
                 self.appear(SHIPYARD_IN_FATE, offset=(20, 20))):
                if confirm_timer.reached():
                    break
            else:
                confirm_timer.reset()
예제 #13
0
    def _guild_logistics_mission_available(self):
        """
        Color sample the GUILD_MISSION area to determine
        whether the button is enabled, mission already
        in progress, or no more missions can be accepted

        Used at least twice, 'Collect' and 'Accept'

        Returns:
            bool: If button active

        Pages:
            in: GUILD_LOGISTICS
            out: GUILD_LOGISTICS
        """
        r, g, b = get_color(self.device.image, GUILD_MISSION.area)
        if g > max(r, b) - 10:
            # Green tick at the bottom right corner if guild mission finished
            logger.info('Guild mission has finished this week')
            self._guild_logistics_mission_finished = True
            return False
        # 0/300 in EN is bold and pure white, and Collect rewards is blue white, so reverse the if condition
        elif self.image_color_count(GUILD_MISSION,
                                    color=(255, 255, 255),
                                    threshold=235,
                                    count=100):

            logger.info('Guild mission button inactive')
            return False
        elif self.image_color_count(GUILD_MISSION,
                                    color=(255, 255, 255),
                                    threshold=180,
                                    count=50):
            # white pixels less than 50, but has blue-white pixels
            logger.info('Guild mission button active')
            return True
        else:
            # No guild mission counter
            logger.info(
                'No guild mission found, mission of this week may not started')
            if self.image_color_count(GUILD_MISSION_CHOOSE,
                                      color=(255, 255, 255),
                                      threshold=221,
                                      count=100):
                # Guild mission choose available if user is guild master
                logger.info('Guild mission choose found')
                return True
            else:
                logger.info('Guild mission choose not found')
                return False
예제 #14
0
    def _guild_exchange_check(options, item_priority,
                              grade_to_plate_priorities):
        """
        Sift through all exchangeable options
        Record details on each to determine
        selection order

        Pages:
            in: GUILD_LOGISTICS
            out: GUILD_LOGISTICS
        """
        # Contains the details of all options
        choices = dict()

        for i, (option, in_red) in enumerate(options):
            # Options already sorted sequentially
            # Button indexes are in sync
            btn = EXCHANGE_BUTTONS[i, 0]

            # Defaults set absurd values, which tells ALAS to skip option
            item_weight = len(DEFAULT_ITEM_PRIORITY)
            plate_weight = len(DEFAULT_PLATE_PRIORITY)
            can_exchange = False

            # Player lacks inventory of this item
            # so leave this choice under all defaults
            # to skip
            if not in_red:
                # Plate perhaps, extract last
                # 2 characters to ensure
                grade = option[-2:]
                if grade in GRADES:
                    item_weight = item_priority.index(grade)
                    can_exchange = True

                    plate_priority = grade_to_plate_priorities.get(grade)
                    plate_name = option[5:-2]
                    if plate_name in plate_priority:
                        plate_weight = plate_priority.index(plate_name)

                    # Did weight update?
                    # If not, then this choice given less priority
                    # also set to absurd cost to avoid using
                    if plate_weight == len(DEFAULT_PLATE_PRIORITY):
                        item_weight = len(DEFAULT_ITEM_PRIORITY)
                        can_exchange = False

                # Else normal item, check normally
                # Plates are skipped since only grade in priority
                if option in item_priority:
                    item_weight = item_priority.index(option)
                    can_exchange = True

            choices[f'{i + 1}'] = [
                item_weight, plate_weight, i + 1, can_exchange, btn
            ]
            logger.info(
                f'Choice #{i + 1} - Name: {option:15}, Weight: {item_weight:3}, Exchangeable: {can_exchange}'
            )

        return choices
예제 #15
0
    def _commission_choose(self, daily, urgent, priority, time_limit=None):
        """
        Args:
            daily (CommissionGroup):
            urgent (CommissionGroup):
            priority (dict):
            time_limit (datetime):

        Returns:
            CommissionGroup, CommissionGroup: Chosen daily commission, Chosen urgent commission
        """
        # Count Commission
        commission = daily.commission + urgent.commission
        running_count = int(
            np.sum([1 for c in commission if c.status == 'running']))
        logger.attr('Running', running_count)
        if running_count >= 4:
            return [], []

        # Calculate priority
        commission = [
            c for c in commission if c.valid and c.status == 'pending'
        ]
        comm_priority = []
        for comm in commission:
            pri = priority[comm.genre]
            if comm.duration <= timedelta(hours=2):
                pri += priority['duration_shorter_than_2']
            if comm.duration >= timedelta(hours=6):
                pri += priority['duration_longer_than_6']
            if comm.expire:
                if comm.expire <= timedelta(hours=2):
                    pri += priority['expire_shorter_than_2']
                if comm.expire >= timedelta(hours=6):
                    pri += priority['expire_longer_than_6']
            comm_priority.append(pri)

        # Sort
        commission = list(
            np.array(commission)[np.argsort(comm_priority)])[::-1]
        # Select priority > 0
        commission = [comm for comm in commission if priority[comm.genre] > 0]
        # Select within time_limit
        if time_limit:
            commission = [
                comm for comm in commission
                if datetime.now() + comm.duration <= time_limit
            ]

        commission = commission[:4 - running_count]
        daily_choose, urgent_choose = CommissionGroup(
            self.config), CommissionGroup(self.config)
        for comm in commission:
            if comm in daily:
                daily_choose.commission.append(comm)
            if comm in urgent:
                urgent_choose.commission.append(comm)

        if daily_choose:
            logger.info('Choose daily commission')
            for comm in daily_choose:
                logger.info(comm)
        if urgent_choose:
            logger.info('Choose urgent commission')
            for comm in urgent_choose:
                logger.info(comm)

        return daily_choose, urgent_choose
예제 #16
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])
예제 #17
0
 def next(self):
     self.daily_current += 1
     logger.info('Switch to %s' % str(self.daily_current))
     self.device.click(DAILY_NEXT)
     self._wait_daily_switch()
     self.device.screenshot()
 def handle_camera_outside_map(self, e):
     msg = str(e).split(':')[1].strip()
     logger.info(f'Camera outside map: {msg}')
     dic = {'to the left': (2, 0), 'to the right': (-2, 0), 'to the lower': (0, 2), 'to the upper': (0, -2)}
     self._map_swipe(dic[msg])
예제 #19
0
 def daily_check(self, n=None):
     if not n:
         n = self.daily_current
     self.daily_checked.append(n)
     logger.info(f'Checked daily {n}')
     logger.info(f'Checked_list: {self.daily_checked}')
예제 #20
0
    def clear_potential_boss(self):
        """
        Method to step on all boss spawn point when boss not detected.
        """
        grids = self.map.select(may_boss=True, is_accessible=True)
        logger.info('May boss: %s' % grids)
        battle_count = self.battle_count

        for grid in grids:
            logger.hr('Clear potential BOSS')
            grids = grids.sort('weight', 'cost')
            logger.info('Grid: %s' % str(grid))
            self.fleet_boss.clear_chosen_enemy(grid)
            if self.battle_count > battle_count:
                logger.info('Boss guessing correct.')
                return True
            else:
                logger.info('Boss guessing incorrect.')

        grids = self.map.select(may_boss=True, is_accessible=False)
        logger.info('May boss: %s' % grids)

        for grid in grids:
            logger.hr('Clear potential BOSS roadblocks')
            fleet = 2 if self.config.FLEET_BOSS == 2 and self.config.FLEET_2 else 1
            roadblocks = self.brute_find_roadblocks(grid, fleet=fleet)
            roadblocks = roadblocks.sort('weight', 'cost')
            logger.info('Grids: %s' % str(roadblocks))
            self.fleet_1.clear_chosen_enemy(roadblocks[0])
            return True

        return False
예제 #21
0
    def _commission_start_click(self, comm, is_urgent=False):
        """
        Start a commission.

        Args:
            comm (Commission):
            is_urgent (bool):

        Returns:
            bool: If success

        Pages:
            in: page_commission
            out: page_commission, info_bar, commission details unfold
        """
        logger.hr('Commission start')
        self.interval_clear(COMMISSION_ADVICE)
        self.interval_clear(COMMISSION_START)
        comm_timer = Timer(7)
        count = 0
        while 1:
            if comm_timer.reached():
                self.device.click(comm.button)
                self.device.sleep(0.3)
                comm_timer.reset()

            if self.handle_popup_confirm('COMMISSION_START'):
                comm_timer.reset()
                pass
            if self.appear(COMMISSION_ADVICE, offset=(5, 20), interval=7):
                area = (0, 0, image_size(self.device.image)[0],
                        COMMISSION_ADVICE.button[1])
                current = self.commission_detect(area=area)
                if is_urgent:
                    current.call('convert_to_night'
                                 )  # Convert extra commission to night
                if current.count >= 1:
                    current = current[0]
                    if current == comm:
                        logger.info('Selected to the correct commission')
                    else:
                        logger.warning('Selected to the wrong commission')
                        return False
                else:
                    logger.warning(
                        'No selected commission detected, assuming correct')
                self.device.click(COMMISSION_ADVICE)
                count += 1
                comm_timer.reset()
                pass
            if self.appear_then_click(COMMISSION_START,
                                      offset=(5, 20),
                                      interval=7):
                comm_timer.reset()
                pass

            # End
            if self.info_bar_count():
                break
            if count >= 3:
                # Restart game and handle commission recommend bug.
                # After you click "Recommend", your ships appear and then suddenly disappear.
                # At the same time, the icon of commission is flashing.
                logger.warning('Triggered commission list flashing bug')
                raise GameStuckError('Triggered commission list flashing bug')

            self.device.screenshot()

        return True
예제 #22
0
    def fleet_2_push_forward(self):
        """Move fleet 2 to the grid with lower grid.weight
        This will reduce the possibility of Boss fleet get stuck by enemies, especially for those one-way-road map
        from chapter 7 to chapter 9.

        Know more (in Chinese simplified):
        9章道中战最小化路线规划 (Route Planning for battle minimization in chapter 9)
        https://wiki.biligame.com/blhx/9%E7%AB%A0%E9%81%93%E4%B8%AD%E6%88%98%E6%9C%80%E5%B0%8F%E5%8C%96%E8%B7%AF%E7%BA%BF%E8%A7%84%E5%88%92

        Returns:
            bool: If pushed forward.
        """
        if not self.config.FLEET_2:
            return False

        logger.info('Fleet_2 push forward')
        grids = self.map.select(is_land=False).sort('weight', 'cost')
        if self.map[self.fleet_2_location].weight <= grids[0].weight:
            logger.info('Fleet_2 pushed to destination')
            return False

        fleets = SelectedGrids(
            [self.map[self.fleet_1_location], self.map[self.fleet_2_location]])
        grids = grids.select(is_accessible_2=True, is_sea=True).delete(fleets)
        if not grids:
            logger.info('Fleet_2 has no where to push')
            return False
        if self.map[self.fleet_2_location].weight <= grids[0].weight:
            logger.info('Fleet_2 pushed to closest grid')
            return False

        logger.info(f'Grids: {grids}')
        logger.info(f'Push forward: {grids[0]}')
        self.fleet_2.goto(grids[0])
        self.fleet_1.switch_to()
        return True
예제 #23
0
    def update(self, camera=True):
        """Update map image

        Args:
            camera: True to update camera position and perspective data.
        """
        self.device.screenshot()
        if not camera:
            self.view.update(image=self.device.image)
            return True

        self._view_init()
        try:
            self.view.load(self.device.image)
        except (MapDetectionError, AttributeError) as e:
            if self.info_bar_count():
                logger.info('Perspective error cause by info bar. Waiting.')
                self.handle_info_bar()
                return self.update(camera=camera)
            elif self.appear(GET_ITEMS_1):
                logger.warning('Items got. Trying handling mystery.')
                self.handle_mystery()
                return self.update(camera=camera)
            elif self.is_in_stage():
                logger.warning('Image is in stage')
                raise CampaignEnd('Image is in stage')
            elif not self.appear(IN_MAP):
                logger.warning('Image to detect is not in_map')
                if self.appear_then_click(GAME_TIPS, offset=(20, 20)):
                    logger.warning('Game tips found, retrying')
                    self.device.screenshot()
                    self.view.load(self.device.image)
                else:
                    raise e
            elif 'Camera outside map' in str(e):
                string = str(e)
                logger.warning(string)
                x, y = string.split('=')[1].strip('() ').split(',')
                self._map_swipe((-int(x.strip()), -int(y.strip())))
            else:
                raise e

        if self._prev_view is not None and np.linalg.norm(
                self._prev_swipe) > 0:
            if self.config.MAP_SWIPE_PREDICT:
                swipe = self._prev_view.predict_swipe(self.view)
                if swipe is not None:
                    self._prev_swipe = swipe
            self.camera = tuple(np.add(self.camera, self._prev_swipe))
            self._prev_view = None
            self._prev_swipe = None
            self.show_camera()

        if not self._correct_camera:
            self.show_camera()
            return False
        # Set camera position
        if self.view.left_edge:
            x = 0 + self.view.center_loca[0]
        elif self.view.right_edge:
            x = self.map.shape[0] - self.view.shape[0] + self.view.center_loca[
                0]
        else:
            x = self.camera[0]
        if self.view.lower_edge:
            y = 0 + self.view.center_loca[1]
        elif self.view.upper_edge:
            y = self.map.shape[1] - self.view.shape[1] + self.view.center_loca[
                1]
        else:
            y = self.camera[1]

        if self.camera != (x, y):
            logger.attr_align(
                'camera_corrected',
                f'{location2node(self.camera)} -> {location2node((x, y))}')
        self.camera = (x, y)
        self.show_camera()
예제 #24
0
def main(ini_name=''):
    if not ini_name:
        ini_name = pyw_name
    ini_name = ini_name.lower()

    # Load default value from .ini file.
    config_file = f'./config/{ini_name}.ini'
    config = configparser.ConfigParser(interpolation=None)
    try:
        config.read_file(codecs.open(config_file, "r", "utf8"))
    except FileNotFoundError:
        logger.info('Config file not exists, copy from ./config/template.ini')
        shutil.copy('./config/template.ini', config_file)
        config.read_file(codecs.open(config_file, "r", "utf8"))

    config = update_config_from_template(config, file=config_file)

    event_folder = [
        dic_eng_to_chi.get(f, f) for f in os.listdir('./campaign')
        if f.startswith('event_')
    ][::-1]

    saved_config = {}
    for opt, option in config.items():
        for key, value in option.items():
            key = dic_eng_to_chi.get(key, key)
            if value in dic_eng_to_chi:
                value = dic_eng_to_chi.get(value, value)
            if value == 'None':
                value = ''

            saved_config[key] = value

    def default(name):
        """Get default value in .ini file.
        Args:
            name (str): option, in chinese.

        Returns:
            str: Default value, in chinese.
        """
        name = name.strip('-')
        return saved_config.get(name, '')

    def choice_list(total):
        return [str(index) for index in range(1, total + 1)]

    # Don't use checkbox in gooey, use drop box instead.
    # https://github.com/chriskiehl/Gooey/issues/148
    # https://github.com/chriskiehl/Gooey/issues/485

    parser = GooeyParser(
        description=
        f'AzurLaneAutoScript, An Azur Lane automation tool. Config: {config_file}'
    )
    subs = parser.add_subparsers(help='commands', dest='command')

    # ==========出击设置==========
    setting_parser = subs.add_parser('出击设置')

    # 选择关卡
    stage = setting_parser.add_argument_group('关卡设置', '需要运行一次来保存选项')
    stage.add_argument('--启用停止条件',
                       default=default('--启用停止条件'),
                       choices=['是', '否'])
    stage.add_argument('--使用周回模式',
                       default=default('--使用周回模式'),
                       choices=['是', '否'])

    stop = stage.add_argument_group('停止条件', '触发后不会马上停止会先完成当前出击, 不需要就填0')
    stop.add_argument('--如果出击次数大于',
                      default=default('--如果出击次数大于'),
                      help='会沿用先前设置, 完成出击将扣除次数, 直至清零')
    stop.add_argument(
        '--如果时间超过',
        default=default('--如果时间超过'),
        help='使用未来24小时内的时间, 会沿用先前设置, 触发后清零. 建议提前10分钟左右, 以完成当前出击. 格式 14:59')
    stop.add_argument('--如果石油低于', default=default('--如果石油低于'))
    stop.add_argument('--如果触发心情控制',
                      default=default('--如果触发心情控制'),
                      choices=['是', '否'],
                      help='若是, 等待回复, 完成本次, 停止\n若否, 等待回复, 完成本次, 继续')
    stop.add_argument('--如果船舱已满',
                      default=default('--如果船舱已满'),
                      choices=['是', '否'])

    # 出击舰队
    fleet = setting_parser.add_argument_group('出击舰队',
                                              '暂不支持备用道中队, 非活动图或周回模式会忽略步长设置')
    fleet.add_argument('--启用舰队控制',
                       default=default('--启用舰队控制'),
                       choices=['是', '否'])
    fleet.add_argument('--启用阵容锁定',
                       default=default('--启用阵容锁定'),
                       choices=['是', '否'])

    f1 = fleet.add_argument_group('道中队')
    f1.add_argument('--舰队编号1',
                    default=default('--舰队编号1'),
                    choices=['1', '2', '3', '4', '5', '6'])
    f1.add_argument('--舰队阵型1',
                    default=default('--舰队阵型1'),
                    choices=['单纵阵', '复纵阵', '轮形阵'])
    f1.add_argument('--舰队步长1',
                    default=default('--舰队步长1'),
                    choices=['1', '2', '3', '4', '5', '6'])

    f2 = fleet.add_argument_group('BOSS队')
    f2.add_argument('--舰队编号2',
                    default=default('--舰队编号2'),
                    choices=['不使用', '1', '2', '3', '4', '5', '6'])
    f2.add_argument('--舰队阵型2',
                    default=default('--舰队阵型2'),
                    choices=['单纵阵', '复纵阵', '轮形阵'])
    f2.add_argument('--舰队步长2',
                    default=default('--舰队步长2'),
                    choices=['1', '2', '3', '4', '5', '6'])

    f3 = fleet.add_argument_group('备用道中队')
    f3.add_argument('--舰队编号3',
                    default=default('--舰队编号3'),
                    choices=['不使用', '1', '2', '3', '4', '5', '6'])
    f3.add_argument('--舰队阵型3',
                    default=default('--舰队阵型3'),
                    choices=['单纵阵', '复纵阵', '轮形阵'])
    f3.add_argument('--舰队步长3',
                    default=default('--舰队步长3'),
                    choices=['1', '2', '3', '4', '5', '6'])

    f4 = fleet.add_argument_group('自律模式')
    f4.add_argument('--战斗自律模式',
                    default=default('--战斗自律模式'),
                    choices=['自律', '手操', '中路站桩'])

    # 潜艇设置
    submarine = setting_parser.add_argument_group('潜艇设置',
                                                  '仅支持: 不使用, 仅狩猎, 每战出击')
    submarine.add_argument('--舰队编号4',
                           default=default('--舰队编号4'),
                           choices=['不使用', '1', '2'])
    submarine.add_argument(
        '--潜艇出击方案',
        default=default('--潜艇出击方案'),
        choices=['不使用', '仅狩猎', '每战出击', '空弹出击', 'BOSS战出击', 'BOSS战BOSS出现后召唤'])

    # 心情控制
    emotion = setting_parser.add_argument_group('心情控制')
    emotion.add_argument('--启用心情消耗',
                         default=default('--启用心情消耗'),
                         choices=['是', '否'])
    emotion.add_argument('--无视红脸出击警告',
                         default=default('--无视红脸出击警告'),
                         choices=['是', '否'])

    e1 = emotion.add_argument_group('道中队')
    e1.add_argument('--心情回复1',
                    default=default('--心情回复1'),
                    choices=['未放置于后宅', '后宅一楼', '后宅二楼'])
    e1.add_argument('--心情控制1',
                    default=default('--心情控制1'),
                    choices=['保持经验加成', '防止绿脸', '防止黄脸', '防止红脸'])
    e1.add_argument('--全员已婚1', default=default('--全员已婚1'), choices=['是', '否'])

    e2 = emotion.add_argument_group('BOSS队')
    e2.add_argument('--心情回复2',
                    default=default('--心情回复2'),
                    choices=['未放置于后宅', '后宅一楼', '后宅二楼'])
    e2.add_argument('--心情控制2',
                    default=default('--心情控制2'),
                    choices=['保持经验加成', '防止绿脸', '防止黄脸', '防止红脸'])
    e2.add_argument('--全员已婚2', default=default('--全员已婚2'), choices=['是', '否'])

    e3 = emotion.add_argument_group('备用道中队', '会在主队触发心情控制时使用')
    e3.add_argument('--心情回复3',
                    default=default('--心情回复3'),
                    choices=['未放置于后宅', '后宅一楼', '后宅二楼'])
    e3.add_argument('--心情控制3',
                    default=default('--心情控制3'),
                    choices=['保持经验加成', '防止绿脸', '防止黄脸', '防止红脸'])
    e3.add_argument('--全员已婚3', default=default('--全员已婚3'), choices=['是', '否'])

    # 血量平衡
    hp = setting_parser.add_argument_group('血量控制', '需关闭舰队锁定才能生效')
    hp.add_argument('--启用血量平衡',
                    default=default('--启用血量平衡'),
                    choices=['是', '否'])
    hp.add_argument('--启用低血量撤退',
                    default=default('--启用低血量撤退'),
                    choices=['是', '否'])
    hp_balance = hp.add_argument_group('血量平衡', '')
    hp_balance.add_argument('--先锋血量平衡阈值',
                            default=default('--先锋血量平衡阈值'),
                            help='血量差值大于阈值时, 换位')
    hp_balance.add_argument('--先锋血量权重',
                            default=default('--先锋血量权重'),
                            help='先锋肉度有差别时应修改, 格式 1000,1000,1000')
    hp_add = hp.add_argument_group('紧急维修', '')
    hp_add.add_argument('--紧急维修单人阈值',
                        default=default('--紧急维修单人阈值'),
                        help='单人低于阈值时使用')
    hp_add.add_argument('--紧急维修全队阈值',
                        default=default('--紧急维修全队阈值'),
                        help='前排全部或后排全部低于阈值时使用')
    hp_withdraw = hp.add_argument_group('低血量撤退', '')
    hp_withdraw.add_argument('--低血量撤退阈值',
                             default=default('--低血量撤退阈值'),
                             help='任意一人血量低于阈值时, 撤退')

    # 退役选项
    retire = setting_parser.add_argument_group('退役设置', '')
    retire.add_argument('--启用退役',
                        default=default('--启用退役'),
                        choices=['是', '否'])
    retire.add_argument('--使用一键退役',
                        default=default('--使用一键退役'),
                        choices=['是', '否'])
    retire.add_argument('--退役方案',
                        default=default('--退役方案'),
                        choices=['退役全部', '退役10个'])

    rarity = retire.add_argument_group('退役稀有度', '暂不支持舰种选择, 使用一键退役时忽略以下选项')
    rarity.add_argument('--退役白皮',
                        default=default('--退役白皮'),
                        choices=['是', '否'],
                        help='N')
    rarity.add_argument('--退役蓝皮',
                        default=default('--退役蓝皮'),
                        choices=['是', '否'],
                        help='R')
    rarity.add_argument('--退役紫皮',
                        default=default('--退役紫皮'),
                        choices=['是', '否'],
                        help='SR')
    rarity.add_argument('--退役金皮',
                        default=default('--退役金皮'),
                        choices=['是', '否'],
                        help='SSR')

    # 掉落记录
    drop = setting_parser.add_argument_group('掉落记录',
                                             '保存掉落物品的截图, 启用后会放缓结算时的点击速度')
    drop.add_argument('--启用掉落记录',
                      default=default('--启用掉落记录'),
                      choices=['是', '否'])
    drop.add_argument('--掉落保存目录', default=default('--掉落保存目录'))

    clear = setting_parser.add_argument_group(
        '开荒模式', '未开荒地图会在完成后停止, 已开荒的地图会忽略选项, 无脑开就完事了')
    clear.add_argument('--启用开荒', default=default('--启用开荒'), choices=['是', '否'])
    clear.add_argument('--开荒停止条件',
                       default=default('--开荒停止条件'),
                       choices=['地图通关', '地图三星', '地图绿海'])
    clear.add_argument('--地图全清星星',
                       default=default('--地图全清星星'),
                       choices=['第一个', '第二个', '第三个', '不使用'],
                       help='第几颗星星是击破所有敌舰')

    # ==========收菜设置==========
    reward_parser = subs.add_parser('收菜设置')
    reward_condition = reward_parser.add_argument_group(
        '触发条件', '需要运行一次来保存选项, 运行后会进入挂机收菜模式')
    reward_condition.add_argument('--启用收获',
                                  default=default('--启用收获'),
                                  choices=['是', '否'])
    reward_condition.add_argument('--收菜间隔',
                                  default=default('--收菜间隔'),
                                  choices=['20', '30', '60'],
                                  help='每隔多少分钟触发收菜')

    reward_oil = reward_parser.add_argument_group('石油物资', '')
    reward_oil.add_argument('--启用石油收获',
                            default=default('--启用石油收获'),
                            choices=['是', '否'])
    reward_oil.add_argument('--启用物资收获',
                            default=default('--启用物资收获'),
                            choices=['是', '否'])

    reward_mission = reward_parser.add_argument_group('任务奖励', '')
    reward_mission.add_argument('--启用任务收获',
                                default=default('--启用任务收获'),
                                choices=['是', '否'])

    reward_commission = reward_parser.add_argument_group('委托设置', '')
    reward_commission.add_argument('--启用委托收获',
                                   default=default('--启用委托收获'),
                                   choices=['是', '否'])
    reward_commission.add_argument('--委托时间限制',
                                   default=default('--委托时间限制'),
                                   help='忽略完成时间超过限制的委托, 格式: 23:30')

    priority1 = reward_commission.add_argument_group('委托耗时优先级', '')
    priority1.add_argument('--委托耗时小于2h',
                           default=default('--委托耗时小于2h'),
                           help='')
    priority1.add_argument('--委托耗时超过6h',
                           default=default('--委托耗时超过6h'),
                           help='')
    priority1.add_argument('--委托过期小于2h',
                           default=default('--委托过期小于2h'),
                           help='')
    priority1.add_argument('--委托过期大于6h',
                           default=default('--委托过期大于6h'),
                           help='')

    priority2 = reward_commission.add_argument_group('日常委托优先级', '')
    priority2.add_argument('--日常委托',
                           default=default('--日常委托'),
                           help='日常资源开发, 高阶战术研发')
    priority2.add_argument('--主要委托',
                           default=default('--主要委托'),
                           help='1200油/1000油委托')

    priority3 = reward_commission.add_argument_group('额外委托优先级', '')
    priority3.add_argument('--钻头类额外委托',
                           default=default('--钻头类额外委托'),
                           help='短距离航行训练, 近海防卫巡逻')
    priority3.add_argument('--部件类额外委托',
                           default=default('--部件类额外委托'),
                           help='矿脉护卫委托, 林木护卫委托')
    priority3.add_argument('--魔方类额外委托',
                           default=default('--魔方类额外委托'),
                           help='舰队高阶演习, 舰队护卫演习')
    priority3.add_argument('--石油类额外委托',
                           default=default('--石油类额外委托'),
                           help='小型油田开发, 大型油田开发')
    priority3.add_argument('--教材类额外委托',
                           default=default('--教材类额外委托'),
                           help='小型商船护卫, 大型商船护卫')

    priority4 = reward_commission.add_argument_group('紧急委托优先级', '')
    priority4.add_argument('--钻头类紧急委托',
                           default=default('--钻头类紧急委托'),
                           help='保卫运输部队, 歼灭敌精锐部队')
    priority4.add_argument('--部件类紧急委托',
                           default=default('--部件类紧急委托'),
                           help='支援维拉维拉岛, 支援恐班纳')
    priority4.add_argument('--魔方类紧急委托',
                           default=default('--魔方类紧急委托'),
                           help='解救商船, 敌袭')
    priority4.add_argument('--教材类紧急委托',
                           default=default('--教材类紧急委托'),
                           help='支援土豪尔岛, 支援萌岛')
    priority4.add_argument('--装备类紧急委托',
                           default=default('--装备类紧急委托'),
                           help='BIW装备运输, NYB装备研发')
    priority4.add_argument('--钻石类紧急委托',
                           default=default('--钻石类紧急委托'),
                           help='BIW要员护卫, NYB巡视护卫')
    priority4.add_argument('--观舰类紧急委托',
                           default=default('--观舰类紧急委托'),
                           help='小型观舰仪式, 同盟观舰仪式')

    reward_tactical = reward_parser.add_argument_group('战术学院',
                                                       '只支持续技能书, 不支持学新技能')
    reward_tactical.add_argument('--启用战术学院收获',
                                 default=default('--启用战术学院收获'),
                                 choices=['是', '否'])
    reward_tactical.add_argument('--战术学院夜间时段',
                                 default=default('--战术学院夜间时段'),
                                 help='格式 23:30-06:30')
    reward_tactical.add_argument('--技能书稀有度',
                                 default=default('--技能书稀有度'),
                                 choices=['3', '2', '1'],
                                 help='最多使用T几的技能书\nT3是金书, T2是紫书, T1是蓝书')
    reward_tactical.add_argument('--技能书优先使用同类型',
                                 default=default('--技能书优先使用同类型'),
                                 choices=['是', '否'],
                                 help='选是, 优先使用有150%加成的\n选否, 优先使用同稀有度的技能书')
    reward_tactical.add_argument('--技能书夜间稀有度',
                                 default=default('--技能书夜间稀有度'),
                                 choices=['3', '2', '1'])
    reward_tactical.add_argument('--技能书夜间优先使用同类型',
                                 default=default('--技能书夜间优先使用同类型'),
                                 choices=['是', '否'])

    # ==========设备设置==========
    emulator_parser = subs.add_parser('设备设置')
    emulator = emulator_parser.add_argument_group(
        '模拟器', '需要运行一次来保存选项, 会检查游戏是否启动\n若启动了游戏, 触发一次收菜')
    emulator.add_argument('--设备',
                          default=default('--设备'),
                          help='例如 127.0.0.1:62001')
    emulator.add_argument('--包名', default=default('--包名'), help='')

    debug = emulator_parser.add_argument_group('调试设置', '')
    debug.add_argument('--出错时保存log和截图',
                       default=default('--出错时保存log和截图'),
                       choices=['是', '否'])
    debug.add_argument('--保存透视识别出错的图像',
                       default=default('--保存透视识别出错的图像'),
                       choices=['是', '否'])

    adb = emulator_parser.add_argument_group('ADB设置', '')
    adb.add_argument('--使用ADB截图',
                     default=default('--使用ADB截图'),
                     choices=['是', '否'],
                     help='建议开启, 能减少CPU占用')
    adb.add_argument('--使用ADB点击',
                     default=default('--使用ADB点击'),
                     choices=['是', '否'],
                     help='建议关闭, 能加快点击速度')
    adb.add_argument('--战斗中截图间隔',
                     default=default('--战斗中截图间隔'),
                     help='战斗中放慢截图速度, 降低CPU使用')

    # ==========每日任务==========
    daily_parser = subs.add_parser('每日任务困难演习')

    # 选择每日
    daily = daily_parser.add_argument_group('选择每日', '每日任务, 演习, 困难图')
    daily.add_argument('--打每日',
                       default=default('--打每日'),
                       help='若当天有记录, 则跳过',
                       choices=['是', '否'])
    daily.add_argument('--打困难',
                       default=default('--打困难'),
                       help='若当天有记录, 则跳过',
                       choices=['是', '否'])
    daily.add_argument('--打演习',
                       default=default('--打演习'),
                       help='若在刷新后有记录, 则跳过',
                       choices=['是', '否'])

    # 每日设置
    daily_task = daily_parser.add_argument_group('每日设置', '不支持潜艇每日')
    daily_task.add_argument('--战术研修',
                            default=default('--战术研修'),
                            choices=['航空', '炮击', '雷击'])
    daily_task.add_argument('--斩首行动',
                            default=default('--斩首行动'),
                            choices=['第一个', '第二个', '第三个'])
    daily_task.add_argument('--商船护航',
                            default=default('--商船护航'),
                            choices=['第一个', '第二个', '第三个'])
    daily_task.add_argument('--海域突进',
                            default=default('--海域突进'),
                            choices=['第一个', '第二个', '第三个'])
    daily_task.add_argument('--每日舰队',
                            default=default('--每日舰队'),
                            choices=['1', '2', '3', '4', '5', '6'])
    daily_task.add_argument(
        '--每日舰队快速换装',
        default=default('--每日舰队快速换装'),
        help='打之前换装备, 打完后卸装备, 不需要就填0\n逗号分割, 例如 3, 1, 0, 1, 1, 0')

    # 困难设置
    hard = daily_parser.add_argument_group('困难设置', '需要开启周回模式, 暂时仅支持 10-4')
    hard.add_argument('--困难地图', default=default('--困难地图'), help='比如 10-4')
    hard.add_argument('--困难舰队', default=default('--困难舰队'), choices=['1', '2'])
    hard.add_argument('--困难舰队快速换装',
                      default=default('--困难舰队快速换装'),
                      help='打之前换装备, 打完后卸装备, 不需要就填0\n逗号分割, 例如 3, 1, 0, 1, 1, 0')

    # 演习设置
    exercise = daily_parser.add_argument_group('演习设置', '暂时仅支持经验最多')
    exercise.add_argument('--演习对手选择',
                          default=default('--演习对手选择'),
                          choices=['经验最多', '排名最前', '福利队'],
                          help='暂时仅支持经验最多')
    exercise.add_argument('--演习次数保留',
                          default=default('--演习次数保留'),
                          help='暂时仅支持保留0个')
    exercise.add_argument('--演习尝试次数',
                          default=default('--演习尝试次数'),
                          help='每个对手的尝试次数, 打不过就换')
    exercise.add_argument('--演习SL阈值',
                          default=default('--演习SL阈值'),
                          help='HP<阈值时撤退')
    exercise.add_argument('--演习低血量确认时长',
                          default=default('--演习低血量确认时长'),
                          help='HP低于阈值后, 过一定时长才会撤退\n推荐 1.0 ~ 3.0')
    exercise.add_argument(
        '--演习快速换装',
        default=default('--演习快速换装'),
        help='打之前换装备, 打完后卸装备, 不需要就填0\n逗号分割, 例如 3, 1, 0, 1, 1, 0')

    # ==========每日活动图三倍PT==========
    event_ab_parser = subs.add_parser('每日活动图三倍PT')
    event_name = event_ab_parser.add_argument_group('选择活动', '')
    event_name.add_argument('--活动名称ab',
                            default=default('--活动名称ab'),
                            choices=event_folder,
                            help='例如 event_20200326_cn')

    # ==========主线图==========
    main_parser = subs.add_parser('主线图')
    # 选择关卡
    stage = main_parser.add_argument_group('选择关卡', '主线图出击, 目前仅支持前六章和7-2')
    stage.add_argument('--主线地图出击', default=default('--主线地图出击'), help='例如 7-2')

    # ==========活动图==========
    event_parser = subs.add_parser('活动图')

    description = """
    支持「穹顶下的圣咏曲」(event_20200521_cn), 针对D1D3有优化
    D3第一次进图和100%通关时均有剧情战斗, 会导致报错
    出击未优化关卡或地图未达到安全海域时, 使用开荒模式运行(较慢)
    """
    event = event_parser.add_argument_group(
        '选择关卡',
        '\n'.join([line.strip() for line in description.strip().split('\n')]))
    event.add_argument('--活动地图',
                       default=default('--活动地图'),
                       choices=[
                           'a1', 'a2', 'a3', 'b1', 'b2', 'b3', 'c1', 'c2',
                           'c3', 'd1', 'd2', 'd3'
                       ],
                       help='例如 d3')
    event.add_argument('--sp地图',
                       default=default('--sp地图'),
                       choices=['sp1', 'sp2', 'sp3'],
                       help='例如 sp3')
    event.add_argument('--活动名称',
                       default=default('--活动名称'),
                       choices=event_folder,
                       help='例如 event_20200312_cn')

    # ==========半自动==========
    semi_parser = subs.add_parser('半自动辅助点击')
    semi = semi_parser.add_argument_group('半自动模式', '手动选敌, 自动结算, 用于出击未适配的图')
    semi.add_argument('--进图准备',
                      default=default('--进图准备'),
                      help='',
                      choices=['是', '否'])
    semi.add_argument('--跳过剧情',
                      default=default('--跳过剧情'),
                      help='注意, 这会自动确认所有提示框, 包括红脸出击',
                      choices=['是', '否'])

    # ==========7-2三战拣垃圾==========
    c_7_2_parser = subs.add_parser('7-2三战拣垃圾')
    c_7_2 = c_7_2_parser.add_argument_group('7-2三战拣垃圾', '')
    c_7_2.add_argument('--BOSS队踩A3',
                       default=default('--BOSS队踩A3'),
                       choices=['是', '否'],
                       help='A3有敌人就G3, C3, E3')

    # ==========12-2打中型练级==========
    c_12_2_parser = subs.add_parser('12-2打中型练级')
    c_12_2 = c_12_2_parser.add_argument_group('12-2索敌设置', '')
    c_12_2.add_argument('--大型敌人忍耐',
                        default=default('--大型敌人忍耐'),
                        choices=['0', '1', '2', '10'],
                        help='最多打多少战大型敌人, 不挑敌人选10')

    # ==========12-4打大型练级==========
    c_12_4_parser = subs.add_parser('12-4打大型练级')
    c_12_4 = c_12_4_parser.add_argument_group('12-4索敌设置', '需保证队伍有一定强度')
    c_12_4.add_argument('--非大型敌人进图忍耐',
                        default=default('--非大型敌人进图忍耐'),
                        choices=['0', '1', '2'],
                        help='忍受进场多少战没有大型')
    c_12_4.add_argument('--非大型敌人撤退忍耐',
                        default=default('--非大型敌人撤退忍耐'),
                        choices=['0', '1', '2', '10'],
                        help='没有大型之后还会打多少战, 不挑敌人选10')
    c_12_4.add_argument('--拣弹药124',
                        default=default('--拣弹药124'),
                        choices=['2', '3', '4', '5'],
                        help='多少战后拣弹药')

    args = parser.parse_args()

    # Convert option from chinese to english.
    out = {}
    for key, value in vars(args).items():
        key = dic_chi_to_eng.get(key, key)
        value = dic_chi_to_eng.get(value, value)
        out[key] = value
    args = out

    # Update option to .ini file.
    command = args['command'].capitalize()
    config['Command']['command'] = command
    for key, value in args.items():
        config[command][key] = str(value)
    config.write(codecs.open(config_file, "w+", "utf8"))

    # Call AzurLaneAutoScript
    alas = AzurLaneAutoScript(ini_name=ini_name)
    alas.run(command=command)
예제 #25
0
    def fleet_preparation(self):
        """Change fleets.

        Returns:
            bool: True if changed.
        """
        logger.info(
            f'Using fleet: {[self.config.Fleet_Fleet1, self.config.Fleet_Fleet2, self.config.Submarine_Fleet]}'
        )
        if self.map_fleet_checked:
            return False

        self.map_is_hard_mode = self.get_map_is_hard_mode()
        if self.map_is_hard_mode:
            logger.info('Hard Campaign. No fleet preparation')
            return False

        fleet_1 = FleetOperator(choose=FLEET_1_CHOOSE,
                                bar=FLEET_1_BAR,
                                clear=FLEET_1_CLEAR,
                                in_use=FLEET_1_IN_USE,
                                main=self)
        fleet_2 = FleetOperator(choose=FLEET_2_CHOOSE,
                                bar=FLEET_2_BAR,
                                clear=FLEET_2_CLEAR,
                                in_use=FLEET_2_IN_USE,
                                main=self)
        submarine = FleetOperator(choose=SUBMARINE_CHOOSE,
                                  bar=SUBMARINE_BAR,
                                  clear=SUBMARINE_CLEAR,
                                  in_use=SUBMARINE_IN_USE,
                                  main=self)

        # Submarine.
        if submarine.allow():
            if self.config.Submarine_Fleet:
                submarine.ensure_to_be(self.config.Submarine_Fleet)
            else:
                submarine.clear()

        # No need, this may clear FLEET_2 by mistake, clear FLEET_2 in map config.
        # if not fleet_2.allow():
        #     self.config.FLEET_2 = 0

        if self.config.Fleet_Fleet2:
            # Using both fleets.
            # Force to set it again.
            # Fleets may reversed, because AL no longer treat the fleet with smaller index as first fleet
            fleet_2.clear()
            fleet_1.ensure_to_be(self.config.Fleet_Fleet1)
            fleet_2.ensure_to_be(self.config.Fleet_Fleet2)
        else:
            # Not using fleet 2.
            if fleet_2.allow():
                fleet_2.clear()
            fleet_1.ensure_to_be(self.config.Fleet_Fleet1)

        # Check if submarine is empty again.
        if submarine.allow():
            if self.config.Submarine_Fleet:
                pass
            else:
                submarine.clear()

        return True
예제 #26
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
예제 #27
0
    def research_receive(self, skip_first_screenshot=True, save_get_items=False):
        logger.info('Research receive')
        executed = False

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

            if self.appear(RESEARCH_CHECK, interval=10):
                if self._research_has_finished_at(RESEARCH_STATUS[self._research_finished_index]):
                    if save_get_items:
                        self.device.save_screenshot('research_project', interval=0, to_base_folder=True)
                    self.device.click(RESEARCH_ENTRANCE[self._research_finished_index])
                    continue

            if self.appear(GET_ITEMS_1, interval=5):
                self.wait_until_get_items_stable()
                logger.info('Get items 1 stabled')
                if not self.appear(GET_ITEMS_1, interval=0):
                    continue
                self.device.sleep(2)
                if save_get_items:
                    self.device.screenshot()
                    self.device.save_screenshot('research_items', to_base_folder=True)
                self.device.click(GET_ITEMS_RESEARCH_SAVE)
                executed = True
                continue
            if self.appear(GET_ITEMS_2, interval=5):
                self.wait_until_get_items_stable()
                logger.info('Get items 2 stabled')
                if not self.appear(GET_ITEMS_2, interval=0):
                    continue
                self.device.sleep(2)
                if save_get_items:
                    self.device.screenshot()
                    self.device.save_screenshot('research_items', to_base_folder=True)
                self.device.click(GET_ITEMS_RESEARCH_SAVE)
                executed = True
                continue
            if self.appear(GET_ITEMS_3, interval=5):
                self.wait_until_get_items_stable()
                logger.info('Get items 3 stabled')
                if not self.appear(GET_ITEMS_3, interval=0):
                    continue
                self.device.sleep(3)
                if save_get_items:
                    self.device.screenshot()
                    self.device.save_screenshot('research_items', to_base_folder=True)
                    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()
                    self.device.save_screenshot('research_items', interval=0, to_base_folder=True)
                self.device.click(GET_ITEMS_RESEARCH_SAVE)
                executed = True
                continue

            # End
            if executed and self._in_research():
                self.ensure_research_stable()
                break

        self.device.screenshot_interval_set(0.1)
예제 #28
0
    def run(self, name, folder='campaign_main', total=0):
        """
        Args:
            name (str): Name of .py file.
            folder (str): Name of the file folder under campaign.
            total (int):
        """
        name, folder = self.handle_stage_name(name, folder)
        self.load_campaign(name, folder=folder)
        self.run_count = 0
        while 1:
            if self.handle_app_restart():
                self.campaign.fleet_checked_reset()
            if self.handle_reward():
                self.campaign.fleet_checked_reset()
            if self.config.GUILD_POPUP_TRIGGERED:
                self.ensure_auto_search_exit()
                self.handle_guild(
                )  # Will reset self.config.GUILD_POPUP_TRIGGERED afterwards
                self.campaign.config.GUILD_POPUP_TRIGGERED = False
                self.campaign.fleet_checked_reset()

            # End
            if total and self.run_count == total:
                break

            # Log
            logger.hr(name, level=1)
            if self.config.STOP_IF_COUNT_GREATER_THAN > 0:
                logger.info(
                    f'Count: [{self.run_count}/{self.config.STOP_IF_COUNT_GREATER_THAN}]'
                )
            else:
                logger.info(f'Count: [{self.run_count}]')

            # UI ensure
            self.device.screenshot()
            self.campaign.device.image = self.device.image
            if self.campaign.is_in_map():
                logger.info('Already in map, skip ensure_campaign_ui.')
            elif self.campaign.is_in_auto_search_menu():
                logger.info('In auto search menu, skip ensure_campaign_ui.')
            else:
                self.campaign.ensure_campaign_ui(
                    name=self.stage,
                    mode=self.config.CAMPAIGN_MODE
                    if self.config.COMMAND.lower() == 'main' else 'normal')
            if self.config.ENABLE_REWARD and self.commission_notice_show_at_campaign(
            ):
                logger.info('Commission notice found')
                if self.reward():
                    self.campaign.fleet_checked_reset()
                    continue

            # End
            if self.triggered_stop_condition(
                    oil_check=not self.campaign.is_in_auto_search_menu()):
                self.campaign.ensure_auto_search_exit()
                break

            # Run
            try:
                self.campaign.run()
            except ScriptEnd as e:
                logger.hr('Script end')
                logger.info(str(e))
                break

            # After run
            self.run_count += 1
            self.config.GUILD_POPUP_TRIGGERED = self.campaign.config.GUILD_POPUP_TRIGGERED
            if self.config.STOP_IF_COUNT_GREATER_THAN > 0:
                count = self.config.STOP_IF_COUNT_GREATER_THAN - self.run_count
                count = 0 if count < 0 else count
                self.config.config.set('Setting', 'if_count_greater_than',
                                       str(count))
                self.config.save()
            # One-time stage limit
            if self.campaign.config.MAP_IS_ONE_TIME_STAGE:
                if self.run_count >= 1:
                    logger.hr('Triggered one-time stage limit')
                    break

        self.campaign.ensure_auto_search_exit()
예제 #29
0
 def show_camera(self):
     logger.info('                Camera: %s' % location2node(self.camera))
예제 #30
0
    def find_current_fleet(self):
        logger.hr('Find current fleet')
        if not self.config.POOR_MAP_DATA:
            fleets = self.map.select(is_fleet=True, is_spawn_point=True)
        else:
            fleets = self.map.select(is_fleet=True)
        logger.info('Fleets: %s' % str(fleets))
        count = fleets.count
        if count == 1:
            if not self.config.FLEET_2:
                self.fleet_1 = fleets[0].location
            else:
                logger.info('Fleet_2 not detected.')
                if self.config.POOR_MAP_DATA and not self.map.select(
                        is_spawn_point=True):
                    self.fleet_1 = fleets[0].location
                elif self.map.select(is_spawn_point=True).count == 2:
                    logger.info('Predict fleet to be spawn point')
                    another = self.map.select(is_spawn_point=True).delete(
                        SelectedGrids([fleets[0]]))[0]
                    if fleets[0].is_current_fleet:
                        self.fleet_1 = fleets[0].location
                        self.fleet_2 = another.location
                    else:
                        self.fleet_1 = another.location
                        self.fleet_2 = fleets[0].location
                else:
                    cover = self.map.grid_covered(fleets[0],
                                                  location=[(0, -1)])
                    if fleets[0].is_current_fleet and len(
                            cover) and cover[0].is_spawn_point:
                        self.fleet_1 = fleets[0].location
                        self.fleet_2 = cover[0].location
                    else:
                        self.find_all_fleets()
        elif count == 2:
            current = self.map.select(is_current_fleet=True)
            if current.count == 1:
                self.fleet_1 = current[0].location
                self.fleet_2 = fleets.delete(current)[0].location
            else:
                fleets = fleets.sort_by_camera_distance(self.camera)
                self.in_sight(fleets[0], sight=(-1, 0, 1, 2))
                if self.convert_map_to_grid(fleets[0]).predict_current_fleet():
                    self.fleet_1 = fleets[0].location
                    self.fleet_2 = fleets[1].location
                else:
                    self.in_sight(fleets[1], sight=(-1, 0, 1, 2))
                    if self.convert_map_to_grid(
                            fleets[1]).predict_current_fleet():
                        self.fleet_1 = fleets[1].location
                        self.fleet_2 = fleets[0].location
                    else:
                        logger.warning('Current fleet not found')
                        self.fleet_1 = fleets[0].location
                        self.fleet_2 = fleets[1].location
        else:
            if count == 0:
                logger.warning('No fleets detected.')
                fleets = self.map.select(is_current_fleet=True)
                if fleets.count:
                    self.fleet_1 = fleets[0].location
            if count > 2:
                logger.warning('Too many fleets: %s.' % str(fleets))
            self.find_all_fleets()

        self.fleet_current_index = 1
        self.show_fleet()
        return self.fleet_current