diff --git a/assets/template/TEMPLATE_CAUGHT_BY_SIREN.png b/assets/template/TEMPLATE_CAUGHT_BY_SIREN.png new file mode 100644 index 000000000..25542f881 Binary files /dev/null and b/assets/template/TEMPLATE_CAUGHT_BY_SIREN.png differ diff --git a/campaign/event_20200521_cn/a1.py b/campaign/event_20200521_cn/a1.py new file mode 100644 index 000000000..725651382 --- /dev/null +++ b/campaign/event_20200521_cn/a1.py @@ -0,0 +1,48 @@ +from module.campaign.campaign_base import CampaignBase +from module.map.map_base import CampaignMap +from module.map.map_grids import SelectedGrids, RoadGrids +from module.logger import logger + + +MAP = CampaignMap() +MAP.map_data = ''' + -- -- ++ -- -- -- ++ ++ + -- -- -- -- -- -- -- -- + ++ -- -- ++ -- -- -- -- + ++ -- -- -- -- -- -- -- + -- -- -- -- -- -- -- -- + -- -- ++ ++ ++ -- -- ++ +''' + + +class Config: + SUBMARINE = 0 + FLEET_BOSS = 1 + + POOR_MAP_DATA = True + MAP_HAS_AMBUSH = False + MAP_HAS_FLEET_STEP = True + MAP_HAS_MOVABLE_ENEMY = True + MAP_HAS_SIREN = True + MAP_HAS_DYNAMIC_RED_BORDER = True + MAP_HAS_MAP_STORY = True + MAP_SIREN_COUNT = 2 + + TRUST_EDGE_LINES = False + COINCIDENT_POINT_ENCOURAGE_DISTANCE = 1.5 + INTERNAL_LINES_FIND_PEAKS_PARAMETERS = { + 'height': (100, 235), + 'width': 1, + 'prominence': 10, + 'distance': 35, + } + EDGE_LINES_FIND_PEAKS_PARAMETERS = { + 'height': (255 - 80, 255), + 'prominence': 2, + 'distance': 50, + 'wlen': 1000 + } + + +class Campaign(CampaignBase): + MAP = MAP diff --git a/campaign/event_20200521_cn/a2.py b/campaign/event_20200521_cn/a2.py new file mode 100644 index 000000000..aeaf899cd --- /dev/null +++ b/campaign/event_20200521_cn/a2.py @@ -0,0 +1,21 @@ +from module.campaign.campaign_base import CampaignBase +from module.map.map_base import CampaignMap +from module.map.map_grids import SelectedGrids, RoadGrids +from module.logger import logger +from campaign.event_20200521_cn.a1 import Config + + +MAP = CampaignMap() +MAP.map_data = ''' + -- -- -- -- -- -- -- -- ++ + -- -- -- ++ ++ -- -- -- ++ + ++ -- -- -- -- -- -- -- -- + ++ -- ++ -- -- -- -- -- -- + -- -- -- -- ++ ++ -- -- -- + -- -- -- -- ++ ++ -- -- -- +''' +MAP.camera_data = ['D1', 'D4', 'F2', 'F4'] + + +class Campaign(CampaignBase): + MAP = MAP diff --git a/campaign/event_20200521_cn/a3.py b/campaign/event_20200521_cn/a3.py new file mode 100644 index 000000000..cf091669a --- /dev/null +++ b/campaign/event_20200521_cn/a3.py @@ -0,0 +1,25 @@ +from module.campaign.campaign_base import CampaignBase +from module.map.map_base import CampaignMap +from module.map.map_grids import SelectedGrids, RoadGrids +from module.logger import logger +from campaign.event_20200521_cn.a1 import Config as ConfigBase + + +MAP = CampaignMap() +MAP.map_data = ''' + -- -- -- ++ ++ -- -- -- + -- -- -- -- -- -- -- -- + ++ ++ -- -- -- -- ++ ++ + -- -- -- ++ -- -- ++ ++ + -- -- -- ++ -- -- -- -- + -- -- -- -- -- -- -- ++ + -- -- -- ++ -- -- -- -- +''' + + +class Config(ConfigBase): + MAP_HAS_DYNAMIC_RED_BORDER = False + + +class Campaign(CampaignBase): + MAP = MAP diff --git a/campaign/event_20200521_cn/c1.py b/campaign/event_20200521_cn/c1.py new file mode 100644 index 000000000..725651382 --- /dev/null +++ b/campaign/event_20200521_cn/c1.py @@ -0,0 +1,48 @@ +from module.campaign.campaign_base import CampaignBase +from module.map.map_base import CampaignMap +from module.map.map_grids import SelectedGrids, RoadGrids +from module.logger import logger + + +MAP = CampaignMap() +MAP.map_data = ''' + -- -- ++ -- -- -- ++ ++ + -- -- -- -- -- -- -- -- + ++ -- -- ++ -- -- -- -- + ++ -- -- -- -- -- -- -- + -- -- -- -- -- -- -- -- + -- -- ++ ++ ++ -- -- ++ +''' + + +class Config: + SUBMARINE = 0 + FLEET_BOSS = 1 + + POOR_MAP_DATA = True + MAP_HAS_AMBUSH = False + MAP_HAS_FLEET_STEP = True + MAP_HAS_MOVABLE_ENEMY = True + MAP_HAS_SIREN = True + MAP_HAS_DYNAMIC_RED_BORDER = True + MAP_HAS_MAP_STORY = True + MAP_SIREN_COUNT = 2 + + TRUST_EDGE_LINES = False + COINCIDENT_POINT_ENCOURAGE_DISTANCE = 1.5 + INTERNAL_LINES_FIND_PEAKS_PARAMETERS = { + 'height': (100, 235), + 'width': 1, + 'prominence': 10, + 'distance': 35, + } + EDGE_LINES_FIND_PEAKS_PARAMETERS = { + 'height': (255 - 80, 255), + 'prominence': 2, + 'distance': 50, + 'wlen': 1000 + } + + +class Campaign(CampaignBase): + MAP = MAP diff --git a/campaign/event_20200521_cn/c2.py b/campaign/event_20200521_cn/c2.py new file mode 100644 index 000000000..69d4ef8f0 --- /dev/null +++ b/campaign/event_20200521_cn/c2.py @@ -0,0 +1,21 @@ +from module.campaign.campaign_base import CampaignBase +from module.map.map_base import CampaignMap +from module.map.map_grids import SelectedGrids, RoadGrids +from module.logger import logger +from campaign.event_20200521_cn.c1 import Config + + +MAP = CampaignMap() +MAP.map_data = ''' + -- -- -- -- -- -- -- -- ++ + -- -- -- ++ ++ -- -- -- ++ + ++ -- -- -- -- -- -- -- -- + ++ -- ++ -- -- -- -- -- -- + -- -- -- -- ++ ++ -- -- -- + -- -- -- -- ++ ++ -- -- -- +''' +MAP.camera_data = ['D1', 'D4', 'F2', 'F4'] + + +class Campaign(CampaignBase): + MAP = MAP diff --git a/campaign/event_20200521_cn/c3.py b/campaign/event_20200521_cn/c3.py new file mode 100644 index 000000000..3cb4f9d14 --- /dev/null +++ b/campaign/event_20200521_cn/c3.py @@ -0,0 +1,25 @@ +from module.campaign.campaign_base import CampaignBase +from module.map.map_base import CampaignMap +from module.map.map_grids import SelectedGrids, RoadGrids +from module.logger import logger +from campaign.event_20200521_cn.c1 import Config as ConfigBase + + +MAP = CampaignMap() +MAP.map_data = ''' + -- -- -- ++ ++ -- -- -- + -- -- -- -- -- -- -- -- + ++ ++ -- -- -- -- ++ ++ + -- -- -- ++ -- -- ++ ++ + -- -- -- ++ -- -- -- -- + -- -- -- -- -- -- -- ++ + -- -- -- ++ -- -- -- -- +''' + + +class Config(ConfigBase): + FLEET_BOSS = 2 + + +class Campaign(CampaignBase): + MAP = MAP diff --git a/module/campaign/campaign_base.py b/module/campaign/campaign_base.py index e2ec33009..a49ba3595 100644 --- a/module/campaign/campaign_base.py +++ b/module/campaign/campaign_base.py @@ -62,7 +62,12 @@ class CampaignBase(Map): return True return self.battle_default() else: - return self.battle_boss() + backup = self.config.FLEET_BOSS + if self.config.FLEET_2 != 0: + self.config.FLEET_BOSS = 2 + result = self.battle_boss() + self.config.FLEET_BOSS = backup + return result @Config.when(MAP_CLEAR_ALL_THIS_TIME=False, POOR_MAP_DATA=False) def execute_a_battle(self): diff --git a/module/combat/combat.py b/module/combat/combat.py index 91dfd6159..2ab76b535 100644 --- a/module/combat/combat.py +++ b/module/combat/combat.py @@ -160,7 +160,7 @@ class Combat(HPBalancer, EnemySearchingHandler, Retirement, SubmarineCall, Comba if self.appear_then_click(EMERGENCY_REPAIR_CONFIRM, offset=True): self.device.sleep(0.5) # Animation: hp increase and emergency_repair amount decrease. return True - if self.appear(EMERGENCY_REPAIR_AVAILABLE): + if self.appear(BATTLE_PREPARATION) and self.appear(EMERGENCY_REPAIR_AVAILABLE): logger.info('EMERGENCY_REPAIR_AVAILABLE') if np.min(np.array(self.hp)[np.array(self.hp) > 0.001]) < self.config.EMERGENCY_REPAIR_SINGLE_THRESHOLD \ or np.max(self.hp[:3]) < self.config.EMERGENCY_REPAIR_HOLE_THRESHOLD \ @@ -346,3 +346,4 @@ class Combat(HPBalancer, EnemySearchingHandler, Retirement, SubmarineCall, Comba func=func, call_submarine_at_boss=call_submarine_at_boss, save_get_items=save_get_items) self.combat_status( save_get_items=save_get_items, expected_end=expected_end) + self.handle_map_after_combat_story() diff --git a/module/config/config.py b/module/config/config.py index 6be2fdf0d..7f29ebd43 100644 --- a/module/config/config.py +++ b/module/config/config.py @@ -201,6 +201,7 @@ class AzurLaneConfig: MAP_HAS_MOVABLE_ENEMY = False MAP_HAS_SIREN = False MAP_HAS_DYNAMIC_RED_BORDER = False + MAP_HAS_MAP_STORY = False # event_20200521_cn(穹顶下的圣咏曲) adds after-combat story. MAP_SIREN_MOVE_WAIT = 1.5 # The enemy moving takes about 1.2 ~ 1.5s. MAP_SIREN_COUNT = 0 MAP_MYSTERY_HAS_CARRIER = False @@ -233,7 +234,8 @@ class AzurLaneConfig: MID_Y = SCREEN_CENTER[1] # UI mask UI_MASK_FILE = './module/map/ui_mask.png' - UI_MASK = np.array(Image.open(UI_MASK_FILE).convert('L')) + UI_MASK_PIL = Image.open(UI_MASK_FILE).convert('L') + UI_MASK = np.array(UI_MASK_PIL) kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3)) UI_MASK_STROKE = cv2.erode(UI_MASK, kernel).astype('uint8') diff --git a/module/config/dictionary.py b/module/config/dictionary.py index 94f4bd8a7..c4759ba32 100644 --- a/module/config/dictionary.py +++ b/module/config/dictionary.py @@ -200,6 +200,7 @@ dic_chi_to_eng = { '微层混合': 'event_20200326_cn', '复刻苍红的回响': 'event_20200423_cn', '夜幕下的归途': 'event_20200507_cn', + '穹顶下的圣咏曲': 'event_20200521_cn', } dic_eng_to_chi = {v: k for k, v in dic_chi_to_eng.items()} diff --git a/module/daemon/daemon.py b/module/daemon/daemon.py index cb0c7bcce..10def96a6 100644 --- a/module/daemon/daemon.py +++ b/module/daemon/daemon.py @@ -3,11 +3,11 @@ from module.daemon.assets import * from module.handler.ambush import MAP_AMBUSH_EVADE from module.handler.mystery import MysteryHandler from module.map.map_fleet_preparation import FleetPreparation +from module.exception import * class AzurLaneDaemon(FleetPreparation, Combat, MysteryHandler): def daemon(self): - while 1: self.device.screenshot() @@ -21,8 +21,11 @@ class AzurLaneDaemon(FleetPreparation, Combat, MysteryHandler): # continue # self.device.click(BATTLE_PREPARATION) self.combat_preparation() - if self.handle_battle_status(save_get_items=False): - self.combat_status(save_get_items=False, expected_end='no_searching') + try: + if self.handle_battle_status(save_get_items=False): + self.combat_status(save_get_items=False, expected_end='no_searching') + continue + except CampaignEnd: continue # Map operation diff --git a/module/handler/enemy_searching.py b/module/handler/enemy_searching.py index 43bc81c5d..0bcd8b5f4 100644 --- a/module/handler/enemy_searching.py +++ b/module/handler/enemy_searching.py @@ -55,8 +55,11 @@ class EnemySearchingHandler(InfoHandler): if self.handle_in_stage(): return True if self.handle_story_skip(): + self.ensure_no_story() timeout.limit = 10 timeout.reset() + + # End if self.enemy_searching_appear(): appeared = True else: @@ -66,9 +69,7 @@ class EnemySearchingHandler(InfoHandler): logger.info('In map.') break self.enemy_searching_color_initial() - if timeout.reached(): - # logger.warning('Enemy searching timeout.') logger.info('Enemy searching timeout.') break diff --git a/module/handler/fast_forward.py b/module/handler/fast_forward.py index ebec9d2c7..ace1bf38d 100644 --- a/module/handler/fast_forward.py +++ b/module/handler/fast_forward.py @@ -17,6 +17,9 @@ class FastForwardHandler(ModuleBase): is_map_green = False def handle_fast_forward(self): + if self.config.MAP_HAS_MAP_STORY: + if self.appear(MAP_STAR_1): + self.config.MAP_HAS_MAP_STORY = False if not self.appear(MAP_STAR_1) or not self.appear(MAP_STAR_2) or not self.appear(MAP_STAR_3): self.config.ENABLE_FAST_FORWARD = False logger.info('Campaign is not 3-star cleared.') diff --git a/module/handler/info_handler.py b/module/handler/info_handler.py index 31a01134d..e05a24e22 100644 --- a/module/handler/info_handler.py +++ b/module/handler/info_handler.py @@ -107,3 +107,25 @@ class InfoHandler(ModuleBase): return False return self.story_skip() + + def ensure_no_story(self, skip_first_screenshot=True): + logger.info('Ensure no story') + story_timer = Timer(5, count=4) + story_timer.start() + while 1: + if skip_first_screenshot: + skip_first_screenshot = False + else: + self.device.screenshot() + + if self.story_skip(): + story_timer.reset() + + if story_timer.reached(): + break + + def handle_map_after_combat_story(self): + if not self.config.MAP_HAS_MAP_STORY: + return False + + self.ensure_no_story() diff --git a/module/logger.py b/module/logger.py index a23b03652..f504e136f 100644 --- a/module/logger.py +++ b/module/logger.py @@ -49,6 +49,6 @@ def attr(name, text): logger.hr = hr logger.attr = attr -logger.screenshot_deque = deque(maxlen=30) +logger.screenshot_deque = deque(maxlen=60) logger.hr('Start', level=0) diff --git a/module/map/fleet.py b/module/map/fleet.py index f9bdce0f1..df6591a76 100644 --- a/module/map/fleet.py +++ b/module/map/fleet.py @@ -242,6 +242,10 @@ class Fleet(Camera, MapOperation, AmbushHandler): logger.info(f'Predict fleet_2 to be {fleets[0]}') self.fleet_2_location = fleets[0].location + for loca in [self.fleet_1_location, self.fleet_2_location]: + if len(loca) and loca in self.map: + self.map[loca].wipe_out() + def find_all_fleets(self): logger.hr('Find all fleets') queue = self.map.select(is_spawn_point=True) @@ -269,7 +273,7 @@ class Fleet(Camera, MapOperation, AmbushHandler): self.fleet_1 = fleets[0].location else: logger.info('Fleet_2 not detected.') - if self.config.POOR_MAP_DATA and self.map.select(is_spawn_point=True): + if self.config.POOR_MAP_DATA and not self.map.select(is_spawn_point=True): self.fleet_1 = fleets[0].location else: self.find_all_fleets() @@ -450,22 +454,24 @@ class Fleet(Camera, MapOperation, AmbushHandler): appear = False for data in self.map.spawn_data: if data.get('battle') == self.battle_count and data.get('boss', 0): + logger.info('Catch camera re-positioning after boss appear') appear = True if self.config.POOR_MAP_DATA: + appear = True self.device.screenshot() grids = Grids(self.device.image, config=self.config) grids.predict() grids.show() for grid in grids: if grid.is_boss: - appear = True + logger.info('Catch camera re-positioning after boss appear') + for g in self.map: g.wipe_out() break if appear: - logger.info('Catch camera re-positioning after boss appear') camera = self.camera self.ensure_edge_insight() logger.info('Refocus to previous camera position.') diff --git a/module/map/grid_predictor.py b/module/map/grid_predictor.py index 9dfd3f46e..f1d74ba0f 100644 --- a/module/map/grid_predictor.py +++ b/module/map/grid_predictor.py @@ -13,7 +13,6 @@ class GridPredictor: def __init__(self, location, image, corner): """ - Args: location: image: @@ -55,6 +54,7 @@ class GridPredictor: if self.is_fleet: self.is_current_fleet = self.predict_current_fleet() self.is_boss = self.predict_boss() + # self.caught_by_siren = self.predict_siren_caught() # self.image_perspective = color_similarity_2d( # self.image.transform(self.ENEMY_PERSPECTIVE_IMAGE_SIZE, Image.PERSPECTIVE, self._perspective) # , color=(255, 36, 82) @@ -220,3 +220,7 @@ class GridPredictor: return True return False + + def predict_siren_caught(self): + image = self.get_relative_image((-1, -1.5, 1, 0.5), output_shape=(120, 120)) + return TEMPLATE_CAUGHT_BY_SIREN.match(image, similarity=0.6) diff --git a/module/map/grids.py b/module/map/grids.py index 397d3bbe6..0520acefd 100644 --- a/module/map/grids.py +++ b/module/map/grids.py @@ -1,4 +1,5 @@ import numpy as np +from PIL import Image from module.base.utils import area_in_area from module.config.config import AzurLaneConfig @@ -14,10 +15,9 @@ class Grids(Perspective): image: config(AzurLaneConfig): """ - self.image = image - self.config = config # try: super().__init__(image, config) + self.image = self._image_clear_ui(image) self.grids = {} for grid in self._gen(): @@ -104,6 +104,15 @@ class Grids(Perspective): # break def update(self, image): + image = self._image_clear_ui(image) self.image = image for grid in self: grid.image = image + + def _image_clear_ui(self, image): + if not self.config.MAP_HAS_DYNAMIC_RED_BORDER: + return image + + new = Image.new('RGB', self.config.SCREEN_SIZE, (0, 0, 0)) + new.paste(image.crop(self.config.DETECTING_AREA), box=self.config.DETECTING_AREA, mask=self.config.UI_MASK_PIL) + return new diff --git a/module/template/assets.py b/module/template/assets.py index 411b9b842..243c4a017 100644 --- a/module/template/assets.py +++ b/module/template/assets.py @@ -6,6 +6,7 @@ from module.base.template import Template TEMPLATE_AMBUSH_EVADE_FAILED = Template(file='./assets/template/TEMPLATE_AMBUSH_EVADE_FAILED.png') TEMPLATE_AMBUSH_EVADE_SUCCESS = Template(file='./assets/template/TEMPLATE_AMBUSH_EVADE_SUCCESS.png') +TEMPLATE_CAUGHT_BY_SIREN = Template(file='./assets/template/TEMPLATE_CAUGHT_BY_SIREN.png') TEMPLATE_ENEMY_BOSS = Template(file='./assets/template/TEMPLATE_ENEMY_BOSS.png') TEMPLATE_ENEMY_L = Template(file='./assets/template/TEMPLATE_ENEMY_L.png') TEMPLATE_ENEMY_M = Template(file='./assets/template/TEMPLATE_ENEMY_M.png')