mirror of
https://github.com/LmeSzinc/AzurLaneAutoScript.git
synced 2025-01-09 09:37:41 +08:00
Add: Attack abyssal boss
This commit is contained in:
parent
1290224c50
commit
caf51858e9
@ -147,6 +147,7 @@ class ManualConfig:
|
||||
HOMO_CORNER_THRESHOLD = 0.8
|
||||
HOMO_RECTANGLE_THRESHOLD = 10
|
||||
|
||||
HOMO_EDGE_DETECT = True
|
||||
HOMO_EDGE_HOUGHLINES_THRESHOLD = 120
|
||||
HOMO_EDGE_COLOR_RANGE = (0, 24)
|
||||
# ((x, y), [upper-left, upper-right, bottom-left, bottom-right])
|
||||
|
@ -126,14 +126,23 @@ class FastForwardHandler(AutoSearchHandler):
|
||||
changed = fast_forward.set(status=status, main=self)
|
||||
return changed
|
||||
|
||||
def handle_map_fleet_lock(self):
|
||||
def handle_map_fleet_lock(self, enable=None):
|
||||
"""
|
||||
Args:
|
||||
enable (bool): Default to None, use Campaign_UseFleetLock.
|
||||
|
||||
Returns:
|
||||
bool: If switched.
|
||||
"""
|
||||
# Fleet lock depends on if it appear on map, not depends on map status.
|
||||
# Because if already in map, there's no map status,
|
||||
if not fleet_lock.appear(main=self):
|
||||
logger.info('No fleet lock option.')
|
||||
return False
|
||||
|
||||
status = 'on' if self.config.Campaign_UseFleetLock else 'off'
|
||||
if enable is None:
|
||||
enable = self.config.Campaign_UseFleetLock
|
||||
status = 'on' if enable else 'off'
|
||||
changed = fleet_lock.set(status=status, main=self)
|
||||
|
||||
return changed
|
||||
|
@ -12,7 +12,7 @@ def location_ensure(location):
|
||||
Returns:
|
||||
tuple(int): Location, such as (4, 3)
|
||||
"""
|
||||
if isinstance(location, GridInfo):
|
||||
if hasattr(location, 'location'):
|
||||
return location.location
|
||||
elif isinstance(location, str):
|
||||
return node2location(location)
|
||||
|
@ -43,9 +43,29 @@ class GridPredictor:
|
||||
self.homo_invt = cv2.invert(self.homo_data)[1]
|
||||
|
||||
def screen2grid(self, points):
|
||||
"""
|
||||
Args:
|
||||
points (np.ndarray): Coordinates from screen, [[x1, y1], [x2, y2], ...]
|
||||
|
||||
Returns:
|
||||
np.ndarray: Coordinates from sea surface, [[x1, y1], [x2, y2], ...]
|
||||
Coordinate zero point is the upper-left corner.
|
||||
(0, 0) +------+
|
||||
| |
|
||||
| |
|
||||
+------+ (1, 1)
|
||||
"""
|
||||
return perspective_transform(points, self.homo_data) / self.config.HOMO_TILE
|
||||
|
||||
def grid2screen(self, points):
|
||||
"""
|
||||
Args:
|
||||
points (np.ndarray): Coordinates from sea surface, [[x1, y1], [x2, y2], ...]
|
||||
See Also screen2grid().
|
||||
|
||||
Returns:
|
||||
np.ndarray: Coordinates from screen, [[x1, y1], [x2, y2], ...]
|
||||
"""
|
||||
return perspective_transform(np.multiply(points, self.config.HOMO_TILE), self.homo_invt)
|
||||
|
||||
@cached_property
|
||||
|
@ -188,10 +188,13 @@ class Homography:
|
||||
self.homo_loca %= self.config.HOMO_TILE
|
||||
|
||||
# Detect map edges
|
||||
image_edge = cv2.bitwise_and(cv2.dilate(image_edge, kernel),
|
||||
cv2.inRange(image_trans, *self.config.HOMO_EDGE_COLOR_RANGE))
|
||||
image_edge = cv2.bitwise_and(image_edge, self.ui_mask_homo_stroke)
|
||||
self.detect_edges(image_edge, hough_th=self.config.HOMO_EDGE_HOUGHLINES_THRESHOLD)
|
||||
self.lower_edge, self.upper_edge, self.left_edge, self.right_edge = False, False, False, False
|
||||
self._map_edge_count = (0, 0)
|
||||
if self.config.HOMO_EDGE_DETECT:
|
||||
image_edge = cv2.bitwise_and(cv2.dilate(image_edge, kernel),
|
||||
cv2.inRange(image_trans, *self.config.HOMO_EDGE_COLOR_RANGE))
|
||||
image_edge = cv2.bitwise_and(image_edge, self.ui_mask_homo_stroke)
|
||||
self.detect_edges(image_edge, hough_th=self.config.HOMO_EDGE_HOUGHLINES_THRESHOLD)
|
||||
|
||||
# Log
|
||||
time_cost = round(time.time() - start_time, 3)
|
||||
|
@ -2,7 +2,6 @@ import numpy as np
|
||||
|
||||
from module.base.button import Button
|
||||
from module.base.decorator import cached_property
|
||||
from module.base.timer import Timer
|
||||
from module.exception import MapDetectionError
|
||||
from module.logger import logger
|
||||
from module.map.camera import Camera
|
||||
@ -36,12 +35,12 @@ class OSCamera(OSMapOperation, Camera):
|
||||
"""
|
||||
return Radar(self.config)
|
||||
|
||||
def update_radar(self):
|
||||
def predict_radar(self):
|
||||
"""
|
||||
Scan radar and merge it into map
|
||||
"""
|
||||
self.radar.predict(self.device.image)
|
||||
self.map.update(self.radar, camera=self.fleet_current)
|
||||
self.radar.show()
|
||||
|
||||
def grid_is_in_sight(self, grid, camera=None, sight=None):
|
||||
location = location_ensure(grid)
|
||||
@ -154,33 +153,6 @@ class OSCamera(OSMapOperation, Camera):
|
||||
logger.info('Radar %s -> Local %s (fleet=%s)' % (
|
||||
str(location),
|
||||
location2node(local.location),
|
||||
location2node(self.view.center_loca)
|
||||
location2node(center)
|
||||
))
|
||||
return local
|
||||
|
||||
def wait_until_camera_stable(self, skip_first_screenshot=True):
|
||||
"""
|
||||
Wait until homo_loca stabled.
|
||||
DETECTION_BACKEND must be 'homography'.
|
||||
"""
|
||||
logger.info('Wait until camera stable')
|
||||
record = None
|
||||
confirm_timer = Timer(0.3, count=0).start()
|
||||
while 1:
|
||||
if skip_first_screenshot:
|
||||
skip_first_screenshot = False
|
||||
else:
|
||||
self.device.screenshot()
|
||||
|
||||
self.update_os()
|
||||
self.view.predict()
|
||||
current = tuple(self.view.backend.homo_loca.tolist())
|
||||
if record is None or (current is not None and current == record):
|
||||
if confirm_timer.reached():
|
||||
break
|
||||
else:
|
||||
confirm_timer.reset()
|
||||
|
||||
record = current
|
||||
|
||||
logger.info('Camera stabled')
|
||||
|
@ -4,6 +4,7 @@ class OSConfig:
|
||||
"""
|
||||
MAP_FOCUS_ENEMY_AFTER_BATTLE = True
|
||||
MAP_HAS_SIREN = True
|
||||
MAP_HAS_FLEET_STEP = True
|
||||
IGNORE_LOW_EMOTION_WARN = False
|
||||
|
||||
MAP_GRID_CENTER_TOLERANCE = 0.2
|
||||
@ -28,6 +29,7 @@ class OSConfig:
|
||||
INTERNAL_LINES_HOUGHLINES_THRESHOLD = 75
|
||||
EDGE_LINES_HOUGHLINES_THRESHOLD = 75
|
||||
|
||||
HOMO_EDGE_DETECT = False
|
||||
HOMO_CANNY_THRESHOLD = (50, 50)
|
||||
|
||||
MAP_ENEMY_GENRE_DETECTION_SCALING = {
|
||||
|
@ -5,6 +5,8 @@ from module.exception import MapWalkError
|
||||
from module.logger import logger
|
||||
from module.map.fleet import Fleet
|
||||
from module.map.map_grids import SelectedGrids
|
||||
from module.map.utils import location_ensure
|
||||
from module.map_detection.utils import *
|
||||
from module.os.assets import TEMPLATE_EMPTY_HP
|
||||
from module.os.camera import OSCamera
|
||||
from module.os.map_base import OSCampaignMap
|
||||
@ -12,10 +14,15 @@ from module.os_ash.ash import OSAsh
|
||||
from module.os_combat.combat import Combat
|
||||
|
||||
|
||||
def limit_walk(location, step=3):
|
||||
x, y = location
|
||||
return min(abs(x), step - abs(y)) * x // abs(x), y
|
||||
|
||||
|
||||
class OSFleet(OSCamera, Combat, Fleet, OSAsh):
|
||||
def _goto(self, location, expected=''):
|
||||
super()._goto(location, expected)
|
||||
self.update_radar()
|
||||
self.predict_radar()
|
||||
self.map.show()
|
||||
|
||||
if self.handle_ash_beacon_attack():
|
||||
@ -165,6 +172,97 @@ class OSFleet(OSCamera, Combat, Fleet, OSAsh):
|
||||
center = self.camera
|
||||
return SelectedGrids(sea).sort_by_camera_distance(center)
|
||||
|
||||
def wait_until_camera_stable(self, skip_first_screenshot=True):
|
||||
"""
|
||||
Wait until homo_loca stabled.
|
||||
DETECTION_BACKEND must be 'homography'.
|
||||
"""
|
||||
logger.hr('Wait until camera stable')
|
||||
record = None
|
||||
confirm_timer = Timer(0.3, count=0).start()
|
||||
while 1:
|
||||
if skip_first_screenshot:
|
||||
skip_first_screenshot = False
|
||||
else:
|
||||
self.device.screenshot()
|
||||
|
||||
self.update_os()
|
||||
current = self.view.backend.homo_loca
|
||||
logger.attr('homo_loca', current)
|
||||
if record is None or (current is not None and np.linalg.norm(np.subtract(current, record)) < 3):
|
||||
if confirm_timer.reached():
|
||||
break
|
||||
else:
|
||||
confirm_timer.reset()
|
||||
|
||||
record = current
|
||||
|
||||
logger.info('Camera stabled')
|
||||
|
||||
def wait_until_walk_stable(self, skip_first_screenshot=False):
|
||||
"""
|
||||
Wait until homo_loca stabled.
|
||||
DETECTION_BACKEND must be 'homography'.
|
||||
|
||||
Raises:
|
||||
MapWalkError: If unable to goto such grid.
|
||||
"""
|
||||
logger.hr('Wait until walk stable')
|
||||
record = None
|
||||
enemy_searching_appear = False
|
||||
|
||||
confirm_timer = Timer(0.8, count=2).start()
|
||||
while 1:
|
||||
if skip_first_screenshot:
|
||||
skip_first_screenshot = False
|
||||
else:
|
||||
self.device.screenshot()
|
||||
|
||||
# Map event
|
||||
if self.handle_map_event():
|
||||
confirm_timer.reset()
|
||||
continue
|
||||
if self.handle_walk_out_of_step():
|
||||
raise MapWalkError('walk_out_of_step')
|
||||
|
||||
# Enemy searching
|
||||
if not enemy_searching_appear and self.enemy_searching_appear():
|
||||
enemy_searching_appear = True
|
||||
confirm_timer.reset()
|
||||
continue
|
||||
else:
|
||||
if enemy_searching_appear:
|
||||
self.handle_enemy_flashing()
|
||||
self.device.sleep(0.3)
|
||||
logger.info('Enemy searching appeared.')
|
||||
enemy_searching_appear = False
|
||||
if self.is_in_map():
|
||||
self.enemy_searching_color_initial()
|
||||
|
||||
# Combat
|
||||
if self.combat_appear():
|
||||
# Use ui_back() for testing, because there are too few abyssal loggers every month.
|
||||
# self.ui_back(check_button=self.is_in_map)
|
||||
self.combat(expected_end=self.is_in_map, fleet_index=self.fleet_show_index)
|
||||
confirm_timer.reset()
|
||||
continue
|
||||
|
||||
# Arrive
|
||||
if self.is_in_map():
|
||||
self.update_os()
|
||||
current = self.view.backend.homo_loca
|
||||
logger.attr('homo_loca', current)
|
||||
if record is None or (current is not None and np.linalg.norm(np.subtract(current, record)) < 3):
|
||||
if confirm_timer.reached():
|
||||
break
|
||||
else:
|
||||
confirm_timer.reset()
|
||||
record = current
|
||||
else:
|
||||
confirm_timer.reset()
|
||||
|
||||
logger.info('Walk stabled')
|
||||
|
||||
def port_goto(self):
|
||||
"""
|
||||
A simple and poor implement to goto port. Searching port on radar.
|
||||
@ -179,43 +277,23 @@ class OSFleet(OSCamera, Combat, Fleet, OSAsh):
|
||||
"""
|
||||
while 1:
|
||||
# Calculate destination
|
||||
port = self.radar.port_predict(self.device.image)
|
||||
logger.info(f'Port route at {port}')
|
||||
if np.linalg.norm(port) == 0:
|
||||
grid = self.radar.port_predict(self.device.image)
|
||||
logger.info(f'Port route at {grid}')
|
||||
if np.linalg.norm(grid) == 0:
|
||||
logger.info('Arrive port')
|
||||
break
|
||||
|
||||
# Update local view
|
||||
self.update_os()
|
||||
self.view.predict()
|
||||
self.view.show()
|
||||
self.predict()
|
||||
|
||||
# Click way point
|
||||
port = point_limit(port, area=(-4, -2, 3, 2))
|
||||
port = self.convert_radar_to_local(port)
|
||||
self.device.click(port)
|
||||
grid = point_limit(grid, area=(-4, -2, 3, 2))
|
||||
grid = self.convert_radar_to_local(grid)
|
||||
self.device.click(grid)
|
||||
|
||||
# Wait until arrived
|
||||
prev = (0, 0)
|
||||
confirm_timer = Timer(1, count=2).start()
|
||||
backup = self.config.temporary(MAP_HAS_FLEET_STEP=True)
|
||||
while 1:
|
||||
self.device.screenshot()
|
||||
|
||||
if self.handle_walk_out_of_step():
|
||||
backup.recover()
|
||||
raise MapWalkError('walk_out_of_step')
|
||||
|
||||
self.radar.port_predict(self.device.image)
|
||||
if np.linalg.norm(np.subtract(self.radar.port_loca, prev)) < 1:
|
||||
if confirm_timer.reached():
|
||||
break
|
||||
else:
|
||||
confirm_timer.reset()
|
||||
|
||||
prev = self.radar.port_loca
|
||||
|
||||
backup.recover()
|
||||
self.wait_until_walk_stable()
|
||||
|
||||
def fleet_set(self, index=1, skip_first_screenshot=True):
|
||||
"""
|
||||
@ -226,9 +304,159 @@ class OSFleet(OSCamera, Combat, Fleet, OSAsh):
|
||||
Returns:
|
||||
bool: If switched.
|
||||
"""
|
||||
logger.info(f'Fleet set to {index}')
|
||||
logger.hr(f'Fleet set to {index}')
|
||||
if self.fleet_selector.ensure_to_be(index):
|
||||
self.wait_until_camera_stable()
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def question_goto(self, has_fleet_step=False):
|
||||
logger.hr('Question goto')
|
||||
while 1:
|
||||
# Update local view
|
||||
# Not screenshots taking, reuse the old one
|
||||
self.update_os()
|
||||
self.predict()
|
||||
self.predict_radar()
|
||||
|
||||
# Calculate destination
|
||||
grids = self.radar.select(is_question=True)
|
||||
if grids:
|
||||
# Click way point
|
||||
grid = location_ensure(grids[0])
|
||||
grid = point_limit(grid, area=(-4, -2, 3, 2))
|
||||
if has_fleet_step:
|
||||
grid = limit_walk(grid)
|
||||
grid = self.convert_radar_to_local(grid)
|
||||
self.device.click(grid)
|
||||
else:
|
||||
logger.info('No question mark to goto, stop')
|
||||
break
|
||||
|
||||
# Wait until arrived
|
||||
# Having new screenshots
|
||||
self.wait_until_walk_stable()
|
||||
|
||||
def boss_goto(self, location=(0, 0), has_fleet_step=False):
|
||||
logger.hr('BOSS goto')
|
||||
while 1:
|
||||
# Update local view
|
||||
# Not screenshots taking, reuse the old one
|
||||
self.update_os()
|
||||
self.predict()
|
||||
self.predict_radar()
|
||||
|
||||
# Calculate destination
|
||||
grids = self.radar.select(is_enemy=True)
|
||||
if grids:
|
||||
# Click way point
|
||||
grid = np.add(location_ensure(grids[0]), location)
|
||||
grid = point_limit(grid, area=(-4, -2, 3, 2))
|
||||
if has_fleet_step:
|
||||
grid = limit_walk(grid)
|
||||
if grid == (0, 0):
|
||||
logger.info(f'Arrive destination: boss {location}')
|
||||
break
|
||||
grid = self.convert_radar_to_local(grid)
|
||||
self.device.click(grid)
|
||||
else:
|
||||
logger.info('No boss to goto, stop')
|
||||
break
|
||||
|
||||
# Wait until arrived
|
||||
# Having new screenshots
|
||||
self.wait_until_walk_stable()
|
||||
|
||||
def boss_leave(self, skip_first_screenshot=True):
|
||||
"""
|
||||
Pages:
|
||||
in: is_in_map(), or combat_appear()
|
||||
out: is_in_map(), fleet not in boss.
|
||||
"""
|
||||
logger.hr('BOSS leave')
|
||||
# Update local view
|
||||
self.update_os()
|
||||
|
||||
click_timer = Timer(3)
|
||||
while 1:
|
||||
if skip_first_screenshot:
|
||||
skip_first_screenshot = False
|
||||
else:
|
||||
self.device.screenshot()
|
||||
|
||||
# End
|
||||
if self.is_in_map():
|
||||
self.predict_radar()
|
||||
if self.radar.select(is_enemy=True):
|
||||
logger.info('Fleet left boss')
|
||||
break
|
||||
|
||||
# Re-enter boss accidently
|
||||
if self.combat_appear():
|
||||
self.ui_back(check_button=self.is_in_map)
|
||||
|
||||
# Click leave button
|
||||
if self.is_in_map() and click_timer.reached():
|
||||
grid = self.view[self.view.center_loca]
|
||||
# The left half grid next to the center grid.
|
||||
area = corner2inner(grid.grid2screen(area2corner((1, 0.25, 1.5, 0.75))))
|
||||
button = Button(area=area, color=(), button=area, name='BOSS_LEAVE')
|
||||
self.device.click(button)
|
||||
click_timer.reset()
|
||||
|
||||
def boss_clear(self, has_fleet_step=True):
|
||||
"""
|
||||
All fleets take turns in attacking the boss.
|
||||
|
||||
Args:
|
||||
has_fleet_step (bool):
|
||||
|
||||
Returns:
|
||||
bool: If success to clear.
|
||||
|
||||
Pages:
|
||||
in: Siren logger (abyssal), boss appeared.
|
||||
out: If success, dangerous or safe zone.
|
||||
If failed, still in abyssal.
|
||||
"""
|
||||
logger.hr(f'BOSS clear', level=1)
|
||||
fleets = [1, 2, 3, 4]
|
||||
standby_grids = [(-1, -1), (0, -1), (1, -1), (0, 0)]
|
||||
for fleet, standby in zip(fleets, standby_grids):
|
||||
logger.hr(f'Try boss with fleet {fleet}', level=2)
|
||||
self.fleet_set(fleet)
|
||||
self.boss_goto(location=(0, 0), has_fleet_step=has_fleet_step)
|
||||
|
||||
# End
|
||||
self.predict_radar()
|
||||
if self.radar.select(is_question=True):
|
||||
logger.info('BOSS clear')
|
||||
self.map_exit()
|
||||
return True
|
||||
|
||||
# Standby
|
||||
self.boss_leave()
|
||||
if standby == (0, 0):
|
||||
break
|
||||
self.boss_goto(location=standby, has_fleet_step=has_fleet_step)
|
||||
|
||||
logger.critical('Unable to clear boss, fleets exhausted')
|
||||
return False
|
||||
|
||||
def run_abyssal(self):
|
||||
"""
|
||||
Handle double confirms and attack abyssal (siren logger) boss.
|
||||
|
||||
Returns:
|
||||
bool: If success to clear.
|
||||
|
||||
Pages:
|
||||
in: Siren logger (abyssal).
|
||||
out: If success, in a dangerous or safe zone.
|
||||
If failed, still in abyssal.
|
||||
"""
|
||||
self.handle_map_fleet_lock(enable=False)
|
||||
self.question_goto(has_fleet_step=True)
|
||||
result = self.boss_clear(has_fleet_step=True)
|
||||
return result
|
||||
|
@ -278,10 +278,7 @@ class GlobeOperation(ActionPointHandler, MapEventHandler):
|
||||
|
||||
# End
|
||||
if self.is_in_map():
|
||||
if confirm_timer.reached():
|
||||
break
|
||||
else:
|
||||
confirm_timer.reset()
|
||||
break
|
||||
|
||||
if self.is_zone_pinned() and click_timer.reached():
|
||||
self.device.click(ZONE_ENTRANCE)
|
||||
|
@ -4,6 +4,7 @@ from module.base.mask import Mask
|
||||
from module.base.utils import *
|
||||
from module.config.config import AzurLaneConfig
|
||||
from module.logger import logger
|
||||
from module.map.map_grids import SelectedGrids
|
||||
from module.map_detection.utils import fit_points
|
||||
|
||||
MASK_RADAR = Mask('./assets/mask/MASK_OS_RADAR.png')
|
||||
@ -31,7 +32,7 @@ class RadarGrid:
|
||||
'ME': 'is_meowfficer',
|
||||
'PO': 'is_port',
|
||||
'QU': 'is_question',
|
||||
# 'FL': 'is_fleet',
|
||||
'FL': 'is_fleet',
|
||||
}
|
||||
|
||||
def __init__(self, location, image, center, config):
|
||||
@ -201,6 +202,25 @@ class Radar:
|
||||
grid.reset()
|
||||
grid.predict()
|
||||
|
||||
def select(self, **kwargs):
|
||||
"""
|
||||
Args:
|
||||
**kwargs: Attributes of Grid.
|
||||
|
||||
Returns:
|
||||
SelectedGrids:
|
||||
"""
|
||||
result = []
|
||||
for grid in self:
|
||||
flag = True
|
||||
for k, v in kwargs.items():
|
||||
if grid.__getattribute__(k) != v:
|
||||
flag = False
|
||||
if flag:
|
||||
result.append(grid)
|
||||
|
||||
return SelectedGrids(result)
|
||||
|
||||
def predict_port_outside(self, image):
|
||||
"""
|
||||
Args:
|
||||
|
@ -77,6 +77,27 @@ class Combat(Combat_, MapEventHandler):
|
||||
# self.emotion.reduce(fleet_index)
|
||||
break
|
||||
|
||||
def handle_exp_info(self):
|
||||
if self.is_combat_executing():
|
||||
return False
|
||||
if self.appear_then_click(EXP_INFO_S):
|
||||
self.device.sleep((0.25, 0.5))
|
||||
return True
|
||||
if self.appear_then_click(EXP_INFO_A):
|
||||
self.device.sleep((0.25, 0.5))
|
||||
return True
|
||||
if self.appear_then_click(EXP_INFO_B):
|
||||
self.device.sleep((0.25, 0.5))
|
||||
return True
|
||||
if self.appear_then_click(EXP_INFO_C):
|
||||
self.device.sleep((0.25, 0.5))
|
||||
return True
|
||||
if self.appear_then_click(EXP_INFO_D):
|
||||
self.device.sleep((0.25, 0.5))
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def handle_get_items(self, drop=None):
|
||||
if self.appear(GET_ITEMS_1, offset=5, interval=self.battle_status_click_interval):
|
||||
self.device.click(CLICK_SAFE_AREA)
|
||||
@ -108,7 +129,7 @@ class Combat(Combat_, MapEventHandler):
|
||||
return self.handle_os_in_map()
|
||||
|
||||
def combat_status(self, drop=None, expected_end=None):
|
||||
super().combat_status(drop=drop, expected_end=self._os_combat_expected_end)
|
||||
super().combat_status(drop=None, expected_end=self._os_combat_expected_end)
|
||||
|
||||
def combat(self, *args, **kwargs):
|
||||
"""
|
||||
|
Loading…
Reference in New Issue
Block a user