def testCanAddLocationToRegion(self): region = Region(100, 100, 1, 1) # Check that initialized properly self.assertEqual(region.getX(), 100) self.assertEqual(region.getY(), 100) self.assertEqual(region.getW(), 1) self.assertEqual(region.getH(), 1) region = region.add(Location(50, 50)) # X,Y Should have changed to 50 self.assertEqual(region.getX(), 50) self.assertEqual(region.getY(), 50) # W,H should have changed to 50 self.assertEqual(region.getW(), 51) self.assertEqual(region.getH(), 51) region = region.add(Location(200, 50)) # Width/Height should have changed to 200/200 self.assertEqual(region.getX(), 50) self.assertEqual(region.getY(), 50) self.assertEqual(region.getW(), 150) self.assertEqual(region.getH(), 51) region = region.add(Location(200, 200)) # Width/Height should have changed to 200/200 self.assertEqual(region.getX(), 50) self.assertEqual(region.getY(), 50) self.assertEqual(region.getW(), 150) self.assertEqual(region.getH(), 150)
def wait_and_click_and_wait( cls, click_region, click_target, wait_region, wait_target, time=10, expand=[]): """Method to wait for the appearance of an image match, click it, then wait for another subsequent image match. Args: click_region (Region): Region to conduct the initial match in click_target (str, Pattern): the filename of the asset or Pattern to search for the initial click wait_region (Region): Region to conduct the second match in wait_target (str, Pattern): the filename of the asset or Pattern to search for in the second wait check time (int, optional): max amount of time to wait for assets to appear expand (list, optional): area expansion for the click """ click_region.wait(click_target, time) if Globals.SLEEP_MODIFIER: cls.kc_sleep() click_region.click( cls.generate_pattern(click_region, click_target, expand, True)) try: wait_region.wait(wait_target, time) except FindFailed: # the initial click might have failed due to lag within the client; # rejigger the mouse and retry the click if this happens click_region.mouseMove(Location(1, 1)) cls.wait_and_click_and_wait( click_region, click_target, wait_region, wait_target, time, expand) cls.kc_sleep()
def click_node(self, kc_region): rand_x = kc_region.x + randint(self.coords[0] - 5, self.coords[0] + 5) rand_y = kc_region.y + randint(self.coords[1] - 5, self.coords[1] + 5) kc_region.mouseMove(Location(rand_x, rand_y)) kc_region.mouseDown(Button.LEFT) Util.kc_sleep() kc_region.mouseUp(Button.LEFT)
def testSetOffset(self): region = Region(100, 100, 100, 100) region = region.offset(Location(100, 100)) self.assertEqual(region.getX(), 200) self.assertEqual(region.getY(), 200) self.assertEqual(region.getW(), 100) self.assertEqual(region.getH(), 100)
def _pick_fleet_ship(self): """Method to click a fleet ship based on the fleet icons displayed next to the ship in the ship list. Returns: boolean: True if a fleet ship was chosen and clicked, False otherwise """ Util.log_msg("Picking damaged ship from combat fleet(s) to repair.") # generate markers for fleets to repair fleet_markers = [] if self.config.combat['fleet_mode'] == 'striking': fleet_markers = ['repairlist_fleet_3.png'] else: fleet_markers = [ 'repairlist_fleet_1_flag.png', 'repairlist_fleet_1.png' ] if self.config.combat['combined_fleet']: fleet_markers.append('repairlist_fleet_2.png') # get damages to repair valid_damages = CombatFleet.get_damages_at_threshold( self.config.combat['repair_limit']) for fleet_marker in fleet_markers: fleet_id = int(fleet_marker[17]) # infer from filename fleet_instance = self.fleets[fleet_id] if fleet_instance.get_damage_counts_at_threshold( self.config.combat['repair_limit']) == 0: # if this fleet has no longer has ships that need repair, # don't search for its marker continue ship_matches = Util.findAll_wrapper( self.regions['repair_shiplist_fleet_markers'], Pattern(fleet_marker).similar(0.9)) for ship_match in ship_matches: target_region = ship_match.offset(Location(342, 0)).nearby(5) for damage in valid_damages: if fleet_instance.damage_counts[damage] == 0: # if no ships in this fleet are at this damage state, # don't search for it continue damage_icon = 'repairlist_dmg_{}.png'.format(damage) if Util.check_and_click(target_region, damage_icon, Globals.EXPAND['repair_list']): fleet_instance.damage_counts[damage] -= 1 fleet_instance.damage_counts['repair'] += 1 return True return False
def testSetClickOffset(self): region = Region(100, 100, 100, 100) region.setClickOffset(Location(10, 10)) # Check that it is set right location = region.getClickOffset() self.assertEqual(location.getX(), 10) self.assertEqual(location.getY(), 10) location = region.getClickLocation() self.assertEqual(location.getX(), 160.0) self.assertEqual(location.getY(), 160.0)
def click_coords(cls, region, x, y): """Method to move the mouse to the specified x,y coordinates and simulate clicking the mouse on the location. Args: region (Region): sikuli Region instance containing the last known location of the Kantai Collection game screen x (int): x-coords in pixels, relative to upper-left corner of game y (int): y-coords in pixels, relative to upper-left corner of game """ # offset x,y coords relative to upper-left corner of game to upper-left # corner of screen offset_x = region.x + x offset_y = region.y + y # move the mouse to offset location region.mouseMove(Location(offset_x, offset_y)) cls.click_mouse(region)
def rejigger_mouse(cls, regions, preset): """Method to move the mouse to a random X,Y coordinate in the specified preset region. Args: regions (dict): dict of pre-defined kcauto regions preset (str): name of preset-area to move the mouse to """ # preset areas are designated as (X_start, X_end, Y_start, Y_end) presets = { 'game': (0, Globals.GAME_WIDTH, 0, Globals.GAME_HEIGHT), 'center': (225, 975, 180, 540), 'top': (400, 1200, 12, 42), 'quest_menu': (790, 895, 50, 95), 'shipgirl': (700, 1100, 130, 550), 'lbas': (500, 700, 5, 50), 'lbas_mode_switch_button': (1150, 1175, 210, 265), '7th_next': (386, 413, 400, 427) # NU } if isinstance(preset, str): x1, x2, y1, y2 = presets[preset] # max bounds temp_screen = Screen().getBounds() max_x = temp_screen.width max_y = temp_screen.height rand_x = regions['game'].x + cls.random_coord(x1, x2) rand_y = regions['game'].y + cls.random_coord(y1, y2) rand_x = max_x - 1 if rand_x > max_x else rand_x rand_y = max_y - 1 if rand_y > max_y else rand_y elif (isinstance(preset, Region) or isinstance(preset, JRegion) or isinstance(preset, Match) or isinstance(preset, JMatch)): rand_x = cls.random_coord(preset.x, preset.x + preset.w) rand_y = cls.random_coord(preset.y, preset.y + preset.h) regions['game'].mouseMove(Location(rand_x, rand_y))
def rejigger_mouse(cls, regions, preset): """Method to move the mouse to a random X,Y coordinate in the specified preset region. Args: regions (dict): dict of pre-defined kcauto-kai regions preset (str): name of preset-area to move the mouse to """ # preset areas are designated as (X_start, X_end, Y_start, Y_end) presets = { 'game': (0, 800, 0, 480), 'center': (150, 650, 130, 350), 'top': (120, 780, 5, 25), 'shipgirl': (370, 780, 100, 420), 'lbas': (350, 450, 5, 50), 'lbas_mode_switch_button': (763, 788, 137, 179), '7th_next': (386, 413, 400, 427) } if isinstance(preset, str): x1, x2, y1, y2 = presets[preset] # max bounds temp_screen = Screen().getBounds() max_x = temp_screen.width max_y = temp_screen.height rand_x = regions['game'].x + cls.randint_gauss(x1, x2) rand_y = regions['game'].y + cls.randint_gauss(y1, y2) rand_x = max_x - 1 if rand_x > max_x else rand_x rand_y = max_y - 1 if rand_y > max_y else rand_y elif (isinstance(preset, Region) or isinstance(preset, JRegion) or isinstance(preset, Match) or isinstance(preset, JMatch)): rand_x = cls.randint_gauss(preset.x, preset.x + preset.w) rand_y = cls.randint_gauss(preset.y, preset.y + preset.h) regions['game'].mouseMove(Location(rand_x, rand_y))
def recover(kcauto_kai, e): """Attempts very basic recovery actions on a FindFailed exception. WIP and does not integrate with the config. Args: kcauto_kai (KCAutoKai): KCAutoKai instance e (Exception): Exception Returns: bool: True on successful recovery, otherwise raises an error """ kc_region = kcauto_kai.kc_region regions = kcauto_kai.regions recovery_method = 'kc3' Util.log_warning( "FindFailed error occurred; attempting basic recovery.") App.focus(kcauto_kai.config.program) kc_region.mouseMove(Location(1, 1)) # basic recovery attempt type(Key.ESC) sleep(1) if kc_region.exists(Pattern('kc_reference_point.png').exact()): # reference point exists, so we are in-game Util.log_success("Recovery successful.") kcauto_kai.stats.increment_recoveries() return True elif kc_region.exists('next.png'): # crashed at some results screen; try to click it away until we see # the main game screen while (kc_region.exists('next.png') and not kc_region.exists( Pattern('kc_reference_point.png').exact())): Util.click_screen(regions, 'center') sleep(2) if kc_region.exists(Pattern('kc_reference_point.png').exact()): # reference point exists, so we are back in-game Util.log_success("Recovery successful.") kcauto_kai.stats.increment_recoveries() return True # catbomb recovery if kc_region.exists('catbomb.png') and recovery_method != 'None': if recovery_method == 'browser': Region.type(Key.F5) elif recovery_method == 'kc3': Region.type(Key.F5) sleep(1) Region.type(Key.SPACE) sleep(1) Region.type(Key.TAB) sleep(1) Region.type(Key.SPACE) elif recovery_method == 'kcv': Region.type(Key.F5) elif recovery_method == 'kct': Region.type(Key.ALT) sleep(1) Region.type(Key.DOWN) sleep(1) Region.type(Key.DOWN) sleep(1) Region.type(Key.ENTER) elif recovery_method == 'eo': Region.type(Key.F5) sleep(1) Region.type(Key.TAB) sleep(1) Region.type(Key.SPACE) sleep(3) kc_region.mouseMove(Location(0, 0)) sleep(3) Util.wait_and_click(kc_region, Pattern('game_start.png').similar(0.999), 60) sleep(5) Util.log_success("Recovery successful.") kcauto_kai.stats.increment_recoveries() return True # recovery failed Util.log_error("Irrecoverable crash") print(e) raise
def recover(kcauto_kai, config, e): """Attempts very basic recovery actions on a FindFailed exception. WIP and does not integrate with the config. Args: kcauto_kai (KCAutoKai): KCAutoKai instance config (Config): Config instance e (Exception): Exception Returns: bool: True on successful recovery, otherwise raises an error """ kc_region = (kcauto_kai.kc_region if kcauto_kai.kc_region else Util.focus_kc(config)) kc_region = kcauto_kai.kc_region regions = kcauto_kai.regions Util.log_warning(e) Util.log_warning( "** FindFailed error occurred; attempting basic recovery. **") App.focus(kcauto_kai.config.program) kc_region.mouseMove(Location(1, 1)) # basic recovery attempt Region.type(kc_region, Key.ESC) sleep(1) Region.type(kc_region, Key.SPACE) if kc_region.exists(Pattern('kc_reference_point.png').exact()): # reference point exists, so we are in-game Util.log_success("Recovery successful.") kcauto_kai.stats.increment_recoveries() return True elif kc_region.exists('next.png'): # crashed at some results screen; try to click it away until we see # the main game screen while (kc_region.exists('next.png') and not kc_region.exists( Pattern('kc_reference_point.png').exact())): Util.click_preset_region(regions, 'center') sleep(2) if kc_region.exists(Pattern('kc_reference_point.png').exact()): # reference point exists, so we are back in-game Util.log_success("Recovery successful.") kcauto_kai.stats.increment_recoveries() return True # catbomb recovery if kc_region.exists('catbomb.png', 10): Util.log_warning("** Catbomb detected. **") catbombed = True catbomb_n = 0 while catbombed and catbomb_n < 7: # generic f5-space-tab-space keystrokes to mimick refresh # attempt Region.type(kc_region, Key.F5) sleep(1) Region.type(kc_region, Key.SPACE) sleep(1) Region.type(kc_region, Key.TAB) sleep(1) Region.type(kc_region, Key.SPACE) sleep(3) # clear mouse kc_region.mouseMove(Location(1, 1)) if kc_region.exists('catbomb.png'): sleep_len = pow(2, catbomb_n + 4) Util.log_warning( "Catbomb recovery attempt {} failed; trying again in " "{} seconds!".format(catbomb_n + 1, sleep_len)) sleep(sleep_len) catbomb_n += 1 else: catbombed = False sleep(3) Util.wait_and_click(kc_region, Pattern('game_start.png').similar(0.999), 60) sleep(5) Util.log_success("Catbomb recovery successful.") kcauto_kai.stats.increment_recoveries() return True # recovery failed Util.log_error("** Irrecoverable crash. **") print(e) raise
def test_moveTo(self): r = Rectangle(0, 10, 20, 30) r.moveTo(Location(5, 15)) self.assertEqual((r.x, r.y, r.w, r.h), (5, 15, 20, 30))
def test_getCenter(self): r = Rectangle(0, 10, 20, 30) self.assertEqual(r.getCenter(), Location(10, 25))
def _pick_fleet_ship(self): """Method to click a fleet ship based on the fleet icons displayed next to the ship in the ship list. Returns: boolean: True if a fleet ship was chosen and clicked, False otherwise """ Util.log_msg("Picking damaged ship from combat fleet(s) to repair.") # generate markers for fleets to repair fleet_markers = [] if self.config.combat['fleet_mode'] == 'striking': fleet_markers = ['repairlist_fleet_3.png'] else: fleet_markers = [ 'repairlist_fleet_1_flag.png', 'repairlist_fleet_1.png' ] if self.config.combat['combined_fleet']: fleet_markers.append('repairlist_fleet_2.png') # get damages to repair valid_damages = CombatFleet.get_damages_at_threshold( self.config.combat['repair_limit']) while self.current_shiplist_page <= self.ship_page_count: for fleet_marker in fleet_markers: fleet_id = int(fleet_marker[17]) # infer from filename fleet_instance = self.fleets[fleet_id] if fleet_instance.get_damage_counts_at_threshold( self.config.combat['repair_limit']) == 0: # if this fleet has no longer has ships that need repair, # don't search for its marker continue ship_matches = Util.findAll_wrapper( self.module_regions['repair_shiplist_fleet_markers'], Pattern(fleet_marker).similar(0.9)) for ship_match in ship_matches: target_region = ship_match.offset(Location(525, 0)).nearby(10) for damage in valid_damages: if fleet_instance.damage_counts[damage] == 0: # if no ships in this fleet are at this damage # state, don't search for it continue damage_icon = 'repairlist_dmg_{}.png'.format(damage) if Util.check_and_click(target_region, damage_icon, Globals.EXPAND['repair_list']): fleet_instance.damage_counts[damage] -= 1 fleet_instance.damage_counts['repair'] += 1 return True if self.current_shiplist_page < self.ship_page_count: # check if there were even any damaged ships on the page damage_exists = False for damage in valid_damages: damage_icon = 'repairlist_dmg_{}.png'.format(damage) if self.regions['right'].exists(damage_icon): damage_exists = True break if damage_exists: # did not select a ship but damaged ships exist; go to next # page self._navigate_to_shiplist_page( self.current_shiplist_page + 1) else: # no more ships of valid damage state exists in list; # return to the first page self._navigate_to_shiplist_page(1) return False return False