diff --git a/assets/cn/handler/MAP_AIR_STRIKE.png b/assets/cn/handler/MAP_AIR_STRIKE.png index 6c1d5fc21..bbaa5e664 100644 Binary files a/assets/cn/handler/MAP_AIR_STRIKE.png and b/assets/cn/handler/MAP_AIR_STRIKE.png differ diff --git a/assets/en/handler/MAP_AIR_STRIKE.png b/assets/en/handler/MAP_AIR_STRIKE.png deleted file mode 100644 index 6c1d5fc21..000000000 Binary files a/assets/en/handler/MAP_AIR_STRIKE.png and /dev/null differ diff --git a/assets/jp/handler/MAP_AIR_STRIKE.png b/assets/jp/handler/MAP_AIR_STRIKE.png deleted file mode 100644 index 6c1d5fc21..000000000 Binary files a/assets/jp/handler/MAP_AIR_STRIKE.png and /dev/null differ diff --git a/assets/tw/handler/MAP_AIR_STRIKE.png b/assets/tw/handler/MAP_AIR_STRIKE.png deleted file mode 100644 index 6c1d5fc21..000000000 Binary files a/assets/tw/handler/MAP_AIR_STRIKE.png and /dev/null differ diff --git a/campaign/Readme.md b/campaign/Readme.md index f8f731f24..1952da77b 100644 --- a/campaign/Readme.md +++ b/campaign/Readme.md @@ -297,3 +297,4 @@ To add a new event, add a new row in here, and run `python -m module.config.conf | 20260417 | event 20201126 cn | Vacation Lane Rerun | - | - | - | 復刻假日航線 | | 20260514 | event 20221222 cn | Parallel Superimposition | - | - | - | 復刻定向折疊 | | 20260520 | event 20260520 cn | Alliance Before the Hagiobull | 圣印前的同盟 | Alliance Before the Hagiobull | 聖印前の同盟 | - | +| 20260528 | event 20220818 cn | Operation Convergence | - | - | - | 復刻遠匯點作戰 | diff --git a/campaign/event_20260520_cn/b2.py b/campaign/event_20260520_cn/b2.py index 6081d74d5..f5baa87c2 100644 --- a/campaign/event_20260520_cn/b2.py +++ b/campaign/event_20260520_cn/b2.py @@ -64,6 +64,9 @@ class Config(ConfigBase): MAP_WALK_USE_CURRENT_FLEET = True # ===== End of generated config ===== + HOMO_STORAGE = ((8, 6), [(137.405, 104.804), (1046.044, 104.804), (-12.171, 652.093), (1166.717, 652.093)]) + MAP_ENSURE_EDGE_INSIGHT_CORNER = 'bottom-right' + class Campaign(CampaignBase): MAP = MAP diff --git a/campaign/event_20260520_cn/d2.py b/campaign/event_20260520_cn/d2.py index 84d326066..e0ff73626 100644 --- a/campaign/event_20260520_cn/d2.py +++ b/campaign/event_20260520_cn/d2.py @@ -65,6 +65,9 @@ class Config(ConfigBase): MAP_WALK_USE_CURRENT_FLEET = True # ===== End of generated config ===== + HOMO_STORAGE = ((8, 6), [(137.405, 104.804), (1046.044, 104.804), (-12.171, 652.093), (1166.717, 652.093)]) + MAP_ENSURE_EDGE_INSIGHT_CORNER = 'bottom-right' + class Campaign(CampaignBase): MAP = MAP diff --git a/module/config/argument/args.json b/module/config/argument/args.json index 8d7512afa..12a669a88 100644 --- a/module/config/argument/args.json +++ b/module/config/argument/args.json @@ -1975,7 +1975,7 @@ "type": "select", "value": "campaign_main", "option": [ - "event_20221222_cn", + "event_20220818_cn", "event_20260520_cn" ], "option_cn": [ @@ -1988,10 +1988,10 @@ "event_20260520_cn" ], "option_tw": [ - "event_20221222_cn" + "event_20220818_cn" ], "option_bold": [ - "event_20221222_cn", + "event_20220818_cn", "event_20260520_cn" ] }, @@ -2395,7 +2395,7 @@ "type": "select", "value": "campaign_main", "option": [ - "event_20221222_cn", + "event_20220818_cn", "event_20260520_cn" ], "option_cn": [ @@ -2408,10 +2408,10 @@ "event_20260520_cn" ], "option_tw": [ - "event_20221222_cn" + "event_20220818_cn" ], "option_bold": [ - "event_20221222_cn", + "event_20220818_cn", "event_20260520_cn" ] }, @@ -2809,7 +2809,7 @@ "type": "select", "value": "campaign_main", "option": [ - "event_20221222_cn", + "event_20220818_cn", "event_20260520_cn" ], "option_cn": [ @@ -2822,10 +2822,10 @@ "event_20260520_cn" ], "option_tw": [ - "event_20221222_cn" + "event_20220818_cn" ], "option_bold": [ - "event_20221222_cn", + "event_20220818_cn", "event_20260520_cn" ] }, @@ -4626,7 +4626,7 @@ "type": "select", "value": "campaign_main", "option": [ - "event_20221222_cn", + "event_20220818_cn", "event_20260520_cn" ], "option_cn": [ @@ -4639,10 +4639,10 @@ "event_20260520_cn" ], "option_tw": [ - "event_20221222_cn" + "event_20220818_cn" ], "option_bold": [ - "event_20221222_cn", + "event_20220818_cn", "event_20260520_cn" ] }, @@ -5058,7 +5058,7 @@ "type": "select", "value": "campaign_main", "option": [ - "event_20221222_cn", + "event_20220818_cn", "event_20260520_cn" ], "option_cn": [ @@ -5071,10 +5071,10 @@ "event_20260520_cn" ], "option_tw": [ - "event_20221222_cn" + "event_20220818_cn" ], "option_bold": [ - "event_20221222_cn", + "event_20220818_cn", "event_20260520_cn" ] }, @@ -5490,7 +5490,7 @@ "type": "select", "value": "campaign_main", "option": [ - "event_20221222_cn", + "event_20220818_cn", "event_20260520_cn" ], "option_cn": [ @@ -5503,10 +5503,10 @@ "event_20260520_cn" ], "option_tw": [ - "event_20221222_cn" + "event_20220818_cn" ], "option_bold": [ - "event_20221222_cn", + "event_20220818_cn", "event_20260520_cn" ] }, @@ -5922,7 +5922,7 @@ "type": "select", "value": "campaign_main", "option": [ - "event_20221222_cn", + "event_20220818_cn", "event_20260520_cn" ], "option_cn": [ @@ -5935,10 +5935,10 @@ "event_20260520_cn" ], "option_tw": [ - "event_20221222_cn" + "event_20220818_cn" ], "option_bold": [ - "event_20221222_cn", + "event_20220818_cn", "event_20260520_cn" ] }, @@ -6344,7 +6344,7 @@ "type": "select", "value": "campaign_main", "option": [ - "event_20221222_cn", + "event_20220818_cn", "event_20260520_cn" ], "option_cn": [ @@ -6357,10 +6357,10 @@ "event_20260520_cn" ], "option_tw": [ - "event_20221222_cn" + "event_20220818_cn" ], "option_bold": [ - "event_20221222_cn", + "event_20220818_cn", "event_20260520_cn" ] }, diff --git a/module/config/i18n/zh-TW.json b/module/config/i18n/zh-TW.json index b02d39cce..7a714bded 100644 --- a/module/config/i18n/zh-TW.json +++ b/module/config/i18n/zh-TW.json @@ -773,7 +773,7 @@ "event_20220428_cn": "復刻虹彩的終幕曲", "event_20220526_cn": "泠誓光庭", "event_20220728_cn": "復刻雄鷹的敘事歌", - "event_20220818_cn": "遠匯點作戰", + "event_20220818_cn": "復刻遠匯點作戰", "event_20220915_cn": "復刻紫絳槿嵐", "event_20221124_cn": "復刻鍊金術士與秘密遺跡群島", "event_20221222_cn": "復刻定向折疊", diff --git a/module/device/connection.py b/module/device/connection.py index fe5f14012..f806be421 100644 --- a/module/device/connection.py +++ b/module/device/connection.py @@ -19,9 +19,10 @@ from module.config.server import VALID_CHANNEL_PACKAGE, VALID_PACKAGE, set_serve from module.device.connection_attr import ConnectionAttr from module.device.env import IS_LINUX, IS_MACINTOSH, IS_WINDOWS from module.device.method.pool import WORKER_POOL +from module.device.method.remove_warning import remove_shell_warning from module.device.method.utils import (PackageNotInstalled, RETRY_TRIES, get_serial_pair, handle_adb_error, handle_unknown_host_service, possible_reasons, random_port, recv_all, - remove_shell_warning, retry_sleep) + retry_sleep) from module.exception import EmulatorNotRunningError, RequestHumanTakeover from module.logger import logger from module.map.map_grids import SelectedGrids diff --git a/module/device/method/adb.py b/module/device/method/adb.py index 8ee6c92c0..7c2c3d8a4 100644 --- a/module/device/method/adb.py +++ b/module/device/method/adb.py @@ -10,8 +10,9 @@ from lxml import etree from module.base.decorator import Config from module.config.server import DICT_PACKAGE_TO_ACTIVITY from module.device.connection import Connection +from module.device.method.remove_warning import remove_screenshot_warning from module.device.method.utils import (ImageTruncated, PackageNotInstalled, RETRY_TRIES, handle_adb_error, - handle_unknown_host_service, remove_prefix, retry_sleep) + handle_unknown_host_service, retry_sleep) from module.exception import EmulatorNotRunningError, RequestHumanTakeover, ScriptError from module.logger import logger @@ -129,10 +130,7 @@ class Adb(Connection): else: raise ScriptError(f'Unknown method to load screenshots: {method}') - # fix compatibility issues for adb screencap decode problem when the data is from vmos pro - # When use adb screencap for a screenshot from vmos pro, there would be a header more than that from emulator - # which would cause image decode problem. So i check and remove the header there. - screenshot = remove_prefix(screenshot, b'long long=8 fun*=10\n') + screenshot = remove_screenshot_warning(screenshot) image = np.frombuffer(screenshot, np.uint8) if image is None: @@ -175,6 +173,7 @@ class Adb(Connection): @Config.when(DEVICE_OVER_HTTP=True) def screenshot_adb(self): data = self.adb_shell(['screencap'], stream=True) + data = remove_screenshot_warning(data) if len(data) < 500: logger.warning(f'Unexpected screenshot: {data}') @@ -183,6 +182,7 @@ class Adb(Connection): @retry def screenshot_adb_nc(self): data = self.adb_shell_nc(['screencap']) + data = remove_screenshot_warning(data) if len(data) < 500: logger.warning(f'Unexpected screenshot: {data}') diff --git a/module/device/method/remove_warning.py b/module/device/method/remove_warning.py new file mode 100644 index 000000000..5cb3952b5 --- /dev/null +++ b/module/device/method/remove_warning.py @@ -0,0 +1,122 @@ +from typing import overload + + +@overload +def remove_shell_warning(s: bytes) -> bytes: ... + + +@overload +def remove_shell_warning(s: str) -> str: ... + + +def remove_shell_warning(s): + """ + Remove warnings from shell + + 1. Warnings in VMOS shell + https://github.com/LmeSzinc/AzurLaneAutoScript/issues/1425 + + WARNING: linker: [vdso]: unused DT entry: type 0x70000001 arg 0x0\n + \x89PNG\r\n\x1a\n\x00\x00\x00\rIH... + + 2. This linker thingy might appear multiple times when executing multiple commands + + mek_8q:/dev # getprop | grep gnss + WARNING: linker: Warning: "[vdso]" unused DT entry: unknown processor-specific (type 0x70000001 arg 0x0) (ignoring) + WARNING: linker: Warning: "[vdso]" unused DT entry: unknown processor-specific (type 0x70000001 arg 0x0) (ignoring) + [init.svc.gnss_service]: [running] + [init.svc_debug_pid.gnss_service]: [406] + [ro.boottime.gnss_service]: [27308752875] + + Args: + s (str | bytes): bytes or str + + Returns: + str | bytes: Shell output with warnings removed + """ + if isinstance(s, bytes): + while 1: + if s.startswith(b'WARNING: linker:'): + _, _, s = s.partition(b'\n') + else: + break + elif isinstance(s, str): + while 1: + if s.startswith('WARNING: linker:'): + _, _, s = s.partition('\n') + else: + break + + return s + + +@overload +def remove_screenshot_warning(s: bytes) -> bytes: ... + + +@overload +def remove_screenshot_warning(s: str) -> str: ... + + +def remove_screenshot_warning(s): + """ + Remove warnings when taking screenshot + + 1. Errors in waydroid screencap render + https://github.com/LmeSzinc/AzurLaneAutoScript/issues/4760 + + Failed to create //.cache for shader cache (Read-only file system)---disabling.\n + + 2. Warning when taking screenshot from multiscreen device + + [Warning] Multiple displays were found, but no display id was specified! Defaulting to the first display found, + however this default is not guaranteed to be consistent across captures.\n + A display id should be specified.\n + See "dumpsys SurfaceFlinger --display-id" for valid display IDs.\n + \x89PNG... + + 3. Another format of multiscreen warning + https://github.com/LmeSzinc/AzurLaneAutoScript/issues/5682 + + [Warning] Multiple displays were found, but no display id was specified! Defaulting to the first display found, + however this default is not guaranteed to be consistent across captures. A display id should be specified.\n + A display ID can be specified with the [-d display-id] option.\n + See "dumpsys SurfaceFlinger --display-id" for valid display IDs.\n + \x89PNG... + + 4. Unknown header on VMOS PRO screenshot + https://github.com/LmeSzinc/AzurLaneAutoScript/pull/940 + + long long=8 fun*=10\n\x89PNG... + + Args: + s (str | bytes): bytes or str + + Returns: + str | bytes: Screenshot data with warnings removed + """ + if isinstance(s, bytes): + if s.startswith(b'Failed to create'): + _, _, s = s.partition(b'\n') + if s.startswith(b'[Warning] Multiple displays'): + _, _, s = s.partition(b'\n') + if s.startswith(b'A display id') or s.startswith(b'A display ID'): + _, _, s = s.partition(b'\n') + if s.startswith(b'See "dumpsys'): + _, _, s = s.partition(b'\n') + if s.startswith(b'long long=8'): + _, _, s = s.partition(b'\n') + + elif isinstance(s, str): + if s.startswith('Failed to create'): + _, _, s = s.partition('\n') + if s.startswith('[Warning] Multiple displays'): + _, _, s = s.partition('\n') + if s.startswith('A display id') or s.startswith('A display ID'): + _, _, s = s.partition('\n') + if s.startswith('See "dumpsys'): + _, _, s = s.partition('\n') + if s.startswith('long long=8'): + _, _, s = s.partition('\n') + + return s diff --git a/module/device/method/utils.py b/module/device/method/utils.py index 6c0dd8536..d17a287e9 100644 --- a/module/device/method/utils.py +++ b/module/device/method/utils.py @@ -10,6 +10,8 @@ import uiautomator2cache from adbutils import AdbTimeout from lxml import etree +from module.device.method.remove_warning import remove_shell_warning + try: # adbutils 0.x from adbutils import _AdbStreamConnection as AdbConnection @@ -299,99 +301,52 @@ def get_serial_pair(serial): return None, None -def remove_prefix(s, prefix): +@t.overload +def removeprefix(s: str, prefix: str) -> str: ... + + +@t.overload +def removeprefix(s: bytes, prefix: bytes) -> bytes: ... + + +@t.overload +def removesuffix(s: str, suffix: str) -> str: ... + + +@t.overload +def removesuffix(s: bytes, suffix: bytes) -> bytes: ... + + +def removeprefix(s, prefix): """ - Remove prefix of a string or bytes like `string.removeprefix(prefix)`, which is on Python3.9+ + Backport `string.removeprefix(prefix)`, which is on Python>=3.9 Args: - s (str, bytes): - prefix (str, bytes): + s (str | bytes): + prefix (str | bytes): Returns: - str, bytes: + str | bytes: """ - return s[len(prefix):] if s.startswith(prefix) else s + if s.startswith(prefix): + return s[len(prefix):] + return s -def remove_suffix(s, suffix): +def removesuffix(s, suffix): """ - Remove suffix of a string or bytes like `string.removesuffix(suffix)`, which is on Python3.9+ + Backport `string.removesuffix(suffix)`, which is on Python>=3.9 Args: - s (str, bytes): - suffix (str, bytes): + s (str | bytes): + suffix (str | bytes): Returns: - str, bytes: + str | bytes: """ - return s[:-len(suffix)] if s.endswith(suffix) else s - - -def remove_shell_warning(s): - """ - Remove warnings from shell - - 1. Warnings in VMOS shell - https://github.com/LmeSzinc/AzurLaneAutoScript/issues/1425 - - WARNING: linker: [vdso]: unused DT entry: type 0x70000001 arg 0x0\n\x89PNG\r\n\x1a\n\x00\x00\x00\rIH - - 2. This linker thingy might appear multiple times when executing multiple commands - - mek_8q:/dev # getprop | grep gnss - WARNING: linker: Warning: "[vdso]" unused DT entry: unknown processor-specific (type 0x70000001 arg 0x0) (ignoring) - WARNING: linker: Warning: "[vdso]" unused DT entry: unknown processor-specific (type 0x70000001 arg 0x0) (ignoring) - [init.svc.gnss_service]: [running] - [init.svc_debug_pid.gnss_service]: [406] - [ro.boottime.gnss_service]: [27308752875] - - 3. Errors in waydroid screencap render - https://github.com/LmeSzinc/AzurLaneAutoScript/issues/4760 - - Failed to create //.cache for shader cache (Read-only file system)---disabling.\n - - 4. Warning when taking screenshot from multiscreen device - - [Warning] Multiple displays were found, but no display id was specified! Defaulting to the first display found, - however this default is not guaranteed to be consistent across captures.\n - A display id should be specified.\n - See "dumpsys SurfaceFlinger --display-id" for valid display IDs.\n - \x89PNG... - - Args: - s (T): bytes or str - - Returns: - T: - """ - if isinstance(s, bytes): - while 1: - if s.startswith(b'WARNING: linker:'): - _, _, s = s.partition(b'\n') - else: - break - if s.startswith(b'Failed to create'): - _, _, s = s.partition(b'\n') - if s.startswith(b'[Warning] Multiple displays'): - _, _, s = s.partition(b'\n') - if s.startswith(b'A display id'): - _, _, s = s.partition(b'\n') - if s.startswith(b'See "dumpsys'): - _, _, s = s.partition(b'\n') - elif isinstance(s, str): - while 1: - if s.startswith('WARNING: linker:'): - _, _, s = s.partition('\n') - else: - break - if s.startswith('Failed to create'): - _, _, s = s.partition('\n') - if s.startswith('[Warning] Multiple displays'): - _, _, s = s.partition('\n') - if s.startswith('A display id'): - _, _, s = s.partition('\n') - if s.startswith('See "dumpsys'): - _, _, s = s.partition('\n') + # s[:-0] is empty string, so we need to check if suffix is empty + if suffix and s.endswith(suffix): + return s[:-len(suffix)] return s diff --git a/module/device/pkg_resources/__init__.py b/module/device/pkg_resources/__init__.py index b685a6d9a..865afcbd1 100644 --- a/module/device/pkg_resources/__init__.py +++ b/module/device/pkg_resources/__init__.py @@ -25,7 +25,7 @@ except KeyError: logger.error('Patch pkg_resources failed, patch module does not exists') -def remove_suffix(s, suffix): +def removesuffix(s, suffix): """ Remove suffix of a string or bytes like `string.removesuffix(suffix)`, which is on Python3.9+ @@ -36,7 +36,10 @@ def remove_suffix(s, suffix): Returns: str, bytes: """ - return s[:-len(suffix)] if s.endswith(suffix) else s + # s[:-0] is empty string, so we need to check if suffix is empty + if suffix and s.endswith(suffix): + return s[:-len(suffix)] + return s class FakeDistributionObject: @@ -71,7 +74,7 @@ class PackageCache: # adbutils-0.11.0-py3.7.egg-info res = re.match(r'^([a-zA-Z0-9._]+)-([a-zA-Z0-9._]+)-', file) if res: - version = remove_suffix(res.group(2), '.dist') + version = removesuffix(res.group(2), '.dist') # version = res.group(2) obj = FakeDistributionObject( dist=res.group(1), diff --git a/module/game_setting/setting_extractor.py b/module/game_setting/setting_extractor.py index 02759b680..90fa12293 100644 --- a/module/game_setting/setting_extractor.py +++ b/module/game_setting/setting_extractor.py @@ -5,7 +5,7 @@ from dataclasses import dataclass from tqdm import tqdm from module.base.decorator import cached_property -from module.device.method.utils import remove_prefix +from module.device.method.utils import removeprefix REGEX_SETTING = re.compile(r'PlayerPrefs.Get(\w{1,10})\((.*)\)') REGEX_SETTING_KEY = re.compile(r'"(.*?)"') @@ -173,7 +173,7 @@ class SettingExtractor: if not settings: continue yield '' - f = remove_prefix(file, folder).replace("\\", "/") + f = removeprefix(file, folder).replace("\\", "/") yield f' # {f}' for setting in settings: if setting.key in dic_settings: diff --git a/module/handler/assets.py b/module/handler/assets.py index 71834ed33..0a5f29e87 100644 --- a/module/handler/assets.py +++ b/module/handler/assets.py @@ -65,7 +65,7 @@ LOGIN_RETURN_SIGN = Button(area={'cn': (1, 7, 104, 47), 'en': (1, 7, 118, 39), ' MAINTENANCE_ANNOUNCE = Button(area={'cn': (923, 141, 990, 186), 'en': (923, 141, 990, 186), 'jp': (923, 141, 990, 186), 'tw': (923, 141, 990, 186)}, color={'cn': (207, 95, 91), 'en': (207, 95, 91), 'jp': (207, 95, 91), 'tw': (207, 95, 91)}, button={'cn': (923, 141, 990, 186), 'en': (923, 141, 990, 186), 'jp': (923, 141, 990, 186), 'tw': (923, 141, 990, 186)}, file={'cn': './assets/cn/handler/MAINTENANCE_ANNOUNCE.png', 'en': './assets/en/handler/MAINTENANCE_ANNOUNCE.png', 'jp': './assets/jp/handler/MAINTENANCE_ANNOUNCE.png', 'tw': './assets/tw/handler/MAINTENANCE_ANNOUNCE.png'}) MANJUU_AREA = Button(area={'cn': (520, 240, 760, 400), 'en': (520, 240, 760, 400), 'jp': (520, 240, 760, 400), 'tw': (520, 240, 760, 400)}, color={'cn': (255, 255, 255), 'en': (255, 255, 255), 'jp': (255, 255, 255), 'tw': (255, 255, 255)}, button={'cn': (520, 240, 760, 400), 'en': (520, 240, 760, 400), 'jp': (520, 240, 760, 400), 'tw': (520, 240, 760, 400)}, file={'cn': './assets/cn/handler/MANJUU_AREA.png', 'en': './assets/cn/handler/MANJUU_AREA.png', 'jp': './assets/cn/handler/MANJUU_AREA.png', 'tw': './assets/cn/handler/MANJUU_AREA.png'}) MAP_AIR_RAID = Button(area={'cn': (350, 447, 1280, 472), 'en': (350, 447, 1280, 472), 'jp': (350, 447, 1280, 472), 'tw': (350, 447, 1280, 472)}, color={'cn': (154, 43, 46), 'en': (154, 43, 46), 'jp': (154, 43, 46), 'tw': (154, 43, 46)}, button={'cn': (350, 447, 1280, 472), 'en': (350, 447, 1280, 472), 'jp': (350, 447, 1280, 472), 'tw': (350, 447, 1280, 472)}, file={'cn': './assets/cn/handler/MAP_AIR_RAID.png', 'en': './assets/en/handler/MAP_AIR_RAID.png', 'jp': './assets/jp/handler/MAP_AIR_RAID.png', 'tw': './assets/tw/handler/MAP_AIR_RAID.png'}) -MAP_AIR_STRIKE = Button(area={'cn': (0, 0, 1280, 720), 'en': (0, 0, 1280, 720), 'jp': (0, 0, 1280, 720), 'tw': (0, 0, 1280, 720)}, color={'cn': (20, 17, 17), 'en': (20, 17, 17), 'jp': (20, 17, 17), 'tw': (20, 17, 17)}, button={'cn': (0, 0, 1280, 720), 'en': (0, 0, 1280, 720), 'jp': (0, 0, 1280, 720), 'tw': (0, 0, 1280, 720)}, file={'cn': './assets/cn/handler/MAP_AIR_STRIKE.png', 'en': './assets/en/handler/MAP_AIR_STRIKE.png', 'jp': './assets/jp/handler/MAP_AIR_STRIKE.png', 'tw': './assets/tw/handler/MAP_AIR_STRIKE.png'}) +MAP_AIR_STRIKE = Button(area={'cn': (351, 453, 1280, 477), 'en': (351, 453, 1280, 477), 'jp': (351, 453, 1280, 477), 'tw': (351, 453, 1280, 477)}, color={'cn': (170, 57, 63), 'en': (170, 57, 63), 'jp': (170, 57, 63), 'tw': (170, 57, 63)}, button={'cn': (351, 453, 1280, 477), 'en': (351, 453, 1280, 477), 'jp': (351, 453, 1280, 477), 'tw': (351, 453, 1280, 477)}, file={'cn': './assets/cn/handler/MAP_AIR_STRIKE.png', 'en': './assets/cn/handler/MAP_AIR_STRIKE.png', 'jp': './assets/cn/handler/MAP_AIR_STRIKE.png', 'tw': './assets/cn/handler/MAP_AIR_STRIKE.png'}) MAP_AMBUSH = Button(area={'cn': (261, 433, 1280, 449), 'en': (261, 433, 1280, 449), 'jp': (261, 433, 1280, 449), 'tw': (261, 433, 1280, 449)}, color={'cn': (161, 41, 43), 'en': (161, 41, 43), 'jp': (161, 41, 43), 'tw': (161, 41, 43)}, button={'cn': (261, 433, 1280, 449), 'en': (261, 433, 1280, 449), 'jp': (261, 433, 1280, 449), 'tw': (261, 433, 1280, 449)}, file={'cn': './assets/cn/handler/MAP_AMBUSH.png', 'en': './assets/en/handler/MAP_AMBUSH.png', 'jp': './assets/jp/handler/MAP_AMBUSH.png', 'tw': './assets/tw/handler/MAP_AMBUSH.png'}) MAP_AMBUSH_ATTACK = Button(area={'cn': (804, 457, 876, 488), 'en': (791, 463, 888, 485), 'jp': (804, 457, 876, 488), 'tw': (804, 455, 876, 486)}, color={'cn': (139, 168, 210), 'en': (164, 187, 221), 'jp': (150, 175, 212), 'tw': (149, 175, 213)}, button={'cn': (755, 446, 925, 501), 'en': (753, 443, 927, 503), 'jp': (757, 447, 925, 501), 'tw': (762, 443, 927, 499)}, file={'cn': './assets/cn/handler/MAP_AMBUSH_ATTACK.png', 'en': './assets/en/handler/MAP_AMBUSH_ATTACK.png', 'jp': './assets/jp/handler/MAP_AMBUSH_ATTACK.png', 'tw': './assets/tw/handler/MAP_AMBUSH_ATTACK.png'}) MAP_AMBUSH_EVADE = Button(area={'cn': (1029, 457, 1101, 487), 'en': (1021, 459, 1110, 483), 'jp': (1031, 458, 1101, 487), 'tw': (1071, 457, 1101, 487)}, color={'cn': (199, 195, 196), 'en': (200, 197, 198), 'jp': (239, 194, 138), 'tw': (247, 209, 164)}, button={'cn': (979, 444, 1152, 502), 'en': (978, 443, 1153, 503), 'jp': (979, 444, 1151, 502), 'tw': (979, 444, 1152, 502)}, file={'cn': './assets/cn/handler/MAP_AMBUSH_EVADE.png', 'en': './assets/en/handler/MAP_AMBUSH_EVADE.png', 'jp': './assets/jp/handler/MAP_AMBUSH_EVADE.png', 'tw': './assets/tw/handler/MAP_AMBUSH_EVADE.png'}) diff --git a/module/map/map_fleet_preparation.py b/module/map/map_fleet_preparation.py index 70e5fef3e..34be9b507 100644 --- a/module/map/map_fleet_preparation.py +++ b/module/map/map_fleet_preparation.py @@ -268,8 +268,17 @@ class FleetOperator: # Cropping FLEET_*_IN_USE to avoid detecting info_bar, also do the trick. # It also avoids wasting time on handling the info_bar. - image = rgb2gray(self.main.image_crop(self._in_use.button, copy=False)) - return np.std(image.flatten(), ddof=1) > self.FLEET_IN_USE_STD + image = self.main.image_crop(self._in_use.button, copy=False) + + # special fix for Perseus skin, which color is so flat + # https://github.com/LmeSzinc/AzurLaneAutoScript/issues/5678 + # no ship is in color (71, 70, 63) + color = cv2.mean(image)[:3] + if color_similar(color, (224, 154, 114), threshold=30): + return True + + gray = rgb2gray(image) + return np.std(gray.flatten(), ddof=1) > self.FLEET_IN_USE_STD def bar_opened(self): """ diff --git a/module/research/project.py b/module/research/project.py index 4a92ae1f5..bc6a93e00 100644 --- a/module/research/project.py +++ b/module/research/project.py @@ -4,7 +4,7 @@ from scipy import signal from module.base.decorator import cached_property from module.base.utils import * -from module.device.method.utils import remove_suffix +from module.device.method.utils import removesuffix from module.logger import logger from module.ocr.ocr import Duration, Ocr from module.research.assets import * @@ -364,7 +364,7 @@ def research_jp_detect(image): """ project = ResearchProjectJp() project.series = get_research_series_jp(image) - project.duration = remove_suffix(str(get_research_duration_jp(image) / 3600), '.0') + project.duration = removesuffix(str(get_research_duration_jp(image) / 3600), '.0') if project.duration == '': project.duration = '0' project.genre = get_research_genre_jp(image)