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 _check_repair_sort(self): if not kca_u.kca.exists('upper_right', 'repair|repairlist_icon.png'): Log.log_msg("Changing repair list sort.") sort_button = Region(kca_u.kca.game_x + 1125, kca_u.kca.game_y + 155, 58, 26) while not kca_u.kca.exists('upper_right', 'repair|repairlist_icon.png'): sort_button.click()
def _click_quest_idx(self, idx): Log.log_debug(f"Clicking quest at position {idx}.") quest_list_region = Region(kca_u.kca.game_x + 230, kca_u.kca.game_y + 173 + (idx * 102), 830, 30) quest_list_region.click() api.api.update_from_api( {KCSAPIEnum.QUEST_LIST, KCSAPIEnum.QUEST_TURN_IN}, need_all=False) kca_u.kca.sleep(1)
def switch_fleet(self, context): preset_id = self._get_next_preset_id(context) Log.log_msg(f"Switching to Fleet Preset {preset_id}.") if preset_id not in self.presets: Log.log_error( f"Fleet Preset {preset_id} is not specified in-game. Please " f"check your config.") exit(1) list_idx = (preset_id if preset_id < 5 else 5) - 1 idx_offset = preset_id - 5 if idx_offset > 0: kca_u.kca.sleep() self._scroll_preset_list(idx_offset) kca_u.kca.r['top'].hover() kca_u.kca.sleep() preset_idx_region = Region(kca_u.kca.game_x + 410, kca_u.kca.game_y + 275 + (list_idx * 76), 70, 45) kca_u.kca.click_existing(preset_idx_region, 'fleetswitcher|fleetswitch_button.png') if kca_u.kca.exists('left', 'fleetswitcher|fleetswitch_fail_check.png'): Log.log_error( f"Could not switch in fleet preset {preset_id}. Please check " f"your config and fleet presets.") exit(1) Log.log_msg(f"Fleet Preset {preset_id} loaded.") if context == 'combat': self._set_next_combat_preset()
def _conduct_pvp(self, index): pvp_list_region = Region(kca_u.kca.game_x + 280, kca_u.kca.game_y + 268 + (index * 82), 795, 64) kca_u.kca.click(pvp_list_region) api_result = api.api.update_from_api({KCSAPIEnum.PVP_ENEMY_INFO}) formation, nb = self._get_formation_and_nb( api_result[KCSAPIEnum.PVP_ENEMY_INFO.name][0]) kca_u.kca.r['top'].hover() kca_u.kca.wait_and_click('lower', 'pvp|pvp_start_1.png') kca_u.kca.sleep() kca_u.kca.wait_and_click('lower', 'pvp|pvp_start_2.png') kca_u.kca.sleep() sts.stats.pvp.pvp_done += 1 kca_u.kca.wait_and_click(f'formation_{formation.value}', f'fleet|formation_{formation.value}.png') kca_u.kca.r['top'].hover() while not kca_u.kca.exists('lower_right_corner', 'global|next.png'): if nb: kca_u.kca.click_existing('kc', 'global|combat_nb_fight.png') else: kca_u.kca.click_existing('kc', 'global|combat_nb_retreat.png') kca_u.kca.sleep(2) while not kca_u.kca.exists('home_menu', 'nav|home_menu_sortie.png'): kca_u.kca.r['shipgirl'].click() kca_u.kca.sleep(1) Log.log_debug("PvP complete.") api.api.update_from_api({KCSAPIEnum.PORT})
def _change_page(region, target, offset): """Method that clicks on the arrow and page number navigation at the bottom of the ship list. 'first', 'prev', 'next', 'last' targets will click their respective arrow buttons, while an int target between 1 and 5 (inclusive) will click the page number at that position at the bottom of the page (left to right). Args: regions (dict): dict of regions target (str, int): specifies which navigation button to press offset (tuple): tuple of x, y pixel offsets as defined by offset_mode and the OFFSET class dictionary """ Log.log_debug(f"Changing to page {target} with {offset} offsets.") if target == 'first': kca_u.kca.click_existing( region, 'global|page_first.png', pad=PAGE_NAV) elif target == 'prev': kca_u.kca.click_existing( region, 'global|page_prev.png', pad=PAGE_NAV) elif target == 'next': kca_u.kca.click_existing( region, 'global|page_next.png', pad=PAGE_NAV) elif target == 'last': kca_u.kca.click_existing( region, 'global|page_last.png', pad=PAGE_NAV) elif 1 <= target <= 5: zero_target = target - 1 x_start = kca_u.kca.game_x + 735 + (zero_target * 53) + offset[0] y_start = kca_u.kca.game_y + 675 + offset[1] Region(x_start, y_start, 16, 15).click() kca_u.kca.sleep(0.5)
def _select_ship(self, idx, ship): page = (idx // 10) + 1 if idx > 9 else 1 Log.log_msg(f"Selecting lvl{ship.level} {ship.name} (pg{page}#{idx}).") if page > 1: tot_pages = shp.ships.current_ship_count // 10 list_control_region = Region(kca_u.kca.game_x + 610, kca_u.kca.game_y + 660, 490, 45) nav.navigate_list.to_page(list_control_region, tot_pages, self.current_repair_list_page, page, 'repair') self.current_repair_list_page = page repair_list_region = Region(kca_u.kca.game_x + 596, kca_u.kca.game_y + 194 + (idx % 10 * 46), 500, 31) kca_u.kca.click(repair_list_region) kca_u.kca.r['top'].hover() kca_u.kca.wait('right', 'repair|repair_confirm_1.png')
def _select_switch_button(self, slot_id): zero_idx = slot_id - 1 slot_button_region = Region( kca_u.kca.game_x + 550 + ((zero_idx % 2) * 513), kca_u.kca.game_y + 295 + ((zero_idx // 2) * 168), 125, 55) kca_u.kca.click_existing(slot_button_region, 'shipswitcher|shiplist_button.png') kca_u.kca.wait_vanish(slot_button_region, 'shipswitcher|shiplist_button.png') kca_u.kca.r['top'].hover()
def _select_replacement_ship(self, ship_idx, ship): target_page = (ship_idx // 10) + 1 if ship_idx > 9 else 1 Log.log_msg(f"Selecting lvl{ship.level} {ship.name} " f"(pg{target_page}#{ship_idx}).") if target_page > 1: tot_pages = shp.ships.current_ship_count // 10 list_control_region = Region(kca_u.kca.game_x + 625, kca_u.kca.game_y + 655, 495, 45) kca_u.kca.sleep(0.5) nav.navigate_list.to_page(list_control_region, tot_pages, self.current_shipcomp_page, target_page, 'shipcomp') self.current_shipcomp_page = target_page shipcomp_list_region = Region( kca_u.kca.game_x + 590, kca_u.kca.game_y + 225 + (ship_idx % 10 * 43), 435, 34) kca_u.kca.click(shipcomp_list_region) kca_u.kca.r['top'].hover() kca_u.kca.wait('lower_right', 'shipswitcher|shiplist_shipswitch_button.png')
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 _create_or_shift_region(self, key, x, y, w, h): """Helper method for generating or shifting an existing Region's x and y position. Args: key (str): region key. x (int): x position of upper-left corner of Region. y (int): y position of upper-left corner of Region. w (int): width of Region. h (int): height of Region. """ if key not in self.r or not isinstance(self.r[key], Region): self.r[key] = Region(x, y, w, h) else: self.r[key].shift_region(x, y)
def start_kancolle(self): """Method that attempts to start Kancolle from the game's splash screen. If starting from the splash screen, kcauto will load the data from the get_data api call. Otherwise, it will load the stored data from previous startups. """ screen = Region() if self.click_existing(screen, 'global|game_start.png'): Log.log_msg("Starting kancolle from splash screen.") api.api.update_from_api({ KCSAPIEnum.GET_DATA, KCSAPIEnum.REQUIRE_INFO, KCSAPIEnum.PORT}) self.wait(screen, 'nav|home_menu_sortie.png', 60) Log.log_success("Kancolle successfully started.") shp.ships.load_wctf_names(force_update=True) else: api.api.update_ship_library_from_json() self.sleep() return True
def recovery_from_chrome_crash(cls, screen=Region(), crash_type=1): """Method that contains steps for recovery from Chrome tab crashes. Args: screen (Region, optional): screen region. Defaults to Region(). crash_type (int, optional): type of Chrome tab crash encountered. Defaults to 1. Returns: bool: True if recovery was successful; False otherwise. """ cls._refresh_screen(screen) if kca_u.kca.start_kancolle(): Log.log_success("Chrome Crash Recovery successful.") sts.stats.recovery.recoveries_done += 1 if crash_type == 1: sts.stats.recovery.chrome_crash_t1_recoveries_done += 1 elif crash_type == 2: sts.stats.recovery.chrome_crash_t2_recoveries_done += 1 return True Log.log_error("Chrome Crash Recovery failed.") return False
def _select_expedition(self, expedition): kca_u.kca.sleep(0.1) expedition_list = self.available_expeditions_per_world[ expedition.world] index = expedition_list.index(expedition) offset = 0 if index >= self.NUM_VISIBLE_EXPEDITONS: if kca_u.kca.exists('lower_left', 'global|scroll_next.png'): self._scroll_list_down() offset = len(expedition_list) - self.NUM_VISIBLE_EXPEDITONS else: if kca_u.kca.exists('upper_left', 'global|scroll_prev.png'): self._scroll_list_up() true_index = index - offset if not 0 <= true_index < self.NUM_VISIBLE_EXPEDITONS: raise ValueError(f"Bad index {true_index}") expedition_list_region = Region( kca_u.kca.game_x + 190, kca_u.kca.game_y + 244 + (true_index * 45), 520, 35) kca_u.kca.click(expedition_list_region) kca_u.kca.r['top'].hover() kca_u.kca.sleep(0.5)
def find_kancolle(self): """Method that finds the Kancolle game on-screen and determine the UI being used as well as the position of the game. On first startup the method will look for all UIs until one is found; on subsequent runs it will first look for the last found UI. Generates or modifies pre-defined regions accordingly. Raises: FindFailed: could not find the game on-screen. """ Log.log_msg("Finding kancolle.") ref_r = None attempt = 0 screen = Region() # look for last-seen UI, if set if self.last_ui: try: ref_r = self.find(screen, f'global|kc_ref_point_{self.last_ui}.png', EXACT) except FindFailed: self.last_ui = None Log.log_debug("Last known UI not found.") # if last-seen UI was not found, or if kcauto is in first start while not ref_r: try: ref_r = self.find(screen, 'global|kc_ref_point_1.png', EXACT) self.last_ui = 1 Log.log_debug("Using UI 1 or 2") break except FindFailed: Log.log_debug("Not using UI 1 or 2") try: ref_r = self.find(screen, 'global|kc_ref_point_2.png', EXACT) self.last_ui = 2 Log.log_debug("Using UI 3") break except FindFailed: Log.log_debug("Not using UI 3") try: ref_r = self.find(screen, 'global|kc_ref_point_3.png', EXACT) self.last_ui = 3 Log.log_debug("Using UI 4 or 5") break except FindFailed: Log.log_debug("Not using UI 4 or 5") attempt += 1 sleep(1) if attempt > 3: Log.log_error("Could not find Kancolle reference point.") raise FindFailed() new_game_x = ref_r.x - 144 new_game_y = ref_r.y Log.log_debug(f"Game X:{new_game_x}, Y:{new_game_y}") # define click callback as needed if not arg.args.parsed_args.no_click_track: ImageMatch.click_callback = clt.click_tracker.track_click # define click and hover method overrides as needed if (cfg.config.general.interaction_mode is InteractionModeEnum.CHROME_DRIVER): ImageMatch.override_click_method = self._override_click_method ImageMatch.override_hover_method = self._override_hover_method if new_game_x != self.game_x or new_game_y != self.game_y: if not self.game_x or self.game_y: Log.log_success("Game found. Initializing regions.") else: Log.log_msg("Game has moved. Shifting regions.") self.game_x = new_game_x self.game_y = new_game_y self._update_regions() return True
def select(self): kca_u.kca.click( Region(kca_u.kca.game_x + self.x - 3, kca_u.kca.game_y + self.y - 3, 7, 7))