def lv_triggered(self): limit = self.config.StopCondition_ReachLevel if not limit: return False for i in range(6): before, after = self._lv_before_battle[i], self.lv[i] if after > before > 0: logger.info(f'Position {i} LV.{before} -> LV.{after}') if after >= limit > before > 0: if after - before == 1 or after < 35: logger.info(f'Position {i} LV.{limit} Reached') self.config.LV_TRIGGERED = True return True else: logger.warning(f'Level gap between {before} and {after} is too large. ' f'This will not be considered as a trigger') return False
def shop_get_choice(self, item): """ Gets the configuration saved in for the appropriate variant shop i.e. GuildShop_X Args: item (Item): Returns: str Raises: ScriptError """ group = item.group if group == 'pr': postfix = None for _ in range(3): if _: self.device.sleep((0.3, 0.5)) self.device.screenshot() for idx, btn in enumerate(SHOP_SELECT_PR): if self.appear(btn, offset=(20, 20)): postfix = f'{idx + 1}' break if postfix is not None: break logger.warning('Failed to detect PR series, ' 'app may be lagging or frozen') else: postfix = f'_{item.tier.upper()}' ugroup = group.upper() class_name = self.__class__.__name__ try: return getattr(self.config, f'{class_name}_{ugroup}{postfix}') except Exception: logger.critical(f'No configuration with name ' f'\'{class_name}_{ugroup}{postfix}\'') raise ScriptError
def get_info(self, main): """ Args: main (ModuleBase): Returns: int, int, int: Index of the active nav item, leftmost nav item, and rightmost nav item. """ total = [] active = [] for index, button in enumerate(self.grids.buttons): if main.image_color_count(button, color=self.active_color, threshold=self.active_threshold, count=self.active_count): total.append(index) active.append(index) elif main.image_color_count(button, color=self.inactive_color, threshold=self.inactive_threshold, count=self.inactive_count): total.append(index) if len(active) == 0: # logger.warning(f'No active nav item found in {self.name}') active = None elif len(active) == 1: active = active[0] else: logger.warning( f'Too many active nav items found in {self.name}, items: {active}' ) active = active[0] if len(total) < 2: logger.warning( f'Too few nav items found in {self.name}, items: {total}') if len(total) == 0: left, right = None, None else: left, right = min(total), max(total) return active, left, right
def wait_until(self, future): """ Wait until a specific time. Args: future (datetime): """ seconds = future.timestamp() - datetime.now().timestamp() + 1 if seconds <= 0: logger.warning(f'Wait until {str(future)}, but sleep length < 0, skip waiting') if self.stop_event is not None: self.stop_event.wait(seconds) if self.stop_event.is_set(): logger.info("Update event detected") logger.info(f"Alas [{self.config_name}] exited.") exit(0) else: time.sleep(seconds)
def connect(self, serial): """Connect to a device. Args: serial (str): device serial or device address. Returns: uiautomator2.UIAutomatorServer: Device. """ self._adb_connect(serial) try: device = u2.connect(serial) return device except AssertionError: logger.warning( 'AssertionError when connecting emulator with uiautomator2.') logger.warning( 'If you are using BlueStacks, you need to enable ADB in the settings of your emulator.' )
def parse_time(self, string): """ Args: string (str): Such as 01:00:00, 05:47:10, 17:50:51. Returns: timedelta: datetime.timedelta instance. """ string = string.replace('D', '0') # Poor OCR result = re.search('(\d+):(\d+):(\d+)', string) if not result: logger.warning(f'Invalid time string: {string}') self.valid = False return None else: result = [int(s) for s in result.groups()] return timedelta(hours=result[0], minutes=result[1], seconds=result[2])
def _commission_ensure_mode(self, mode): if self.appear(COMMISSION_DAILY): current = 'daily' elif self.appear(COMMISSION_URGENT): current = 'urgent' else: logger.warning('Unknown Commission mode') return False if current == mode: return False if mode == 'daily': self.device.click(COMMISSION_DAILY) if mode == 'urgent': self.device.click(COMMISSION_URGENT) self.device.sleep(0.3) self.device.screenshot() return True
def _handle_ambush_evade(self): logger.info('Map ambushed') _ = self._load_ambush_template self.wait_until_appear_then_click(MAP_AMBUSH_EVADE) self.wait_until_appear(INFO_BAR_1) image = info_letter_preprocess( np.array(self.device.image.crop(INFO_BAR_DETECT.area))) if TEMPLATE_AMBUSH_EVADE_SUCCESS.match(image): logger.attr('Ambush_evade', 'success') elif TEMPLATE_AMBUSH_EVADE_FAILED.match(image): logger.attr('Ambush_evade', 'failed') self.combat(expected_end='no_searching') else: logger.warning('Unrecognised info when ambush evade.') self.ensure_no_info_bar() if self.combat_appear(): self.combat()
def _dorm_feed_long_tap(self, button, count): timeout = Timer(count // 5 + 5).start() x, y = random_rectangle_point(button.button) self.device.u2.touch.down(x, y) while 1: self.device.u2.touch.move(x, y) time.sleep(.01) self.device.screenshot() if not self._dorm_has_food(button) \ or self.handle_info_bar() \ or self.handle_popup_cancel('dorm_feed'): break if timeout.reached(): logger.warning('Wait dorm feed timeout') break self.device.u2.touch.up(x, y)
def set(self, position, main, random_range=(-0.05, 0.05), skip_first_screenshot=True): """ Set scroll to a specific position. Args: position (float, int): 0 to 1. main (ModuleBase): random_range (tuple(int, float)): skip_first_screenshot: """ logger.info(f'{self.name} set to {position}') self.drag_interval.clear() if position == 0: random_range = np.subtract(0, self.edge_add) if position == 1: random_range = self.edge_add while 1: if skip_first_screenshot: skip_first_screenshot = False else: main.device.screenshot() current = self.cal_position(main) if abs(position - current) < self.drag_threshold: break if not self.length: logger.warning('Scroll disappeared, assume scroll set') break if self.drag_interval.reached(): p1 = random_rectangle_point(self.position_to_screen(current), n=1) p2 = random_rectangle_point(self.position_to_screen( position, random_range=random_range), n=1) main.device.swipe(p1, p2, name=self.name) main.device.sleep(0.3) self.drag_interval.reset()
def commission_name_parse(self, string): """ Args: string (str): Commission name, such as 'NYB要员护卫'. Returns: str: Commission genre, such as 'urgent_gem'. """ # string = string.replace(' ', '').replace('-', '') if self.is_doa_commission(): return 'doa_daily' for key, value in dictionary_en.items(): for keyword in value: if keyword in string: return key logger.warning(f'Name with unknown genre: {string}') self.valid = False return ''
def clear_boss(self): grids = self.map.select(is_boss=True) grids = grids.add(self.map.select(may_boss=True, is_enemy=True)) logger.info('May boss: %s' % self.map.select(may_boss=True)) logger.info('May boss and is enemy: %s' % self.map.select(may_boss=True, is_enemy=True)) logger.info('Is boss: %s' % self.map.select(is_boss=True)) # logger.info('Grids: %s' % grids) if grids: logger.hr('Clear BOSS') grids = grids.sort('weight', 'cost') logger.info('Grids: %s' % str(grids)) self._goto(grids[0], expected='boss') raise CampaignEnd('BOSS Clear.') logger.warning('BOSS not detected, trying all boss spawn point.') self.clear_potential_boss() return False
def _campaign_separate_name(name): """ Args: name (str): Stage name in lowercase, such as 7-2, d3, sp3. Returns: tuple[str]: Campaign_name and stage index in lowercase, Such as ['7', '2'], ['d', '3'], ['sp', '3']. """ if name == 'sp': return 'ex_sp', '1' elif '-' in name: return name.split('-') elif name.startswith('sp'): return 'sp', name[-1] elif name[-1].isdigit(): return name[:-1], name[-1] logger.warning(f'Unknown stage name: {name}') return name[0], name[1:]
def _tactical_books_choose(self): """ Choose tactical book according to config. """ books = BookGroup([ Book(self.device.image, button) for button in BOOKS_GRID.buttons() ]).select(valid=True) logger.attr('Book_count', len(books)) for index in range(1, 4): logger.info(f'Book_T{index}: {books.select(tier=index)}') if not books: logger.warning('No book found.') raise ScriptError('No book found.') # if not time_range_active(self.config.TACTICAL_NIGHT_RANGE): # tier = self.config.TACTICAL_BOOK_TIER # exp = self.config.TACTICAL_EXP_FIRST # else: # tier = self.config.TACTICAL_BOOK_TIER_NIGHT # exp = self.config.TACTICAL_EXP_FIRST_NIGHT # book = books.choose(tier=tier, exp=exp) book = books.choose(tier_max=self.config.TACTICAL_BOOK_TIER_MAX, tier_min=self.config.TACTICAL_BOOK_TIER_MIN, exp=self.config.TACTICAL_EXP_FIRST) if book is not None: while 1: self.device.click(book.button) self.device.screenshot() if book.check_selected(self.device.image): break self.device.click(TACTICAL_CLASS_START) else: # cancel_tactical, use_the_first_book if self.config.TACTICAL_IF_NO_BOOK_SATISFIED == 'use_the_first_book': logger.info('Choose first book') self.device.click(books[0].button) self.device.sleep((0.3, 0.5)) self.device.click(TACTICAL_CLASS_START) else: logger.info('Cancel tactical') self.device.click(TACTICAL_CLASS_CANCEL)
def _research_has_finished_at(self, button): """ Args: button (Button): Returns: bool: True if a research finished """ color = get_color(self.device.image, button.area) if np.max(color) - np.min(color) < 40: logger.warning(f'Unexpected color: {color}') index = np.argmax(color) # R, G, B if index == 1: return True # Green elif index == 2: return False # Blue else: logger.warning(f'Unexpected color: {color}') return False
def _sos_signal_select(self, chapter): """ select a SOS signal Args: chapter (int): 3 to 10. Pages: in: page_campaign out: page_campaign, in target chapter Returns: bool: whether select successful """ logger.hr(f'Select chapter {chapter} signal ') self.ui_click(SIGNAL_SEARCH_ENTER, appear_button=CAMPAIGN_CHECK, check_button=SIGNAL_LIST_CHECK, skip_first_screenshot=True) if chapter in [3, 4, 5]: positions = [0.0, 0.5, 1.0] elif chapter in [6, 7]: positions = [0.5, 1.0, 0.0] elif chapter in [8, 9, 10]: positions = [1.0, 0.5, 0.0] else: logger.warning(f'Unknown SOS chapter: {chapter}') positions = [0.0, 0.5, 1.0] for scroll_position in positions: if self._sos_scroll.appear(main=self): self._sos_scroll.set(scroll_position, main=self, distance_check=False) else: logger.info( 'SOS signal scroll not appear, skip setting scroll position' ) target_button = self._find_target_chapter(chapter) if target_button is not None: self._sos_signal_confirm(entrance=target_button) return True return False
def _equip_sidebar_click(self, index): """ Args: index (int): 5 for retrofit. 4 for enhancement. 3 for limit break. 2 for gem / equipment. 1 for detail. Returns: bool: if changed. """ current = 0 total = 0 for idx, button in enumerate(DETAIL_SIDEBAR.buttons()): image = np.array(self.device.image.crop(button.area)) if np.sum(image[:, :, 0] > 235) > 100: current = idx + 1 total = idx + 1 continue if np.sum(color_similarity_2d(image, color=(140, 162, 181)) > 221) > 100: total = idx + 1 else: break if not current: logger.warning('No ship details sidebar active.') if total == 4: current = 5 - current elif total == 5: current = 6 - current else: logger.warning('Ship details sidebar total count error.') logger.attr('Detail_sidebar', f'{current}/{total}') if current == index: return False self.device.click(DETAIL_SIDEBAR[0, total - index]) return True
def os_init(self): """ Call this method before doing any Operation functions. Pages: in: IN_MAP or IN_GLOBE or page_os or any page out: IN_MAP """ logger.hr('OS init', level=1) self.config.override(Submarine_Fleet=1, Submarine_Mode='every_combat') # UI switching if self.is_in_map(): logger.info('Already in os map') elif self.is_in_globe(): self.os_globe_goto_map() else: if self.ui_page_appear(page_os): self.ui_goto_main() self.ui_ensure(page_os) # Init self.zone_init() # self.map_init() self.hp_reset() self.handle_fleet_repair(revert=False) # Exit from special zones types, only SAFE and DANGEROUS are acceptable. if self.is_in_special_zone(): logger.warning( 'OS is in a special zone type, while SAFE and DANGEROUS are acceptable' ) self.map_exit() # Clear current zone if self.zone.is_port: logger.info('In port, skip running first auto search') self.handle_ash_beacon_attack() else: self.run_auto_search() self.handle_fleet_repair(revert=False)
def find_bluestacks5_hyperv(serial): """ Find dynamic serial of Bluestacks5 Hyper-v. Args: serial (str): 'bluestacks5-hyperv', 'bluestacks5-hyperv-1' for multi instance, and so on. Returns: str: 127.0.0.1:{port} """ from winreg import ConnectRegistry, OpenKey, QueryInfoKey, EnumValue, CloseKey, HKEY_LOCAL_MACHINE logger.info("Use Bluestacks5 Hyper-v") logger.info("Reading Realtime adb port") if serial == "bluestacks5-hyperv": parameter_name = "bst.instance.Nougat64.status.adb_port" else: parameter_name = f"bst.instance.Nougat64_{serial[19:]}.status.adb_port" reg_root = ConnectRegistry(None, HKEY_LOCAL_MACHINE) sub_dir = f"SOFTWARE\\BlueStacks_nxt" bs_keys = OpenKey(reg_root, sub_dir) bs_keys_count = QueryInfoKey(bs_keys)[1] for i in range(bs_keys_count): key_name, key_value, key_type = EnumValue(bs_keys, i) if key_name == "UserDefinedDir": logger.info(f"Configuration file directory: {key_value}") with open(f"{key_value}\\bluestacks.conf", 'r', encoding='utf-8') as f: content = f.read() port = re.findall(rf'{parameter_name}="(.*?)"\n', content, re.S) if len(port) > 0: logger.info(f"Match to dynamic port: {port[0]}") serial = f"127.0.0.1:{port[0]}" else: logger.warning(f"Did not match the result: {serial}.") break CloseKey(bs_keys) CloseKey(reg_root) return serial
def find_submarine(self): if not (self.config.SUBMARINE and self.map.select(is_submarine_spawn_point=True)): return False fleets = self.map.select(is_submarine=True) count = fleets.count if count == 1: self.fleet_submarine = fleets[0].location elif count == 0: logger.info('No submarine found') # Try spawn points spawn_point = self.map.select(is_submarine_spawn_point=True) if spawn_point.count == 1: logger.info(f'Predict the only submarine spawn point {spawn_point[0]} as submarine') self.fleet_submarine = spawn_point[0].location else: logger.info(f'Having multiple submarine spawn points: {spawn_point}') # Try covered grids covered = SelectedGrids([]) for grid in spawn_point: covered = covered.add(self.map.grid_covered(grid, location=[(0, 1)])) covered = covered.filter(lambda g: g.is_enemy or g.is_fleet or g.is_siren or g.is_boss) if covered.count == 1: spawn_point = self.map.grid_covered(covered[0], location=[(0, -1)]) logger.info(f'Submarine {spawn_point[0]} covered by {covered[0]}') self.fleet_submarine = spawn_point[0].location else: logger.info('Found multiple submarine spawn points being covered') # Give up self.find_all_submarines() else: logger.warning('Too many submarines: %s.' % str(fleets)) self.find_all_submarines() if not len(self.fleet_submarine_location): logger.warning('Unable to find submarine, assume it is at map center') shape = self.map.shape center = (shape[0] // 2, shape[1] // 2) self.fleet_submarine = self.map.select(is_land=False).sort_by_camera_distance(center)[0].location self.show_submarine() return self.fleet_submarine_location
def set(self, status, main, skip_first_screenshot=True): """ Args: status (str): main (ModuleBase): skip_first_screenshot (bool): Returns: bool: """ counter = 0 changed = False warning_show_timer = Timer(5, count=10).start() while 1: if skip_first_screenshot: skip_first_screenshot = False else: main.device.screenshot() current = self.get(main=main) logger.attr(self.name, current) if current == status: return changed if current == 'unknown': if warning_show_timer.reached(): logger.warning(f'Unknown {self.name} switch') warning_show_timer.reset() if counter >= 1: logger.warning( f'{self.name} switch {status} asset has evaluated to unknown too many times, asset should be re-verified' ) return False counter += 1 continue click_status = status if self.is_choice else current for data in self.status_list: if data['status'] == click_status: main.device.click(data['click_button']) main.device.sleep(data['sleep']) changed = True
def convert_radar_to_local(self, location): """ Converts the coordinate on radar to the coordinate of local map view, also handles a rare game bug. Usually, OPSI camera focus on current fleet, which is (5, 4) in local view. The convert should be `local = view[np.add(radar, view.center_loca)]` However, Azur Lane may bugged, not focusing current. In this case, the convert should base on fleet position. Args: location: (x, y), Position on radar. Returns: OSGrid: Grid instance in self.view """ location = location_ensure(location) fleets = self.view.select(is_current_fleet=True) if fleets.count == 1: center = fleets[0].location elif fleets.count > 1: logger.warning( f'Convert radar to local, but found multiple current fleets: {fleets}' ) distance = np.linalg.norm( np.subtract(fleets.location, self.view.center_loca)) center = fleets.grids[np.argmin(distance)].location logger.warning( f'Assuming the nearest fleet to camera canter is current fleet: {location2node(center)}' ) else: logger.warning( f'Convert radar to local, but current fleet not found. ' f'Assuming camera center is current fleet: {location2node(self.view.center_loca)}' ) center = self.view.center_loca try: local = self.view[np.add(location, center)] except KeyError: logger.warning( f'Convert radar to local, but target grid not in local view. ' f'Assuming camera center is current fleet: {location2node(self.view.center_loca)}' ) center = self.view.center_loca local = self.view[np.add(location, center)] logger.info('Radar %s -> Local %s (fleet=%s)' % (str(location), location2node( local.location), location2node(center))) return local
def globe_goto(self, zone, types=('SAFE', 'DANGEROUS'), refresh=False): """ Goto another zone in OS. Args: zone (str, int, Zone): Name in CN/EN/JP, zone id, or Zone instance. types (tuple[str], list[str], str): Zone types, or a list of them. Available types: DANGEROUS, SAFE, OBSCURE, LOGGER, STRONGHOLD. Try the the first selection in type list, if not available, try the next one. refresh (bool): If already at target zone, set false to skip zone switching, set true to re-enter current zone to refresh. Pages: in: IN_MAP or IN_GLOBE out: IN_MAP """ zone = self.name_to_zone(zone) logger.hr(f'Globe goto: {zone}') if self.zone == zone: if refresh: logger.info('Goto another zone to refresh current zone') self.globe_goto(self.zone_nearest_azur_port(self.zone), types=('SAFE', 'DANGEROUS'), refresh=False) else: logger.info('Already at target zone') return False # IN_MAP if self.is_in_map(): self.os_map_goto_globe() # IN_GLOBE if not self.is_in_globe(): logger.warning('Trying to move in globe, but not in os globe map') raise ScriptError('Trying to move in globe, but not in os globe map') # self.ensure_no_zone_pinned() self.globe_update() self.globe_focus_to(zone) self.zone_type_select(types=types) self.globe_enter(zone) # IN_MAP if hasattr(self, 'zone'): del self.zone self.get_current_zone()
def guild_logistics(self): """ Execute all actions in logistics Pages: in: GUILD_LOGISTICS out: GUILD_LOGISTICS """ # Transition to Logistics if not self.guild_sidebar_ensure(3): logger.info( 'Logistics ensurance failed, try again on next reward loop') return # Last screencapture should contain affiliation # color in top-right where guild coins is # Determine guild affiliation color = get_color(self.device.image, GUILD_AFFILIATION_CHECK_LOGISTICS.area) if color_similar(color, (115, 146, 206)): is_azur_affiliation = True elif color_similar(color, (206, 117, 115)): is_azur_affiliation = False else: logger.warning(f'Unknown guild affiliation color: {color}') return # Handle logistics actions collect/accept # Exchange will be executed separately self._guild_logistics_collect(is_azur_affiliation) # Limit check whether can exchange to once a day if not self.config.record_executed_since(option=RECORD_OPTION, since=RECORD_SINCE): # Handle action exchange, determine color of digit based on affiliation GUILD_EXCHANGE_LIMIT.letter = ( 173, 182, 206) if is_azur_affiliation else (214, 113, 115) limit = GUILD_EXCHANGE_LIMIT.ocr(self.device.image) if limit > 0: self._guild_exchange(limit, is_azur_affiliation) self.config.record_save(option=RECORD_OPTION)
def ui_get_current_page(self, skip_first_screenshot=True): """ Args: skip_first_screenshot: Returns: Page: """ if not skip_first_screenshot or not hasattr( self.device, 'image') or self.device.image is None: self.device.screenshot() # Known pages for page in self.ui_pages: if page.check_button is None: continue if self.ui_page_appear(page=page): logger.attr('UI', page.name) self.ui_current = page return page # Unknown page but able to handle logger.info('Unknown ui page') if self.appear_then_click(GOTO_MAIN, offset=(20, 20)) or self.ui_additional(): logger.info('Goto page_main') self.ui_current = page_unknown self.ui_goto(page_main, skip_first_screenshot=True) # Unknown page, need manual switching if hasattr(self, 'ui_current'): logger.warning( f'Unrecognized ui_current, using previous: {self.ui_current}') else: logger.info('Unable to goto page_main') logger.attr('EMULATOR__SCREENSHOT_METHOD', self.config.Emulator_ScreenshotMethod) logger.attr('EMULATOR__CONTROL_METHOD', self.config.Emulator_ControlMethod) logger.attr('SERVER', self.config.SERVER) logger.warning('Starting from current page is not supported') logger.warning( f'Supported page: {[str(page) for page in self.ui_pages]}') logger.warning( f'Supported page: Any page with a "HOME" button on the upper-right' ) if not self.device.app_is_running(): raise GameNotRunningError('Game not running') else: logger.critical( 'Please switch to a supported page before starting Alas') raise RequestHumanTakeover
def _tactical_books_get(self, skip_first_screenshot=True): """ Get books. Handle loadings, wait 10 times at max. When TACTICAL_CLASS_START appears, game may stuck in loading, wait and retry detection. If loading still exists, raise ScriptError. Returns: BookGroup: Pages: in: TACTICAL_CLASS_START out: TACTICAL_CLASS_START """ prev = SelectedGrids([]) for n in range(1, 16): if skip_first_screenshot: skip_first_screenshot = False else: self.device.screenshot() self.handle_info_bar( ) # info_bar appears when get ship in Launch Ceremony commissions books = SelectedGrids([ Book(self.device.image, button) for button in BOOKS_GRID.buttons ]).select(valid=True) self.books = books logger.attr('Book_count', books.count) logger.attr('Books', str(books)) # End if books and books.count == prev.count: return books else: prev = books if n % 3 == 0: self.device.sleep(3) continue logger.warning('No book found.') raise ScriptError('No book found, after 15 attempts.')
def __init__(self, name, series): """ Args: name (str): Such as 'D-057-UL' series (int): Such as 1, 2, 3 """ self.valid = True # self.config = config self.name = self.check_name(name) self.series = f'S{series}' self.genre = '' self.duration = '24' self.ship = '' self.ship_rarity = '' self.need_coin = False self.need_cube = False self.need_part = False matched = False for data in LIST_RESEARCH_PROJECT: if data['name'] == self.name and data['series'] == series: matched = True self.data = data self.genre = data['name'][0] self.duration = str(data['time'] / 3600).rstrip('.0') for item in data['input']: result = re.search(self.REGEX_INPUT, item['name'].replace(' ', '').lower()) if result: self.__setattr__(f'need_{result.group(1)}', True) for item in data['output']: result = re.search(self.REGEX_SHIP, item['name'].replace(' ', '').lower()) if not self.ship: self.ship = result.group(1) if result else '' if self.ship: self.ship_rarity = 'dr' if self.ship in self.DR_SHIP else 'pry' break if not matched: logger.warning(f'Invalid research {self}') self.valid = False
def get_next_task(self): """ Returns: str: Name of the next task. """ task = self.config.get_next() self.config.task = task self.config.bind(task) from module.base.resource import release_resources if self.config.task.command != 'Alas': release_resources(next_task=task.command) if task.next_run > datetime.now(): logger.info( f'Wait until {task.next_run} for task `{task.command}`') method = self.config.Optimization_WhenTaskQueueEmpty if method == 'close_game': logger.info('Close game during wait') self.device.app_stop() release_resources() self.wait_until(task.next_run) self.run('start') elif method == 'goto_main': logger.info('Goto main page during wait') self.run('goto_main') release_resources() self.wait_until(task.next_run) elif method == 'stay_there': logger.info('Stay there during wait') release_resources() self.wait_until(task.next_run) else: logger.warning( f'Invalid Optimization_WhenTaskQueueEmpty: {method}, fallback to stay_there' ) release_resources() self.wait_until(task.next_run) AzurLaneConfig.is_hoarding_task = False return task.command
def _run_update(self, instances, names): self.state = 'run update' logger.info("All alas stopped, start updating") if self.update(): if Setting.reload: self.state = 'reload' with open('./config/reloadalas', mode='w') as f: f.writelines(names) from module.webui.app import clearup self._trigger_reload(2) clearup() else: self.state = 'finish' else: self.state = 'failed' logger.warning("Update failed") self.event.clear() ProcessManager.restart_processes(instances, self.event) return False
def _tactical_animation_running(self): """ Returns: bool: If showing skill points increasing animation. """ color_height = np.mean(self.device.image.crop((922, 0, 1036, 720)).convert('L'), axis=1) parameters = {'height': 200} peaks, _ = signal.find_peaks(color_height, **parameters) peaks = [y for y in peaks if y > 67 + 243] if not len(peaks): logger.warning('No student card found.') for y in peaks: student_area = (447, y - 243, 1244, y) area = area_offset((677, 172, 761, 183), student_area[0:2]) # Normal: 160, In skill-increasing animation: 109 if np.mean(get_color(self.device.image, area)) < 135: return True return False