def in_sight(self, location, sight=None): """Make sure location in camera sight Args: location: sight (tuple): Such as (-3, -1, 3, 2). """ location = location_ensure(location) logger.info('In sight: %s' % location2node(location)) if sight is None: sight = self.map.camera_sight diff = np.array(location) - self.camera if diff[1] > sight[3]: y = diff[1] - sight[3] elif diff[1] < sight[1]: y = diff[1] - sight[1] else: y = 0 if diff[0] > sight[2]: x = diff[0] - sight[2] elif diff[0] < sight[0]: x = diff[0] - sight[0] else: x = 0 self.focus_to((self.camera[0] + x, self.camera[1] + y))
def convert_map_to_grid(self, location): """If self.grids doesn't contain this location, focus camera on the location and re-convert it. Args: location: Grid instance in self.map. Returns: Grid: Grid instance in self.grids. """ location = location_ensure(location) local = np.array(location) - self.camera + self.view.center_loca logger.info('Global %s (camera=%s) -> Local %s (center=%s)' % ( location2node(location), location2node(self.camera), location2node(local), location2node(self.view.center_loca) )) if local in self.view: return self.view[local] else: logger.warning('Convert global to local Failed.') self.focus_to(location) local = np.array(location) - self.camera + self.view.center_loca return self.view[local]
def convert_local_to_global(self, location): """ If self.map doesn't contain this location, camera might be wrong, correct camera and re-convert it. Args: location: Grid instance in self.view Returns: Grid: Grid instance in self.map """ location = location_ensure(location) global_ = np.array(location) + self.camera - self.view.center_loca logger.info( 'Global %s (camera=%s) <- Local %s (center=%s)' % (location2node(global_), location2node(self.camera), location2node(location), location2node(self.view.center_loca))) if global_ in self.map: return self.map[global_] else: logger.warning('Convert local to global Failed.') self.ensure_edge_insight(reverse=True) global_ = np.array(location) + self.camera - self.view.center_loca return self.map[global_]
def goto(self, location, optimize=True, expected=''): # self.device.sleep(1000) location = location_ensure(location) if optimize and (self.config.MAP_HAS_AMBUSH or self.config.MAP_HAS_FLEET_STEP or self.config.MAP_HAS_PORTAL or self.config.MAP_HAS_MAZE): nodes = self.map.find_path(location, step=self.fleet_step) for node in nodes: if self.maze_active_on(node): logger.info( f'Maze is active on {location2node(node)}, bouncing to wait' ) for _ in range(10): grids = self.map[node].maze_nearby.delete( self.map.select(is_fleet=True)) if grids.select(is_enemy=False): grids = grids.select(is_enemy=False) grids = grids.sort('cost') self._goto(grids[0], expected='') try: self._goto(node, expected=expected if node == nodes[-1] else '') except MapWalkError: logger.warning('Map walk error.') self.ensure_edge_insight() nodes_ = self.map.find_path(node, step=1) for node_ in nodes_: self._goto( node_, expected=expected if node == nodes[-1] else '') else: self._goto(location, expected=expected)
def convert_map_to_grid(self, location): """If self.grids doesn't contain this location, focus camera on the location and re-convert it. Args: location: Grid instance in self.map. Returns: Grid: Grid instance in self.grids. """ location = location_ensure(location) grid = np.array(location) - self.camera + self.grids.center_grid logger.info('Convert_map_to_grid') logger.info(f'Map: {location2node(location)}, ' f'Camera: {location2node(self.camera)}, ' f'Center: {location2node(self.grids.center_grid)}, ' f'grid: {location2node(grid)}') if grid in self.grids: return self.grids[grid] else: logger.warning('Convert_map_to_grid Failed.') self.grids.save_error_image() self.focus_to(location) grid = np.array(location) - self.camera + self.grids.center_grid return self.grids[grid]
def submarine_move_near_boss(self, boss): """ Args: boss (tuple, str, GridInfo): Destination. Returns: bool: If submarine moved """ if not (self.is_call_submarine_at_boss and self.map.select(is_submarine_spawn_point=True)): return False if self.config.Submarine_DistanceToBoss == 'use_U522_skill': logger.info('Going to use U522 skill, skip moving submarines') return False boss = location_ensure(boss) logger.info(f'Move submarine near {location2node(boss)}') self.map.find_path_initial(self.fleet_submarine_location, has_ambush=False, has_enemy=False) self.map.show_cost() def get_location(distance=2): grids = self.map.select(is_land=False).filter(lambda grid: np.sum( np.abs(np.subtract(grid.location, boss))) <= distance) if grids: return grids.sort('cost')[0].location elif distance > 0: logger.info( f'Unable to find a grid near boss in distance {distance}, fallback to {distance - 1}' ) return get_location(distance - 1) else: logger.warning( f'Unable to find a grid near boss in distance {distance}, return boss position' ) return boss distance_dict = { 'to_boss_position': 0, '1_grid_to_boss': 1, '2_grid_to_boss': 2 } distance_to_boss = distance_dict.get( self.config.Submarine_DistanceToBoss, 0) logger.attr('Distance to boss', distance_to_boss) if np.sum(np.abs(np.subtract(self.fleet_submarine_location, boss))) <= distance_to_boss: logger.info('Boss is already in hunting zone') self.find_path_initial() return False else: near = get_location(distance_to_boss) self.find_path_initial() logger.info(f'Move submarine to {location2node(near)}') return self.submarine_goto(near)
def grid_is_in_sight(self, grid, camera=None, sight=None): location = location_ensure(grid) camera = location_ensure(camera) if camera is not None else self.camera if sight is None: sight = self.map.camera_sight diff = np.array(location) - camera if diff[1] > sight[3]: y = diff[1] - sight[3] elif diff[1] < sight[1]: y = diff[1] - sight[1] else: y = 0 if diff[0] > sight[2]: x = diff[0] - sight[2] elif diff[0] < sight[0]: x = diff[0] - sight[0] else: x = 0 return x == 0 and y == 0
def convert_radar_to_local(self, location): """ Converts the coordinate on radar to the coordinate of local map view, also handles a rare game bug. Usually, OPSI camera focus on current fleet, which is (5, 4) in local view. The convert should be `local = view[np.add(radar, view.center_loca)]` However, Azur Lane may bugged, not focusing current. In this case, the convert should base on fleet position. Args: location: (x, y), Position on radar. Returns: OSGrid: Grid instance in self.view """ location = location_ensure(location) fleets = self.view.select(is_current_fleet=True) if fleets.count == 1: center = fleets[0].location elif fleets.count > 1: logger.warning( f'Convert radar to local, but found multiple current fleets: {fleets}' ) distance = np.linalg.norm( np.subtract(fleets.location, self.view.center_loca)) center = fleets.grids[np.argmin(distance)].location logger.warning( f'Assuming the nearest fleet to camera canter is current fleet: {location2node(center)}' ) else: logger.warning( f'Convert radar to local, but current fleet not found. ' f'Assuming camera center is current fleet: {location2node(self.view.center_loca)}' ) center = self.view.center_loca try: local = self.view[np.add(location, center)] except KeyError: logger.warning( f'Convert radar to local, but target grid not in local view. ' f'Assuming camera center is current fleet: {location2node(self.view.center_loca)}' ) center = self.view.center_loca local = self.view[np.add(location, center)] logger.info('Radar %s -> Local %s (fleet=%s)' % (str(location), location2node( local.location), location2node(center))) return local
def find_path_initial(self, grid=None): """ Args: grid (tuple, GridInfo): Current fleet grid """ if grid is None: grid = self.fleet_current else: grid = location_ensure(grid) if self.fleet_1_location: self.map[self.fleet_1_location].is_fleet = True if self.fleet_2_location: self.map[self.fleet_2_location].is_fleet = True self.map.find_path_initial(grid, has_ambush=self.config.MAP_HAS_AMBUSH)
def maze_active_on(self, grid): """ Args: grid: Returns: bool: If maze wall is on a the specific grid. """ if not self.config.MAP_HAS_MAZE: return False grid = self.map[location_ensure(grid)] if not grid.is_maze: return False return self.round % self.map.maze_round in grid.maze_round
def goto(self, location, optimize=True, expected=''): # self.device.sleep(1000) location = location_ensure(location) if (self.config.MAP_HAS_AMBUSH or self.config.MAP_HAS_FLEET_STEP or self.config.MAP_HAS_PORTAL) and optimize: nodes = self.map.find_path(location, step=self.fleet_step) for node in nodes: try: self._goto(node, expected=expected if node == nodes[-1] else '') except MapWalkError: logger.warning('Map walk error.') self.ensure_edge_insight() nodes_ = self.map.find_path(node, step=1) for node_ in nodes_: self._goto(node_, expected=expected if node == nodes[-1] else '') else: self._goto(location, expected=expected)
def goto(self, location, optimize=None, expected=''): """ Args: location (tuple, str, GridInfo): Destination. optimize (bool): Optimize walk path, reducing ambushes. If None, loads MAP_WALK_OPTIMIZE expected (str): Expected result on destination grid, such as 'combat', 'combat_siren', 'mystery'. Will give a waring if arrive with unexpected result. """ location = location_ensure(location) if optimize is None: optimize = self.config.MAP_WALK_OPTIMIZE # self.device.sleep(1000) if optimize and (self.config.MAP_HAS_AMBUSH or self.config.MAP_HAS_FLEET_STEP or self.config.MAP_HAS_PORTAL or self.config.MAP_HAS_MAZE): nodes = self.map.find_path(location, step=self.fleet_step) for node in nodes: if self.maze_active_on(node): logger.info( f'Maze is active on {location2node(node)}, bouncing to wait' ) for _ in range(10): grids = self.map[node].maze_nearby.delete( self.map.select(is_fleet=True)) if grids.select(is_enemy=False): grids = grids.select(is_enemy=False) grids = grids.sort('cost') self._goto(grids[0], expected='') try: self._goto(node, expected=expected if node == nodes[-1] else '') except MapWalkError: logger.warning('Map walk error.') self.predict() self.ensure_edge_insight() nodes_ = self.map.find_path(node, step=1) for node_ in nodes_: self._goto( node_, expected=expected if node == nodes[-1] else '') else: self._goto(location, expected=expected)
def focus_to(self, location, swipe_limit=(3, 2)): """Focus camera on a grid Args: location: grid swipe_limit(tuple): (x, y). Limit swipe in (-x, -y, x, y). """ location = location_ensure(location) logger.info('Focus to: %s' % location2node(location)) while 1: vector = np.array(location) - self.camera swipe = tuple( np.min([np.abs(vector), swipe_limit], axis=0) * np.sign(vector)) self.map_swipe(swipe) if np.all(np.abs(vector) <= 0): break
def focus_to(self, location, swipe_limit=(3, 2)): """Focus camera on a grid Args: location: grid swipe_limit(tuple): (x, y). Limit swipe in (-x, -y, x, y). """ location = location_ensure(location) logger.info('Focus to: %s' % location2node(location)) vector = np.array(location) - self.camera vector, sign = np.abs(vector), np.sign(vector) while 1: swipe = ( vector[0] if vector[0] < swipe_limit[0] else swipe_limit[0], vector[1] if vector[1] < swipe_limit[1] else swipe_limit[1]) self.map_swipe(tuple(sign * swipe)) vector -= swipe if np.all(np.abs(vector) <= 0): break
def _submarine_goto(self, location): """ Move submarine to given location. Args: location (tuple, str, GridInfo): Destination. Returns: bool: If submarine moved. Pages: in: SUBMARINE_MOVE_CONFIRM out: SUBMARINE_MOVE_CONFIRM """ location = location_ensure(location) moved = True while 1: self.in_sight(location, sight=self._walk_sight) self.focus_to_grid_center() grid = self.convert_global_to_local(location) grid.__str__ = location self.device.click(grid) arrived = False # Usually no need to wait arrive_timer = Timer(0.1, count=0) # If nothing happens, click again. walk_timeout = Timer(2, count=6).start() while 1: self.device.screenshot() self.view.update(image=self.device.image) # Arrive arrive_checker = grid.predict_submarine_move() if grid.predict_submarine() or (walk_timeout.reached() and grid.predict_fleet()): arrive_checker = True moved = False if arrive_checker: if not arrive_timer.started(): logger.info(f'Arrive {location2node(location)}') arrive_timer.start() if not arrive_timer.reached(): continue logger.info( f'Submarine arrive {location2node(location)} confirm.') if not moved: logger.info( f'Submarine already at {location2node(location)}') arrived = True break # End if walk_timeout.reached(): logger.warning('Walk timeout. Retrying.') self.predict() self.ensure_edge_insight(skip_first_update=False) break # End if arrived: break return moved
def _goto(self, location, expected=''): """Goto a grid directly and handle ambush, air raid, mystery picked up, combat. Args: location (tuple, str, GridInfo): Destination. """ location = location_ensure(location) siren_count = self.map.select(is_siren=True).count result_mystery = '' while 1: sight = self.map.camera_sight self.in_sight(location, sight=(sight[0], 0, sight[2], sight[3])) self.focus_to_grid_center() grid = self.convert_map_to_grid(location) self.ambush_color_initial() self.enemy_searching_color_initial() grid.__str__ = location result = 'nothing' self.device.click(grid) arrived = False # Wait to confirm fleet arrived. It does't appear immediately if fleet in combat . add = self.config.MAP_SIREN_MOVE_WAIT * min(self.config.MAP_SIREN_COUNT, siren_count) \ if self.config.MAP_HAS_MOVABLE_ENEMY and not self.config.ENABLE_FAST_FORWARD else 0 arrive_timer = Timer(0.3 + add) arrive_unexpected_timer = Timer(1.5 + add) # Wait after ambushed. ambushed_retry = Timer(0.5) # If nothing happens, click again. walk_timeout = Timer(20) walk_timeout.start() while 1: self.device.screenshot() grid.image = self.device.image # Ambush if self.handle_ambush(): ambushed_retry.start() # Mystery mystery = self.handle_mystery(button=grid) if mystery: self.mystery_count += 1 result = 'mystery' result_mystery = mystery # Combat if self.config.ENABLE_MAP_FLEET_LOCK and not self.is_in_map(): if self.handle_retirement(): self.map_offensive() walk_timeout.reset() if self.handle_combat_low_emotion(): walk_timeout.reset() if self.combat_appear(): self.combat( expected_end=self._expected_combat_end(expected), fleet_index=self.fleet_current_index) self.hp_get() if self.hp_withdraw_triggered(): self.withdraw() arrived = True if not self.config.MAP_HAS_MOVABLE_ENEMY else False result = 'combat' self.battle_count += 1 self.fleet_ammo -= 1 if 'siren' in expected: self.siren_count += 1 elif self.map[location].may_enemy: self.map[location].is_cleared = True self.handle_boss_appear_refocus() grid = self.convert_map_to_grid(location) walk_timeout.reset() # Cat attack animation if self.handle_map_cat_attack(): walk_timeout.reset() continue if self.handle_walk_out_of_step(): raise MapWalkError('walk_out_of_step') # Arrive if self.is_in_map() and \ (grid.predict_fleet() or (walk_timeout.reached() and grid.predict_current_fleet())): if not arrive_timer.started(): logger.info(f'Arrive {location2node(location)}') arrive_timer.start() arrive_unexpected_timer.start() if not arrive_timer.reached(): continue if expected and result not in expected: if arrive_unexpected_timer.reached(): logger.warning('Arrive with unexpected result') else: continue logger.info( f'Arrive {location2node(location)} confirm. Result: {result}. Expected: {expected}' ) arrived = True break # End if ambushed_retry.started() and ambushed_retry.reached(): break if walk_timeout.reached(): logger.warning('Walk timeout. Retrying.') self.ensure_edge_insight() break # End if arrived: # Ammo grid needs to click again, otherwise the next click doesn't work. if self.map[location].may_ammo: self.device.click(grid) break self.map[self.fleet_current].is_fleet = False self.map[location].wipe_out() self.map[location].is_fleet = True self.__setattr__('fleet_%s_location' % self.fleet_current_index, location) if result_mystery == 'get_carrier': prev_enemy = self.map.select(is_enemy=True) self.full_scan(is_carrier_scan=True) diff = self.map.select(is_enemy=True).delete(prev_enemy) logger.info(f'Carrier spawn: {diff}') self.find_path_initial()
def _goto(self, location, expected=''): """Goto a grid directly and handle ambush, air raid, mystery picked up, combat. Args: location (tuple, str, GridInfo): Destination. """ location = location_ensure(location) result_mystery = '' self.movable_before = self.map.select(is_siren=True) if self.hp_withdraw_triggered(): self.withdraw() is_portal = self.map[location].is_portal while 1: sight = self.map.camera_sight self.in_sight(location, sight=(sight[0], 0, sight[2], sight[3])) self.focus_to_grid_center() grid = self.convert_map_to_grid(location) self.ambush_color_initial() self.enemy_searching_color_initial() grid.__str__ = location result = 'nothing' self.device.click(grid) arrived = False # Wait to confirm fleet arrived. It does't appear immediately if fleet in combat . extra = 4.5 if self.config.SUBMARINE_MODE == 'hunt_only' else 0 arrive_timer = Timer(0.5 + self.round_wait + extra, count=2) arrive_unexpected_timer = Timer(1.5 + self.round_wait + extra, count=6) # Wait after ambushed. ambushed_retry = Timer(0.5) # If nothing happens, click again. walk_timeout = Timer(20) walk_timeout.start() while 1: self.device.screenshot() grid.image = np.array(self.device.image) if is_portal: self.update() grid = self.view[self.view.center_loca] # Combat if self.config.ENABLE_MAP_FLEET_LOCK and not self.is_in_map(): if self.handle_retirement(): self.map_offensive() walk_timeout.reset() if self.handle_combat_low_emotion(): walk_timeout.reset() if self.combat_appear(): self.combat( expected_end=self._expected_combat_end(expected), fleet_index=self.fleet_current_index) self.hp_get() arrived = True if not self.config.MAP_HAS_MOVABLE_ENEMY else False result = 'combat' self.battle_count += 1 self.fleet_ammo -= 1 if 'siren' in expected or ( self.config.MAP_HAS_MOVABLE_ENEMY and not expected): self.siren_count += 1 elif self.map[location].may_enemy: self.map[location].is_cleared = True self.handle_boss_appear_refocus() grid = self.convert_map_to_grid(location) walk_timeout.reset() # Ambush if self.handle_ambush(): self.hp_get() ambushed_retry.start() walk_timeout.reset() # Mystery mystery = self.handle_mystery(button=grid) if mystery: self.mystery_count += 1 result = 'mystery' result_mystery = mystery # Cat attack animation if self.handle_map_cat_attack(): walk_timeout.reset() continue if self.handle_walk_out_of_step(): raise MapWalkError('walk_out_of_step') # Arrive if self.is_in_map() and \ (grid.predict_fleet() or (walk_timeout.reached() and grid.predict_current_fleet())): if not arrive_timer.started(): logger.info(f'Arrive {location2node(location)}') arrive_timer.start() arrive_unexpected_timer.start() if not arrive_timer.reached(): continue if expected and result not in expected: if arrive_unexpected_timer.reached(): logger.warning('Arrive with unexpected result') else: continue if is_portal: location = self.map[location].portal_link self.camera = location logger.info( f'Arrive {location2node(location)} confirm. Result: {result}. Expected: {expected}' ) arrived = True break # Story if expected == 'story': if self.handle_story_skip(): result = 'story' continue # End if ambushed_retry.started() and ambushed_retry.reached(): break if walk_timeout.reached(): logger.warning('Walk timeout. Retrying.') self.ensure_edge_insight() break # End if arrived: # Ammo grid needs to click again, otherwise the next click doesn't work. if self.map[location].may_ammo: self.device.click(grid) break self.map[self.fleet_current].is_fleet = False self.map[location].wipe_out() self.map[location].is_fleet = True self.__setattr__('fleet_%s_location' % self.fleet_current_index, location) if result_mystery == 'get_carrier': self.full_scan_carrier() if result == 'combat': self.round_battle() self.round_next() if self.round_is_new: self.full_scan_movable(enemy_cleared=result == 'combat') self.find_path_initial() raise MapEnemyMoved self.find_path_initial()
def _goto(self, location, expected=''): """Goto a grid directly and handle ambush, air raid, mystery picked up, combat. Args: location (tuple, str, GridInfo): Destination. expected (str): Expected result on destination grid, such as 'combat', 'combat_siren', 'mystery'. Will give a waring if arrive with unexpected result. """ location = location_ensure(location) result_mystery = '' self.movable_before = self.map.select(is_siren=True) self.movable_before_normal = self.map.select(is_enemy=True) if self.hp_retreat_triggered(): self.withdraw() is_portal = self.map[location].is_portal # The upper grid is submarine, may mess up predict_fleet() may_submarine_icon = self.map.grid_covered(self.map[location], location=[(0, -1)]) may_submarine_icon = may_submarine_icon and self.fleet_submarine_location == may_submarine_icon[ 0].location while 1: self.fleet_ensure(self.fleet_current_index) self.in_sight(location, sight=self._walk_sight) self.focus_to_grid_center() grid = self.convert_global_to_local(location) self.ambush_color_initial() self.enemy_searching_color_initial() grid.__str__ = location result = 'nothing' self.device.click(grid) arrived = False # Wait to confirm fleet arrived. It does't appear immediately if fleet in combat. extra = 0 if self.config.Submarine_Mode == 'hunt_only': extra += 4.5 if self.config.MAP_HAS_LAND_BASED and grid.is_mechanism_trigger: extra += grid.mechanism_wait arrive_timer = Timer(0.5 + self.round_wait + extra, count=2) arrive_unexpected_timer = Timer(1.5 + self.round_wait + extra, count=6) # Wait after ambushed. ambushed_retry = Timer(0.5) # If nothing happens, click again. walk_timeout = Timer(20) walk_timeout.start() while 1: self.device.screenshot() self.view.update(image=self.device.image) if is_portal: self.update(allow_error=True) grid = self.view[self.view.center_loca] # Combat if self.config.Campaign_UseFleetLock and not self.is_in_map(): if self.handle_retirement(): self.map_offensive() walk_timeout.reset() if self.handle_combat_low_emotion(): walk_timeout.reset() if self.combat_appear(): self.combat(expected_end=self._expected_end(expected), fleet_index=self.fleet_show_index, submarine_mode=self._submarine_mode(expected)) self.hp_get() self.lv_get(after_battle=True) arrived = True if not self.config.MAP_HAS_MOVABLE_ENEMY else False result = 'combat' self.battle_count += 1 self.fleet_ammo -= 1 if 'siren' in expected or ( self.config.MAP_HAS_MOVABLE_ENEMY and not expected): self.siren_count += 1 elif self.map[location].may_enemy: self.map[location].is_cleared = True if self.catch_camera_repositioning(self.map[location]): self.handle_boss_appear_refocus() if self.config.MAP_FOCUS_ENEMY_AFTER_BATTLE: self.camera = location self.update() grid = self.convert_global_to_local(location) arrive_timer = Timer(0.5 + extra, count=2) arrive_unexpected_timer = Timer(1.5 + extra, count=6) walk_timeout.reset() if not (grid.predict_fleet() and grid.predict_current_fleet()): ambushed_retry.start() # Ambush if self.handle_ambush(): self.hp_get() self.lv_get(after_battle=True) walk_timeout.reset() self.view.update(image=self.device.image) if not (grid.predict_fleet() and grid.predict_current_fleet()): ambushed_retry.start() # Mystery mystery = self.handle_mystery(button=grid) if mystery: self.mystery_count += 1 result = 'mystery' result_mystery = mystery # Cat attack animation if self.handle_map_cat_attack(): walk_timeout.reset() continue # Guild popup # Usually handled in combat_status, but sometimes delayed until after battle on slow PCs. if self.handle_guild_popup_cancel(): walk_timeout.reset() continue if self.handle_walk_out_of_step(): raise MapWalkError('walk_out_of_step') # Arrive arrive_predict = '' arrive_checker = False if self.is_in_map(): if not may_submarine_icon and grid.predict_fleet(): arrive_predict = '(is_fleet)' arrive_checker = True elif may_submarine_icon and grid.predict_current_fleet(): arrive_predict = '(may_submarine_icon, is_current_fleet)' arrive_checker = True elif self.config.MAP_WALK_USE_CURRENT_FLEET \ and expected != 'combat_boss' \ and not ('combat' in expected and grid.may_boss) \ and (grid.predict_fleet() or grid.predict_current_fleet()): arrive_predict = '(MAP_WALK_USE_CURRENT_FLEET, is_current_fleet)' arrive_checker = True elif walk_timeout.reached() and grid.predict_current_fleet( ): arrive_predict = '(walk_timeout, is_current_fleet)' arrive_checker = True if arrive_checker: if not arrive_timer.started(): logger.info( f'Arrive {location2node(location)} {arrive_predict}' .strip()) arrive_timer.start() arrive_unexpected_timer.start() if result == 'nothing' and not arrive_timer.reached(): continue if expected and result not in expected: if arrive_unexpected_timer.reached(): logger.warning('Arrive with unexpected result') else: continue if is_portal: location = self.map[location].portal_link self.camera = location logger.info( f'Arrive {location2node(location)} confirm. Result: {result}. Expected: {expected}' ) arrived = True break else: if arrive_timer.started(): arrive_timer.reset() if arrive_unexpected_timer.started(): arrive_unexpected_timer.reset() # Story if expected == 'story': if self.handle_story_skip(): result = 'story' continue # End if ambushed_retry.started() and ambushed_retry.reached(): break if walk_timeout.reached(): logger.warning('Walk timeout. Retrying.') self.predict() self.ensure_edge_insight(skip_first_update=False) break # End if arrived: # Ammo grid needs to click again, otherwise the next click doesn't work. if self.map[location].may_ammo: self.device.click(grid) break self.map[self.fleet_current].is_fleet = False self.map[location].wipe_out() self.map[location].is_fleet = True self.__setattr__('fleet_%s_location' % self.fleet_current_index, location) if result_mystery == 'get_carrier': self.full_scan_carrier() if result == 'combat': self.round_battle(after_battle=True) self.predict() self.round_next() if self.round_is_new: if result != 'combat': self.predict() self.full_scan_movable(enemy_cleared=result == 'combat') self.find_path_initial() raise MapEnemyMoved if self.round_maze_changed: self.find_path_initial() raise MapEnemyMoved self.find_path_initial() if self.config.MAP_HAS_DECOY_ENEMY: if result == 'nothing' and expected == 'combat': raise MapEnemyMoved