mirror of
https://github.com/LmeSzinc/StarRailCopilot.git
synced 2024-11-23 00:52:22 +00:00
Add: 适配穹顶下的圣咏曲AC图
- 修复UI导致的动态边缘识别出错 - 修复只识别出一队时的处理逻辑 - 修复处理战斗结束后跳出的剧情 - 修复辅助点击报CampaignEnd - 修复游戏出现白屏bug时, 连续点击使用紧急维修 - 增加地图全清时, 使用二队打BOSS, 忽略FLEET_BOSS - 增加被精英抓住的识别, 暂时还用不到
This commit is contained in:
parent
bd8090dff5
commit
8ec5099b58
BIN
assets/template/TEMPLATE_CAUGHT_BY_SIREN.png
Normal file
BIN
assets/template/TEMPLATE_CAUGHT_BY_SIREN.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.2 KiB |
48
campaign/event_20200521_cn/a1.py
Normal file
48
campaign/event_20200521_cn/a1.py
Normal file
@ -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
|
21
campaign/event_20200521_cn/a2.py
Normal file
21
campaign/event_20200521_cn/a2.py
Normal file
@ -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
|
25
campaign/event_20200521_cn/a3.py
Normal file
25
campaign/event_20200521_cn/a3.py
Normal file
@ -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
|
48
campaign/event_20200521_cn/c1.py
Normal file
48
campaign/event_20200521_cn/c1.py
Normal file
@ -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
|
21
campaign/event_20200521_cn/c2.py
Normal file
21
campaign/event_20200521_cn/c2.py
Normal file
@ -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
|
25
campaign/event_20200521_cn/c3.py
Normal file
25
campaign/event_20200521_cn/c3.py
Normal file
@ -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
|
@ -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):
|
||||
|
@ -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()
|
||||
|
@ -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')
|
||||
|
||||
|
@ -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()}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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.')
|
||||
|
@ -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()
|
||||
|
@ -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)
|
||||
|
@ -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.')
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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')
|
||||
|
Loading…
Reference in New Issue
Block a user