|
20 | 20 | LOOT_COUNT_CROP, |
21 | 21 | SHIP_COUNT_CROP, |
22 | 22 | SIDEBAR_CLICK_X, |
23 | | - SIDEBAR_SCAN_Y_RANGE, |
24 | 23 | TOTAL_CHAPTERS, |
25 | 24 | MapPanel, |
26 | 25 | ) |
@@ -149,39 +148,26 @@ class SortiePanelMixin(BaseMapPage): |
149 | 148 | # 章节 / 地图导航 |
150 | 149 | # ═══════════════════════════════════════════════════════════════════════ |
151 | 150 |
|
152 | | - def click_prev_chapter(self, screen: np.ndarray | None = None) -> bool: |
153 | | - """点击侧边栏上方章节 (前一章)。""" |
154 | | - if screen is None: |
155 | | - screen = self._ctrl.screenshot() |
156 | | - sel_y = self.find_selected_chapter_y(screen) |
157 | | - if sel_y is None: |
158 | | - _log.warning('[UI] 侧边栏未找到选中章节,无法切换') |
159 | | - return False |
160 | | - target_y = sel_y - CHAPTER_SPACING |
161 | | - if target_y < SIDEBAR_SCAN_Y_RANGE[0]: |
162 | | - _log.warning('[UI] 已在最前章节,无法继续向前') |
163 | | - return False |
164 | | - _log.info('[UI] 地图页面 -> 上一章 (y={:.3f})', target_y) |
165 | | - self._ctrl.click(SIDEBAR_CLICK_X, target_y) |
166 | | - return True |
| 151 | + def click_chapter(self, num: int): |
| 152 | + """点击侧边栏章节 |
167 | 153 |
|
168 | | - def click_next_chapter(self, screen: np.ndarray | None = None) -> bool: |
169 | | - """点击侧边栏下方章节 (后一章)。""" |
170 | | - if screen is None: |
171 | | - screen = self._ctrl.screenshot() |
172 | | - sel_y = self.find_selected_chapter_y(screen) |
173 | | - if sel_y is None: |
174 | | - _log.warning('[UI] 侧边栏未找到选中章节,无法切换') |
175 | | - return False |
176 | | - target_y = sel_y + CHAPTER_SPACING |
177 | | - if target_y > SIDEBAR_SCAN_Y_RANGE[1]: |
178 | | - _log.warning('[UI] 已在最后章节,无法继续向后') |
179 | | - return False |
180 | | - _log.info('[UI] 地图页面 -> 下一章 (y={:.3f})', target_y) |
| 154 | + Parameters |
| 155 | + ---------- |
| 156 | + num: |
| 157 | + 跳转数量, 正数为向下跳转, 负数为向上跳转 |
| 158 | + 允许输入[-3, 3] |
| 159 | + """ |
| 160 | + if not -3 <= num <= 3: |
| 161 | + raise ValueError(f'跳转数量必须为 -3 到 3, 收到: {num}') |
| 162 | + if num == 0: |
| 163 | + return |
| 164 | + sel_y = self.find_selected_chapter_y() |
| 165 | + target_y = sel_y + num * CHAPTER_SPACING |
| 166 | + _log.info('[UI] 地图页面 -> 跳转章节 {} (y={:.3f})', num, target_y) |
181 | 167 | self._ctrl.click(SIDEBAR_CLICK_X, target_y) |
182 | | - return True |
| 168 | + return |
183 | 169 |
|
184 | | - def navigate_to_chapter(self, target: int) -> int | None: # noqa: C901, PLR0912, PLR0915 |
| 170 | + def navigate_to_chapter(self, target: int) -> int | None: |
185 | 171 | """导航到指定章节 (通过 OCR 识别当前位置并批量点击)。 |
186 | 172 |
|
187 | 173 | 远距离章节切换时采用批量点击 + 充分等待的策略, |
@@ -261,30 +247,14 @@ def _read_chapter( |
261 | 247 | continue |
262 | 248 |
|
263 | 249 | delta = target - current |
264 | | - direction = -1 if delta < 0 else 1 |
265 | | - steps = abs(delta) |
266 | | - |
267 | | - # 远距离批量点击,近距离逐步点击 |
268 | | - if steps > 2: |
269 | | - batch = min(steps, 4) |
270 | | - _log.info('[UI] 章节导航: 批量点击 {} 章', batch) |
271 | | - for _ in range(batch): |
272 | | - ok = self.click_prev_chapter() if direction < 0 else self.click_next_chapter() |
273 | | - if not ok: |
274 | | - _log.warning('[UI] 章节导航: 点击失败,终止') |
275 | | - return None |
276 | | - time.sleep(0.3) |
277 | | - # 批量点击后充分等待动画完全结束,避免 OCR 抖动 |
278 | | - time.sleep(1.0) |
279 | | - else: |
280 | | - if direction < 0: |
281 | | - ok = self.click_prev_chapter(screen) |
282 | | - else: |
283 | | - ok = self.click_next_chapter(screen) |
284 | | - if not ok: |
285 | | - _log.warning('[UI] 章节导航: 点击失败,终止') |
286 | | - return None |
287 | | - time.sleep(CHAPTER_NAV_DELAY) |
| 250 | + direction = 1 if delta > 0 else -1 |
| 251 | + remaining = abs(delta) |
| 252 | + while remaining > 0: |
| 253 | + step = min(remaining, 3) * direction |
| 254 | + self.click_chapter(step) |
| 255 | + remaining -= abs(step) |
| 256 | + _log.info(f'[UI] 章节导航: 跳转{step}章, 剩余{remaining}章') |
| 257 | + time.sleep(CHAPTER_NAV_DELAY * abs(step)) |
288 | 258 |
|
289 | 259 | _log.warning( |
290 | 260 | '[UI] 章节导航: 超过最大尝试次数 ({}), 目标第 {} 章', |
|
0 commit comments