def _conduct_sortie(self, sortie_map): if not self._validate_sortie_map(sortie_map): Log.log_warn(f"Map {sortie_map.value} is not available.") return False if cfg.config.combat.clear_stop and self._sortie_map_is_cleared: Log.log_msg(f"Map {sortie_map.value} has been cleared.") self.enabled = False return False self._select_world(sortie_map) time_to_rest = lbas.lbas.manage_lbas() if time_to_rest: Log.log_warn("LBAS is fatigued.") self.set_next_sortie_time(time_to_rest) return False self._select_map(sortie_map) if self._begin_sortie(): self.nodes_run = [] self.combat_nodes_run = [] self.rescued_ships = [] self.map_cleared = False sts.stats.combat.combat_sorties += 1 self._handle_combat(sortie_map) self._check_map_clear() if self.enabled: self.set_next_sortie_time() return True return False
def _get_ship_name_data(suffixes): """Method for retrieving and parsing the WCTF ship name data with appropriate suffixes. Args: suffixes (dict): suffix dict Returns: dict: dict of ship names """ db_res = requests.get(WCTF_DB_URL) name_db = {} if db_res.status_code == 200: db_split = db_res.text.split('\n') for line in db_split: line = line.strip() if not line: continue line_dict = JsonData.load_json_str(line) suffix_id = line_dict['name']['suffix'] jp_name = line_dict['name']['ja_jp'] non_jp_name = line_dict['name']['ja_romaji'].title() if suffix_id: jp_name += f" {suffixes[suffix_id]['jp']}" if non_jp_name: non_jp_name += f" {suffixes[suffix_id]['non_jp']}" name_db[line_dict['id']] = { 'jp': jp_name, 'non_jp': non_jp_name } else: Log.log_warn("Could not download WCTF ship data.") return name_db
def should_and_able_to_sortie(self): if not self.enabled or not self.time_to_sortie: return False if cfg.config.combat.port_check: if shp.ships.current_ship_count == shp.ships.max_ship_count: Log.log_msg("Port is full.") self.set_next_sortie_time(15) return False if cfg.config.combat.sortie_map.world == 'E': if shp.ships.current_ship_count >= shp.ships.max_ship_count - 5: Log.log_warn("Port is too full for event map.") self.set_next_sortie_time(15) return False if cfg.config.combat.check_fatigue: for fleet in flt.fleets.combat_fleets: if fleet.highest_fatigue > FatigueStateEnum.NO_FATIGUE: Log.log_warn("Combat fleet is fatigued.") self.set_next_sortie_time(49 - fleet.lowest_morale) return False for fleet in flt.fleets.combat_fleets: if fleet.under_repair: Log.log_warn("Combat fleet is under repair.") self.set_next_sortie_time(rep.repair.soonest_complete_time) return False if fleet.needs_repair: Log.log_warn("Combat fleet needs repair.") if rep.repair.docks_are_available: self.set_next_sortie_time() else: self.set_next_sortie_time(rep.repair.soonest_complete_time) return False if fleet.needs_resupply: Log.log_warn("Combat fleet needs resupply.") return False return True
def _get_suffix_data(): """Method for retrieving and parsing the WCTF suffix data needed to generate complete ship names. Returns: dict: suffix dict """ suf_res = requests.get(WCTF_SUFFIX_URL) suffixes = {} if suf_res.status_code == 200: suf_split = suf_res.text.split('\n') for line in suf_split: line = line.strip() if not line: continue line_dict = JsonData.load_json_str(line) suffixes[line_dict['id']] = { 'jp': line_dict['ja_jp'], 'non_jp': line_dict['ja_romaji'] } else: Log.log_warn("Could not download WCTF suffix data.") return suffixes
def find_all(asset, similarity): region = Region() Log.log_warn(f"Searching for {asset} with similarity {similarity}.") Log.log_warn("Results:") results = region.find_all(asset, similarity) for result in results: print(result)
def enabled(self, value): if type(value) is not bool: raise TypeError( f"Enabled setting for module {self.module_display_name} " "is not a bool.") if value is True: Log.log_success(f"{self.module_display_name} module enabled.") self.time_disabled = None else: Log.log_warn(f"{self.module_display_name} module disabled") self.time_disabled = datetime.now() self._enabled = value
def recovery_from_catbomb(cls, screen=Region(), catbomb_201=False): """Method that contains steps for recovery attempt from catbombs. If the catbomb_201 flag is set to True the recovery is attempt is far less aggressive to not aggravate the fairies any further. Includes incremental fallback for retry attempts. Args: screen (Region, optional): screen region. Defaults to Region(). catbomb_201 (bool, optional): whether or not this is a 201 catbomb. Defaults to False. Returns: bool: True if recovery was successful; False otherwise. """ catbomb_count = 0 if not catbomb_201 else 2 if catbomb_201: if sts.stats.recovery.catbomb_201_encountered > 0: Log.log_error( "Multiple 201 catbombs encountered. Shutting down kcauto.") return False else: kca_u.kca.sleep(300) while catbomb_count < 3: cls._refresh_screen(screen) if kca_u.kca.start_kancolle(): Log.log_success("Catbomb Recovery successful.") kca_u.kca.hook_chrome(port=cfg.config.general.chrome_dev_port) sts.stats.recovery.recoveries_done += 1 if catbomb_201: sts.stats.recovery.catbomb_201_encountered += 1 sts.stats.recovery.catbomb_201_recoveries_done += 1 else: sts.stats.recovery.catbomb_recoveries_done += 1 return True elif kca_u.kca.exists(screen, 'global|catbomb.png'): if catbomb_201: Log.log_error( "Persistent 201 catbomb. Shutting down kcauto.") return False catbomb_count += 1 # incremental backoff; 16, 64, then 256 seconds sleep_len = pow(4, catbomb_count + 1) Log.log_warn( f"Catbomb Recovery attempt {catbomb_count}. Sleeping for " f"{sleep_len} seconds before next recovery attempt.") sleep(sleep_len) else: return False return False
def attempt_recovery(cls): """Primary method that runs through all the various recovery options. Runs through basic recovery, result screen recovery, catbomb recovery, then Chrome tab crash recoveries. Typically run when there has been a generic Exception caused. Returns: bool: True if recovery was successful; False otherwise. """ Log.log_warn("Attempting recovery.") screen = Region() if cls.basic_recovery(): Log.log_success("Basic Recovery successful.") sts.stats.recovery.recoveries_done += 1 sts.stats.recovery.basic_recoveries_done += 1 return True if ( kca_u.kca.exists(screen, 'global|next.png') or kca_u.kca.exists(screen, 'global|next_alt.png')): Log.log_warn("Results screen detected.") if cls.recovery_from_results(screen): Log.log_success("Results Recovery successful.") sts.stats.recovery.recoveries_done += 1 sts.stats.recovery.results_recoveries_done += 1 return True return False if kca_u.kca.exists(screen, 'global|catbomb.png'): Log.log_warn("Catbomb detected.") if cls.recovery_from_catbomb(screen=screen): return True else: Log.log_error("Catbomb Recovery failed.") return False if kca_u.kca.exists(screen, 'global|chrome_crash.png'): Log.log_warn("Chrome Crash (Type 1) detected.") if cls.recovery_from_chrome_crash(screen, crash_type=1): return True visual_events = kca_u.kca.visual_hook.pop_messages() for event in visual_events: if event['method'] == 'Inspector.targetCrashed': Log.log_warn("Chrome Crash (Type 2) detected.") if cls.recovery_from_chrome_crash(screen, crash_type=2): return True
def _begin_sortie(self): if kca_u.kca.click_existing('lower_right', 'global|sortie_select.png'): kca_u.kca.r['top'].hover() kca_u.sleep(2) sortie_button_asset = 'combat|combat_start.png' if lbas.lbas.enabled and len(lbas.lbas.assignable_lbas_groups) > 0: sortie_button_asset = 'combat|combat_start_lbas.png' if kca_u.kca.click_existing('lower_right', sortie_button_asset): for fleet in flt.fleets.combat_fleets: Log.log_msg(fleet) Log.log_msg(fleet.detailed_fleet_status) Log.log_msg("Starting sortie.") return True Log.log_warn(f"Cannot start combat.") return False
def _resolve_continue_sortie_prompt(self): Log.log_debug("Resolve continue sortie prompt.") continue_sortie = True retreat_limit = cfg.config.combat.retreat_limit if self.current_node in cfg.config.combat.push_nodes: Log.log_msg(f"{self.current_node} is specified as a push node.") return True for fleet in flt.fleets.combat_fleets: if fleet.weakest_state >= retreat_limit: Log.log_warn( f"Fleet {fleet.fleet_id} has ships with " f"{retreat_limit.display_name} damage or above.") continue_sortie = False elif fleet.visual_health['heavy'] > 0: Log.log_warn( f"Fleet {fleet.fleet_id} has a critically damaged ship " "not calculated from the API.") continue_sortie = False if ( NodeEnum(self.current_node.name) in cfg.config.combat.retreat_points): Log.log_debug("Retreat specified in config.") continue_sortie = False if ( NodeEnum(len(self.combat_nodes_run)) in cfg.config.combat.retreat_points): Log.log_debug("Retreat specified combat # in config.") continue_sortie = False if continue_sortie: Log.log_msg("Continuing sortie.") kca_u.kca.click_existing( 'kc', 'combat|combat_continue.png', cached=True) else: Log.log_msg("Retreating from sortie.") kca_u.kca.click_existing( 'kc', 'combat|combat_retreat.png', cached=True) kca_u.kca.r['lbas'].hover() return continue_sortie
def _dispatch_expedition(self, fleet, expedition): if kca_u.kca.click_existing('lower_right', 'global|sortie_select.png'): fleet.select() kca_u.kca.r['top'].hover() if (fleet.needs_resupply and res.resupply.exp_provisional_enabled in (True, None)): res.resupply.exp_provisional_resupply(fleet) if fleet.needs_resupply: Log.log_warn(f"Fleet {fleet.fleet_id} needs resupply.") return False if kca_u.kca.click_existing('lower_right', 'expedition|expedition_dispatch.png'): result = api.api.update_from_api({KCSAPIEnum.EXPEDITION_START}) sts.stats.expedition.expeditions_sent += 1 fleet.at_base = False fleet.return_time = result[KCSAPIEnum.EXPEDITION_START.name][0] kca_u.kca.r['top'].hover() return True Log.log_warn(f"Fleet {fleet.fleet_id} is already away.") return False Log.log_warn(f"Expedition {expedition.expedition} already underway.") return False
def hook_health_check(self): """Method that runs through the different events reported to the api and visual hooks to ascertain whether or not the tab has crashed or was refreshed. Raises: ChromeCrashException: Chrome tab crash was detected. """ api_events = self.api_hook.pop_messages() visual_events = self.visual_hook.pop_messages() for event in api_events: if event['method'] == 'Inspector.detached': Log.log_warn("Chrome API hook is stale. Reconnecting.") self.hook_chrome(port=cfg.config.general.chrome_dev_port) return visual_events = self.visual_hook.pop_messages() for event in visual_events: if event['method'] == 'Page.frameDetached': Log.log_warn("Chrome visual hook is stale. Reconnecting.") self.hook_chrome(port=cfg.config.general.chrome_dev_port) return if event['method'] == 'Inspector.targetCrashed': Log.log_warn("Chrome crash detected.") raise ChromeCrashException
def _check_for_chrome_crash(self): visual_events = kca_u.kca.visual_hook.pop_messages() for event in visual_events: if event['method'] == 'Inspector.targetCrashed': Log.log_warn("Chrome Crash detected.") raise ChromeCrashException