From e7202a2b46cfaa68f16a4d382f0d7781c6def844 Mon Sep 17 00:00:00 2001 From: Chen Jin <243284244@qq.com> Date: Fri, 30 Oct 2026 12:44:13 +0800 Subject: [PATCH] =?UTF-8?q?fix(GeneralBattle):=20=E5=9C=B0=E9=AC=BC?= =?UTF-8?q?=E5=87=86=E5=A4=87=E7=95=8C=E9=9D=A2=E8=87=AA=E5=8A=A8=E5=BC=80?= =?UTF-8?q?=E6=88=98=E5=AF=BC=E8=87=B4=E5=88=87=E9=A2=84=E8=AE=BE=E6=AD=BB?= =?UTF-8?q?=E5=BE=AA=E7=8E=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 地鬼(极)点挑战后准备界面只活 2~3 秒就会自动开战,而完整切预设要 4 秒以上; 一旦开战,新版阵容预设面板不会出现确认按钮,_bw_preset_default 的无上限循环 就只剩 O_PRESET/O_PRESET_FULL 的 OCR 空转,直到卡死检测报 GameStuckError → restart ×3 → human takeover(10-29 10:57、10-30 12:06/12:09 都是这个形态)。 - battle_wait: 预设 hook 第一个循环加 6s 超时 + 战斗中逃生,直接放弃切阵容而不是空转 - battle_wait: 新增宽松战斗判定 BATTLE_SOFT_THRESHOLD(0.52),准备阶段已开战时直接结束 (I_BATTLE_INFO 在真战斗里只有 0.57~0.63,原 0.8 阈值判不出来) - battle_wait: 战斗中持续续 BATTLE_STATUS_S,避免战斗超过 60s 无点击被误报卡死 - battle_wait: 预设确认循环超时后 break(原来只 warning,确认按钮点不掉会无限循环) - AreaBoss: 挑战按钮连点守卫,看到准备界面或已开战就交给 battle_wait,避免二次点击提前开战 实机验证:MuMu 上单跑一次 AreaBoss,三只地鬼全部正常结算(386s,TASK_END_OK), 无 Wait too long / GameStuckError。 --- tasks/AreaBoss/script_task.py | 7 +++ tasks/Component/GeneralBattle/battle_wait.py | 57 ++++++++++++++++++-- 2 files changed, 60 insertions(+), 4 deletions(-) diff --git a/tasks/AreaBoss/script_task.py b/tasks/AreaBoss/script_task.py index 5006e1328e..2222e157dd 100644 --- a/tasks/AreaBoss/script_task.py +++ b/tasks/AreaBoss/script_task.py @@ -189,8 +189,15 @@ def boss_fight(self, battle: RuleImage, fileter_open: bool = True) -> bool: return result def start_fight(self) -> bool: + # 点「挑战」进详情/准备界面时不能连点:I_FIRE 的搜索区域 (1075,463,150,158) 正好 + # 覆盖准备界面的「准备」鼓,第二次点击会直接落在「准备」上提前开战, + # 让 battle_wait 的切预设阶段没有机会执行(地鬼准备界面本身只活 2~3 秒)。 + # 所以:一旦看到准备界面或已经开战,立刻停手交给 battle_wait。 while 1: self.screenshot() + if self.is_in_battle_soft() or self.appear(self.I_PREPARE_HIGHLIGHT): + logger.info('Prepare stage detected, stop clicking challenge') + break if self.appear_then_click(self.I_FIRE, interval=1): continue if not self.appear(self.I_AB_CLOSE_RED): # 如果这个红色的关闭不见了才可以进行继续 diff --git a/tasks/Component/GeneralBattle/battle_wait.py b/tasks/Component/GeneralBattle/battle_wait.py index 3de381b66c..c46faa134e 100644 --- a/tasks/Component/GeneralBattle/battle_wait.py +++ b/tasks/Component/GeneralBattle/battle_wait.py @@ -1104,6 +1104,26 @@ def refresh_long_time(self) -> None: self.device.stuck_record_add('BATTLE_STATUS_S') self.device.click_record_clear() + # 正式战斗中 I_BATTLE_INFO 实测只能到 0.57~0.63(战斗特效、血条动画干扰), + # 资源自带的 0.8 阈值判不出来;准备界面/详情界面只有 0.31~0.41。 + # 所以另给一个宽松阈值,只用于「大概率已经在打架」的保命判断,不参与 hook 正式判定。 + BATTLE_SOFT_THRESHOLD = 0.52 + + def is_in_battle_soft(self) -> bool: + """宽松的战斗中判定,用于预设阶段逃生和续长等待记录。""" + return self.appear(self.I_BATTLE_INFO, threshold=self.BATTLE_SOFT_THRESHOLD) + + def keep_battle_long_wait(self) -> None: + """战斗中把长等待记录续上。 + + 任何一次点击都会走 device.handle_control_check() → stuck_record_clear(), + 把 detect_record 和两个卡死计时器一起清掉。于是战斗里只要 60 秒没有点击 + (地鬼一场 60~80 秒,中途还可能只剩纯等待),就会误报 GameStuckError。 + 每轮循环在战斗中补一次记录,被点击清掉后再补回来(已存在时不重复打日志)。 + """ + if 'BATTLE_STATUS_S' not in self.device.detect_record: + self.device.stuck_record_add('BATTLE_STATUS_S') + # ------------------------------------------------------------------------------------------------------------------ # build in # ------------------------------------------------------------------------------------------------------------------ @@ -1244,7 +1264,11 @@ def _bw_prepare_default(self, pub: PublicContext, pri: PrivateContext) -> HookSi return HookSignal.CONTINUE # 如果是锁队伍的,就直接跳过, 开启后面的 绿标,红标,应声虫 # 如果确定进入正式战斗中了 也是同样的操作 - if pri_options.lock_team or self.is_in_real_battle(False): + # 地鬼(极)这类界面会在准备界面停留 2~3 秒后自动开战(实测,不点准备也会开打), + # 用宽松阈值补一层「已经进战斗」的判定,否则会先去切预设、把开打时机拖过去。 + # 同时要求看不到准备按钮,避免把别的任务的准备界面误判成战斗中。 + if (pri_options.lock_team or self.is_in_real_battle(False) + or (self.is_in_battle_soft() and not self.appear(self.I_PREPARE_HIGHLIGHT))): logger.info('Prepare stage finish') state.done = True self.refresh_long_time() @@ -1292,6 +1316,12 @@ def _bw_preset_default(self, pub: PublicContext, pri: PrivateContext) -> HookSig return HookSignal.CONTINUE logger.info("Preset is enable") # 点击预设按钮 + # 这个等待必须有出口:地鬼(极)的准备界面约 3 秒后会自动开战(实测不点准备也会开打), + # 开战后预设面板不会出现;旧代码这里是无上限 while,面板里所有资源都匹配不上, + # 就只剩 O_PRESET/O_PRESET_FULL 的 OCR 空转,直到触发 GameStuckError 重启任务。 + # 所以:战斗中直接放弃切阵容;超时也放弃。保命优先于切预设。 + preset_timer = Timer(6).start() + switched = True while 1: self.screenshot() if self.appear(self.I_PRESET_ENSURE): @@ -1299,6 +1329,14 @@ def _bw_preset_default(self, pub: PublicContext, pri: PrivateContext) -> HookSig # 首个队伍没有满足5个式神,未出现预设按钮的情况下跳出循环 if self.appear(self.I_PRESENT_LESS_THAN_5): break + if self.is_in_battle_soft() and not self.appear(self.I_PREPARE_HIGHLIGHT): + logger.warning("Battle already started, give up switching preset") + switched = False + break + if preset_timer.reached(): + logger.warning("Preset panel not found, give up switching preset") + switched = False + break if self.appear_then_click(self.I_PRESET, threshold=0.8, interval=1): continue if self.appear_then_click(self.I_PRESET_WIT_NUMBER, threshold=0.8, interval=1): @@ -1309,6 +1347,12 @@ def _bw_preset_default(self, pub: PublicContext, pri: PrivateContext) -> HookSig if self.ocr_appear(self.O_PRESET_FULL): self.click(self.O_PRESET_FULL, interval=1) continue + if not switched: + state.done = True + return runtime.hook_enabled_update( + enable=(), + disable=('preset',), + ) logger.info("Click preset button") def get_unselect_color(tmp1, tmp2, tmp3, size): @@ -1370,12 +1414,13 @@ def get_unselect_color(tmp1, tmp2, tmp3, size): click_timer = Timer(4).start() while 1: self.screenshot() + if self.appear(self.I_PRESET_ENSURE): + if self.appear_then_click(self.I_PRESET_ENSURE, threshold=0.8, interval=1): + continue if click_timer.reached(): + # 原来这里只 warning 不退出,确认按钮一直点不掉时会无限循环 logger.warning("Switch preset failure") - if not self.appear(self.I_PRESET_ENSURE): break - if self.appear_then_click(self.I_PRESET_ENSURE, threshold=0.8, interval=1): - continue logger.info("Click preset ensure") state.done = True return runtime.hook_enabled_update( @@ -1661,6 +1706,10 @@ def battle_wait_with_strategy(self, *args, **kwargs) -> bool: while True: self.screenshot() + # 战斗里任何一次点击都会清掉 stuck_record,导致战斗超过 60 秒没点击就误报卡死 + # (地鬼一场 60~80 秒),所以战斗中每轮把长等待记录续上。 + if self.is_in_battle_soft(): + self.keep_battle_long_wait() hook_enabled = runtime.hook_enabled() # self.state_show() for handler in handlers: