diff --git a/assets/cn/guild/OCR_GUILD_OPERATIONS_PROGRESS.png b/assets/cn/guild/OCR_GUILD_OPERATIONS_PROGRESS.png index 9876394b4..b5175cfe1 100644 Binary files a/assets/cn/guild/OCR_GUILD_OPERATIONS_PROGRESS.png and b/assets/cn/guild/OCR_GUILD_OPERATIONS_PROGRESS.png differ diff --git a/assets/en/guild/OCR_GUILD_OPERATIONS_PROGRESS.png b/assets/en/guild/OCR_GUILD_OPERATIONS_PROGRESS.png deleted file mode 100644 index 9876394b4..000000000 Binary files a/assets/en/guild/OCR_GUILD_OPERATIONS_PROGRESS.png and /dev/null differ diff --git a/assets/jp/guild/OCR_GUILD_OPERATIONS_PROGRESS.png b/assets/jp/guild/OCR_GUILD_OPERATIONS_PROGRESS.png deleted file mode 100644 index 9876394b4..000000000 Binary files a/assets/jp/guild/OCR_GUILD_OPERATIONS_PROGRESS.png and /dev/null differ diff --git a/assets/tw/guild/OCR_GUILD_OPERATIONS_PROGRESS.png b/assets/tw/guild/OCR_GUILD_OPERATIONS_PROGRESS.png deleted file mode 100644 index 9876394b4..000000000 Binary files a/assets/tw/guild/OCR_GUILD_OPERATIONS_PROGRESS.png and /dev/null differ diff --git a/campaign/Readme.md b/campaign/Readme.md index 6e5243e66..1a394a7c1 100644 --- a/campaign/Readme.md +++ b/campaign/Readme.md @@ -231,3 +231,4 @@ To add a new event, add a new row in here, and run `python -m module.config.conf | 20250116 | raid 20250116 | Spring Fashion Festa | 华裳巧展喜事长 | Spring Fashion Festa | 新春華裳協奏曲 | - | | 20250123 | raid 20250116 | Spring Fashion Festa | - | - | - | 華裳巧展喜事長 | | 20250206 | event 20220818 cn | Operation Convergence Rerun | 复刻远汇点作战 | Operation Convergence Return | 結像点作戦(復刻) | - | +| 20250213 | event 20240815 cn | Windborne Steel Wings | - | - | - | 鐵翼擎風 | diff --git a/module/campaign/campaign_event.py b/module/campaign/campaign_event.py index 37d0a314d..7b28405ea 100644 --- a/module/campaign/campaign_event.py +++ b/module/campaign/campaign_event.py @@ -17,14 +17,18 @@ class CampaignEvent(CampaignStatus): tasks (list[str]): Task name """ with self.config.multi_set(): + # Disable normal events for task in tasks: - if task in ['GemsFarming']: + if task in GEMS_FARMINGS: continue keys = f'{task}.Scheduler.Enable' logger.info(f'Disable task `{task}`') self.config.cross_set(keys=keys, value=False) - for task in ['GemsFarming']: + # Reset GemsFarming + for task in tasks: + if task not in GEMS_FARMINGS: + continue name = self.config.cross_get(keys=f'{task}.Campaign.Name', default='2-4') if not self.stage_is_main(name): logger.info(f'Reset GemsFarming to 2-4') @@ -51,7 +55,7 @@ class CampaignEvent(CampaignStatus): if limit <= 0 or command not in tasks: self.get_event_pt() return False - if command == 'GemsFarming' and self.stage_is_main(self.config.Campaign_Name): + if command in GEMS_FARMINGS and self.stage_is_main(self.config.Campaign_Name): self.get_event_pt() return False @@ -77,7 +81,7 @@ class CampaignEvent(CampaignStatus): command = self.config.Scheduler_Command if command not in tasks or limit == DEFAULT_TIME: return False - if command == 'GemsFarming' and self.stage_is_main(self.config.Campaign_Name): + if command in GEMS_FARMINGS and self.stage_is_main(self.config.Campaign_Name): return False now = datetime.now().replace(microsecond=0) @@ -141,7 +145,7 @@ class CampaignEvent(CampaignStatus): """ if self.appear(CAMPAIGN_MENU_NO_EVENT, offset=(20, 20)): logger.info('Event unavailable, disable task') - tasks = EVENTS + COALITIONS + GEMS_FARMINGS + tasks = EVENTS + RAIDS + COALITIONS + GEMS_FARMINGS self._disable_tasks(tasks) self.config.task_stop() else: @@ -190,6 +194,26 @@ class CampaignEvent(CampaignStatus): self.ui_goto(page_coalition) return True + def disable_raid_on_event(self): + """ + Disable raid tasks (or coalition) when entered an event, + to be foolproof if user forgot to disable raid tasks when raid is over and another event is ongoing + """ + command = self.config.Scheduler_Command + if command not in EVENTS + GEMS_FARMINGS: + return False + if command in GEMS_FARMINGS and self.stage_is_main(self.config.Campaign_Name): + return False + + tasks = RAIDS + COALITIONS + MARITIME_ESCORTS + tasks = [t for t in tasks if self.config.is_task_enabled(t)] + if tasks: + logger.info('New event ongoing, disable old event tasks') + self._disable_tasks(tasks) + return True + else: + return False + @staticmethod def stage_is_main(name) -> bool: """ diff --git a/module/campaign/run.py b/module/campaign/run.py index d54a0b5d3..21944d80d 100644 --- a/module/campaign/run.py +++ b/module/campaign/run.py @@ -434,6 +434,7 @@ class CampaignRun(CampaignEvent, ShopStatus): self.campaign.ensure_campaign_ui(name=self.stage, mode=mode) else: self.campaign.ensure_campaign_ui(name=self.stage, mode=mode) + self.disable_raid_on_event() self.handle_commission_notice() # if in hard mode, check remain times diff --git a/module/config/argument/args.json b/module/config/argument/args.json index 3f25929ac..53e736b11 100644 --- a/module/config/argument/args.json +++ b/module/config/argument/args.json @@ -1944,12 +1944,12 @@ "display": "hide", "option_bold": [ "event_20220818_cn", - "event_20231221_cn" + "event_20240815_cn" ], "cn": "event_20220818_cn", "en": "event_20220818_cn", "jp": "event_20220818_cn", - "tw": "event_20231221_cn" + "tw": "event_20240815_cn" }, "Mode": { "type": "select", @@ -2290,12 +2290,12 @@ ], "option_bold": [ "event_20220818_cn", - "event_20231221_cn" + "event_20240815_cn" ], "cn": "event_20220818_cn", "en": "event_20220818_cn", "jp": "event_20220818_cn", - "tw": "event_20231221_cn" + "tw": "event_20240815_cn" }, "Mode": { "type": "select", @@ -2751,12 +2751,12 @@ ], "option_bold": [ "event_20220818_cn", - "event_20231221_cn" + "event_20240815_cn" ], "cn": "event_20220818_cn", "en": "event_20220818_cn", "jp": "event_20220818_cn", - "tw": "event_20231221_cn" + "tw": "event_20240815_cn" }, "Mode": { "type": "select", @@ -4653,12 +4653,12 @@ ], "option_bold": [ "event_20220818_cn", - "event_20231221_cn" + "event_20240815_cn" ], "cn": "event_20220818_cn", "en": "event_20220818_cn", "jp": "event_20220818_cn", - "tw": "event_20231221_cn" + "tw": "event_20240815_cn" }, "Mode": { "type": "select", @@ -5135,12 +5135,12 @@ ], "option_bold": [ "event_20220818_cn", - "event_20231221_cn" + "event_20240815_cn" ], "cn": "event_20220818_cn", "en": "event_20220818_cn", "jp": "event_20220818_cn", - "tw": "event_20231221_cn" + "tw": "event_20240815_cn" }, "Mode": { "type": "select", @@ -5617,12 +5617,12 @@ ], "option_bold": [ "event_20220818_cn", - "event_20231221_cn" + "event_20240815_cn" ], "cn": "event_20220818_cn", "en": "event_20220818_cn", "jp": "event_20220818_cn", - "tw": "event_20231221_cn" + "tw": "event_20240815_cn" }, "Mode": { "type": "select", @@ -6099,12 +6099,12 @@ ], "option_bold": [ "event_20220818_cn", - "event_20231221_cn" + "event_20240815_cn" ], "cn": "event_20220818_cn", "en": "event_20220818_cn", "jp": "event_20220818_cn", - "tw": "event_20231221_cn" + "tw": "event_20240815_cn" }, "Mode": { "type": "select", @@ -6581,12 +6581,12 @@ ], "option_bold": [ "event_20220818_cn", - "event_20231221_cn" + "event_20240815_cn" ], "cn": "event_20220818_cn", "en": "event_20220818_cn", "jp": "event_20220818_cn", - "tw": "event_20231221_cn" + "tw": "event_20240815_cn" }, "Mode": { "type": "select", diff --git a/module/config/i18n/zh-TW.json b/module/config/i18n/zh-TW.json index 2039e5941..4fb2f0e4c 100644 --- a/module/config/i18n/zh-TW.json +++ b/module/config/i18n/zh-TW.json @@ -969,7 +969,7 @@ "event_20240425_cn": "共鳴的PASSION", "event_20240521_cn": "Light of the Martyrium", "event_20240725_cn": "Interlude of Illusions", - "event_20240815_cn": "Windborne Steel Wings", + "event_20240815_cn": "鐵翼擎風", "event_20240829_cn": "埋葬於彼岸之花", "event_20240912_cn": "Ode of Everblooming Crimson", "event_20241024_cn": "Tempesta and the Sleeping Sea", diff --git a/module/device/platform/platform_windows.py b/module/device/platform/platform_windows.py index 0936d93fe..ef1db201a 100644 --- a/module/device/platform/platform_windows.py +++ b/module/device/platform/platform_windows.py @@ -54,7 +54,9 @@ class PlatformWindows(PlatformBase, EmulatorManager): """ command = command.replace(r"\\", "/").replace("\\", "/").replace('"', '"') logger.info(f'Execute: {command}') - return subprocess.Popen(command, close_fds=True) # only work on Windows + # `close_fds` only work on Windows + # `start_new_session` to avoid emulator getting tree-killed when Alas gets killed + return subprocess.Popen(command, close_fds=True, start_new_session=True) @classmethod def kill_process_by_regex(cls, regex: str) -> int: diff --git a/module/guild/assets.py b/module/guild/assets.py index 2d0715cad..910ae66dd 100644 --- a/module/guild/assets.py +++ b/module/guild/assets.py @@ -34,4 +34,4 @@ GUILD_REPORT_CLAIMED = Button(area={'cn': (738, 595, 1078, 637), 'en': (851, 602 GUILD_REPORT_CLOSE = Button(area={'cn': (1059, 93, 1102, 136), 'en': (1059, 93, 1102, 136), 'jp': (1059, 93, 1102, 136), 'tw': (1059, 93, 1102, 136)}, color={'cn': (71, 31, 32), 'en': (71, 31, 32), 'jp': (71, 31, 32), 'tw': (71, 31, 32)}, button={'cn': (1059, 93, 1102, 136), 'en': (1059, 93, 1102, 136), 'jp': (1059, 93, 1102, 136), 'tw': (1059, 93, 1102, 136)}, file={'cn': './assets/cn/guild/GUILD_REPORT_CLOSE.png', 'en': './assets/en/guild/GUILD_REPORT_CLOSE.png', 'jp': './assets/jp/guild/GUILD_REPORT_CLOSE.png', 'tw': './assets/tw/guild/GUILD_REPORT_CLOSE.png'}) GUILD_SUPPLY = Button(area={'cn': (1077, 617, 1233, 658), 'en': (1079, 619, 1231, 656), 'jp': (1077, 614, 1233, 655), 'tw': (1079, 619, 1231, 656)}, color={'cn': (84, 104, 154), 'en': (137, 87, 91), 'jp': (98, 100, 107), 'tw': (137, 87, 91)}, button={'cn': (1077, 617, 1233, 658), 'en': (1079, 619, 1231, 656), 'jp': (1077, 614, 1233, 655), 'tw': (1079, 619, 1231, 656)}, file={'cn': './assets/cn/guild/GUILD_SUPPLY.png', 'en': './assets/en/guild/GUILD_SUPPLY.png', 'jp': './assets/jp/guild/GUILD_SUPPLY.png', 'tw': './assets/tw/guild/GUILD_SUPPLY.png'}) OCR_GUILD_EXCHANGE_LIMIT = Button(area={'cn': (960, 383, 975, 403), 'en': (999, 383, 1018, 402), 'jp': (971, 384, 985, 403), 'tw': (962, 384, 975, 403)}, color={'cn': (59, 46, 47), 'en': (91, 93, 103), 'jp': (95, 101, 107), 'tw': (72, 57, 59)}, button={'cn': (960, 383, 975, 403), 'en': (999, 383, 1018, 402), 'jp': (971, 384, 985, 403), 'tw': (962, 384, 975, 403)}, file={'cn': './assets/cn/guild/OCR_GUILD_EXCHANGE_LIMIT.png', 'en': './assets/en/guild/OCR_GUILD_EXCHANGE_LIMIT.png', 'jp': './assets/jp/guild/OCR_GUILD_EXCHANGE_LIMIT.png', 'tw': './assets/tw/guild/OCR_GUILD_EXCHANGE_LIMIT.png'}) -OCR_GUILD_OPERATIONS_PROGRESS = Button(area={'cn': (928, 501, 978, 521), 'en': (928, 501, 978, 521), 'jp': (928, 501, 978, 521), 'tw': (928, 501, 978, 521)}, color={'cn': (83, 88, 101), 'en': (83, 88, 101), 'jp': (83, 88, 101), 'tw': (83, 88, 101)}, button={'cn': (928, 501, 978, 521), 'en': (928, 501, 978, 521), 'jp': (928, 501, 978, 521), 'tw': (928, 501, 978, 521)}, file={'cn': './assets/cn/guild/OCR_GUILD_OPERATIONS_PROGRESS.png', 'en': './assets/en/guild/OCR_GUILD_OPERATIONS_PROGRESS.png', 'jp': './assets/jp/guild/OCR_GUILD_OPERATIONS_PROGRESS.png', 'tw': './assets/tw/guild/OCR_GUILD_OPERATIONS_PROGRESS.png'}) +OCR_GUILD_OPERATIONS_PROGRESS = Button(area={'cn': (1064, 501, 1114, 521), 'en': (1064, 501, 1114, 521), 'jp': (1064, 501, 1114, 521), 'tw': (1064, 501, 1114, 521)}, color={'cn': (89, 94, 107), 'en': (89, 94, 107), 'jp': (89, 94, 107), 'tw': (89, 94, 107)}, button={'cn': (1064, 501, 1114, 521), 'en': (1064, 501, 1114, 521), 'jp': (1064, 501, 1114, 521), 'tw': (1064, 501, 1114, 521)}, file={'cn': './assets/cn/guild/OCR_GUILD_OPERATIONS_PROGRESS.png', 'en': './assets/cn/guild/OCR_GUILD_OPERATIONS_PROGRESS.png', 'jp': './assets/cn/guild/OCR_GUILD_OPERATIONS_PROGRESS.png', 'tw': './assets/cn/guild/OCR_GUILD_OPERATIONS_PROGRESS.png'}) diff --git a/module/handler/info_handler.py b/module/handler/info_handler.py index 5c05cf196..0f9791b00 100644 --- a/module/handler/info_handler.py +++ b/module/handler/info_handler.py @@ -189,6 +189,12 @@ class InfoHandler(ModuleBase): if not self.device.app_is_running(): logger.error('Detected hot fixes from game server, game died') raise GameNotRunningError + if self.match_template_color(LOGIN_CHECK, offset=(30, 30)): + logger.error('Account logged out, ' + 'probably because account kicked by server maintenance or another log in') + # Kill game, because game patches after maintenance can only be downloaded at game startup + self.device.app_stop() + raise GameNotRunningError self._hot_fix_check_wait.clear() return appear diff --git a/module/shop/shop_voucher.py b/module/shop/shop_voucher.py index 3b0045cc8..1cdb2b27f 100644 --- a/module/shop/shop_voucher.py +++ b/module/shop/shop_voucher.py @@ -204,8 +204,7 @@ class VoucherShop(ShopClerk, ShopStatus): logger.info('Voucher Shop reach bottom, stop') break else: - # Only 4 rows of items at max, goto bottom directly - VOUCHER_SHOP_SCROLL.set_bottom(main=self) + VOUCHER_SHOP_SCROLL.next_page(main=self) del_cached_property(self, 'shop_grid') del_cached_property(self, 'shop_voucher_items') continue