@@ -109,6 +109,13 @@ def click_team(self) -> OperationRoundResult:
109109 @node_from (from_name = '选择编队' , status = STATUS_CONTINUE_CHOOSE )
110110 @operation_node (name = '选择编队' )
111111 def choose_team (self ) -> OperationRoundResult :
112+ """
113+ 分轮完成防卫战预备编队的扫描、评分与选择。
114+
115+ 扫描完成后先回到目标编队所在页面。游戏可能已有预选编队,
116+ 此时需取消预选并等待原按钮恢复为 SELECT,避免直接选择失败。
117+ 每次点击后也要等待画面确认选中,再继续处理下一支编队。
118+ """
112119 if not self .shiyu_team_selected :
113120 scan_finished = self ._scan_team_page (self .last_screenshot )
114121 log .info (
@@ -237,6 +244,13 @@ def click_confirm(self) -> OperationRoundResult:
237244 return self .round_retry (result .status , wait = 1 )
238245
239246 def _scan_team_page (self , screen : MatLike ) -> bool :
247+ """
248+ 扫描当前页面的预备编队,返回是否应结束扫描。
249+
250+ 相邻页面会重复显示卡片,按队名去重。识别到队名但没有任何代理人时,
251+ 视为首个空队并停止扫描。被禁用的队伍不参与评分,但仍保留其游戏列表
252+ 序号,避免后续有效队的翻页与点击位置错位。
253+ """
240254 ocr_result_map = self .ctx .ocr .run_ocr (screen )
241255
242256 for title_y in self .CARD_TITLE_Y_LIST :
@@ -287,6 +301,8 @@ def _scan_team_page(self, screen: MatLike) -> bool:
287301 if team_idx >= self .MAX_TEAM_COUNT :
288302 log .info ('预备编队扫描结束:已达到最大队伍数量:%d' , self .MAX_TEAM_COUNT )
289303 return True
304+ # 被禁用的队伍不参与自动配队,但仍占用游戏列表中的位置。
305+ # 必须先递增真实序号,再跳过候选列表;否则后续有效队会错位。
290306 self .next_scanned_team_idx += 1
291307 self .scanned_team_name_set .add (team_name )
292308
@@ -384,6 +400,7 @@ def _find_selected_button(
384400 ocr_result_map : dict [str , MatchResultList ],
385401 team_name_mr : MatchResult ,
386402 ) -> MatchResult | None :
403+ """查找当前卡片的已选状态,用于在缺少 SELECT 时先取消预选。"""
387404 candidate_list : list [MatchResult ] = []
388405 for text , mr_list in ocr_result_map .items ():
389406 if not self ._is_selected_text (text .replace (' ' , '' ).upper ()):
@@ -408,6 +425,7 @@ def _is_select_button_visible(
408425 self ,
409426 ocr_result_map : dict [str , MatchResultList ],
410427 ) -> bool :
428+ """确认取消后原按钮区域恢复 SELECT,避免误认其他卡片的 SELECT。"""
411429 if self .pending_cancel_button_center is None :
412430 return False
413431
@@ -427,6 +445,7 @@ def _is_select_button_visible(
427445
428446 @staticmethod
429447 def _is_selected_text (normalized_text : str ) -> bool :
448+ """统一已选按钮的 OCR 结果,仅在目标卡片的按钮区域内使用。"""
430449 return (
431450 normalized_text == 'SELECTED'
432451 or normalized_text == 'TEAM'
@@ -489,6 +508,12 @@ def _get_team_agent_slot_set(
489508 ocr_result_map : dict [str , MatchResultList ],
490509 team_name_mr : MatchResult ,
491510 ) -> set [str ]:
511+ """
512+ 获取当前卡片识别到的代理人槽位标记。
513+
514+ 1P、2P、3P 任一缺失表示该队被禁用。角色头像仍可能被识别到,
515+ 因此不能仅凭头像判断可用性;OCR 结果也必须限制在当前卡片范围内。
516+ """
492517 agent_rect = Rect (
493518 team_name_mr .left_top .x - 10 ,
494519 team_name_mr .left_top .y ,
@@ -539,6 +564,12 @@ def _recognize_team_agents(
539564 return [mr .data for mr in filtered_mr_list ]
540565
541566 def _scroll_team_list (self , direction : int ) -> None :
567+ """
568+ 按固定坐标拖动列表。
569+
570+ 固定坐标避免鼠标停留位置影响翻页;向下扫描和向上回退使用相反方向,
571+ 每次拖动对应 current_scroll_page 的一页变化。
572+ """
542573 if direction < 0 :
543574 self .ctx .controller .drag_to (
544575 start = ChoosePredefinedTeam .TEAM_DRAG_START ,
0 commit comments