diff --git a/autowsgr/ops/decisive/handlers.py b/autowsgr/ops/decisive/handlers.py index e59a59f0..1b00c66f 100644 --- a/autowsgr/ops/decisive/handlers.py +++ b/autowsgr/ops/decisive/handlers.py @@ -17,6 +17,8 @@ import time from typing import TYPE_CHECKING +import cv2 + from autowsgr.combat.engine import run_combat from autowsgr.combat.plan import CombatMode, CombatPlan, NodeDecision from autowsgr.infra.logger import get_logger @@ -153,11 +155,20 @@ def _handle_waiting_for_map(self) -> None: phase == DecisivePhase.PREPARE_COMBAT and self._state.stage == 1 and not self._has_chosen_fleet - and self._state.node != 'U' # 排除暂离后重进的情况 ): - _log.warning('[决战] 首进第 1 小节将 PREPARE_COMBAT 修正为 CHOOSE_FLEET') - self._state.phase = DecisivePhase.CHOOSE_FLEET - return + if self._state.node != 'U': + _log.warning('[决战] 首进第 1 小节将 PREPARE_COMBAT 修正为 CHOOSE_FLEET') + self._state.phase = DecisivePhase.CHOOSE_FLEET + return + # node == 'U' 时,通过舰标检测区分暂离重进与 overlay 延迟加载 + bgr = cv2.cvtColor(screen, cv2.COLOR_RGB2BGR) + icon_x = self._map._locate_ship_icon(bgr) + if icon_x is None: + _log.warning( + '[决战] 首进第 1 小节未检测到舰标,将 PREPARE_COMBAT 修正为 CHOOSE_FLEET' + ) + self._state.phase = DecisivePhase.CHOOSE_FLEET + return if phase is not None: self._state.phase = phase @@ -280,6 +291,7 @@ def _handle_prepare_combat(self) -> None: # 先使用技能,再注册舰船,如果是未知节点,也判定一下技能是否使用 current_node = self._state.node + time.sleep(0.5) # 等待动画稳定后截图判定 skill_used = self._map.is_skill_used() _log.debug('[决战] 节点: {}, 技能已使用检测: {}', current_node, skill_used) @@ -300,6 +312,14 @@ def _handle_prepare_combat(self) -> None: else: _log.debug('[决战] 跳过技能使用: 节点={}, 技能已使用={}', current_node, skill_used) + # 首次进入且尚未选择过舰队时,使用技能后可能出现战备舰队获取 overlay, + # 先切回 WAITING_FOR_MAP 等待 overlay 稳定,避免直接点击编队超时。 + if not skill_used not self._has_chosen_fleet: + _log.info('[决战] 首次进入,使用技能后等待 overlay 稳定') + self._wait_deadline = time.monotonic() + 10.0 + self._state.phase = DecisivePhase.WAITING_FOR_MAP + return + # ── 恢复模式: 扫描当前舰队与可用舰船 ───────────────────────── # 对齐 legacy: if fleet.empty() and not is_begin(): _check_fleet() if self._resume_mode: diff --git a/autowsgr/ui/decisive/battle_page.py b/autowsgr/ui/decisive/battle_page.py index e49ae43f..71fbc780 100644 --- a/autowsgr/ui/decisive/battle_page.py +++ b/autowsgr/ui/decisive/battle_page.py @@ -107,11 +107,14 @@ """章节导航最大尝试次数。""" MAX_CHAPTER: int = 6 -MIN_CHAPTER: int = 4 +MIN_CHAPTER: int = 1 # ── recognize_stage 检测点 ── _STAGE_CHECK_POINTS: dict[int, list[tuple[float, float]]] = { + 1: [(0.4115, 0.4019), (0.6604, 0.4630), (0.8396, 0.7093)], + 2: [(0.4354, 0.3852), (0.5792, 0.6648), (0.8187, 0.5889)], + 3: [(0.4219, 0.6648), (0.6531, 0.3944), (0.8042, 0.7444)], 4: [(0.381, 0.436), (0.596, 0.636), (0.778, 0.521)], 5: [(0.418, 0.378), (0.760, 0.477), (0.550, 0.750)], 6: [(0.606, 0.375), (0.532, 0.703), (0.862, 0.644)], diff --git a/autowsgr/ui/decisive/map_controller.py b/autowsgr/ui/decisive/map_controller.py index 17498005..0d569277 100644 --- a/autowsgr/ui/decisive/map_controller.py +++ b/autowsgr/ui/decisive/map_controller.py @@ -626,7 +626,7 @@ def confirm_stage_clear(self) -> list[str]: # 掉落处理结束后,继续等待回到决战入口页,避免奖励弹窗残留导致后续状态识别超时 settle_deadline = time.monotonic() + 12.0 - reward_ack_pos = (0.5, 0.5) + reward_ack_pos = (0.953, 0.954) while time.monotonic() < settle_deadline: screen = self._ctrl.screenshot() if ImageChecker.find_any(screen, entry_templates, confidence=0.8) is not None: @@ -639,6 +639,7 @@ def confirm_stage_clear(self) -> list[str]: self._ctrl.click(*reward_ack_pos) time.sleep(0.35) confirm_operation(self._ctrl, timeout=0.8) + confirm_operation(self._ctrl, timeout=0.8) continue if confirm_operation(self._ctrl, timeout=0.8): @@ -648,6 +649,18 @@ def confirm_stage_clear(self) -> list[str]: else: _log.warning('[地图控制器] 小关通关后未能确认已回到决战入口页') + # settle 循环结束后,如果仍未回到入口页,尝试从地图页返回 + for _ in range(5): + screen = self._ctrl.screenshot() + if ImageChecker.find_any(screen, entry_templates, confidence=0.8) is not None: + _log.info('[地图控制器] 通过返回按钮回到决战入口页') + break + _log.debug('[地图控制器] 尝试点击返回按钮回到决战入口页') + self._ctrl.click(0.03, 0.06) + time.sleep(1.0) + else: + _log.warning('[地图控制器] 多次尝试后仍未能回到决战入口页') + if collected: _log.info('[地图控制器] 小关通关共收集 {} 个掉落', len(collected)) return collected