Add patrol route cycling and point offsets
This commit is contained in:
@@ -260,6 +260,8 @@ class AutoBotMove:
|
|||||||
distance_interact_pause_sec=None,
|
distance_interact_pause_sec=None,
|
||||||
mailbox_route_path=None,
|
mailbox_route_path=None,
|
||||||
skip_prepare_route=False,
|
skip_prepare_route=False,
|
||||||
|
patrol_route_switch_laps=0,
|
||||||
|
patrol_point_offset_radius=0.15,
|
||||||
):
|
):
|
||||||
self.last_tab_time = 0
|
self.last_tab_time = 0
|
||||||
self.last_interaction_time = 0 # 记录上一次按互动键的时间
|
self.last_interaction_time = 0 # 记录上一次按互动键的时间
|
||||||
@@ -291,6 +293,14 @@ class AutoBotMove:
|
|||||||
self._last_mouse_path_scale_signature = None
|
self._last_mouse_path_scale_signature = None
|
||||||
self.logistics_resume_cooldown_until = 0.0
|
self.logistics_resume_cooldown_until = 0.0
|
||||||
self.skip_prepare_route = bool(skip_prepare_route)
|
self.skip_prepare_route = bool(skip_prepare_route)
|
||||||
|
try:
|
||||||
|
self.patrol_route_switch_laps = max(0, int(patrol_route_switch_laps or 0))
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
self.patrol_route_switch_laps = 0
|
||||||
|
try:
|
||||||
|
self.patrol_point_offset_radius = max(0.0, float(patrol_point_offset_radius or 0.0))
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
self.patrol_point_offset_radius = 0.0
|
||||||
# stop_check: 返回 True 表示需要立即停止(用于中断阻塞中的后勤/路线导航)
|
# stop_check: 返回 True 表示需要立即停止(用于中断阻塞中的后勤/路线导航)
|
||||||
self._stop_check = stop_check if callable(stop_check) else (lambda: False)
|
self._stop_check = stop_check if callable(stop_check) else (lambda: False)
|
||||||
|
|
||||||
@@ -298,6 +308,8 @@ class AutoBotMove:
|
|||||||
self.route_profile_name = ""
|
self.route_profile_name = ""
|
||||||
self.patrol_routes = []
|
self.patrol_routes = []
|
||||||
self.current_patrol_route_name = ""
|
self.current_patrol_route_name = ""
|
||||||
|
self.current_patrol_route_index = None
|
||||||
|
self.current_patrol_route_laps = 0
|
||||||
|
|
||||||
if route_profile_path:
|
if route_profile_path:
|
||||||
self.route_profile = load_route_profile(route_profile_path)
|
self.route_profile = load_route_profile(route_profile_path)
|
||||||
@@ -386,18 +398,27 @@ class AutoBotMove:
|
|||||||
if self.patrol_routes and self.prepare_route_completed:
|
if self.patrol_routes and self.prepare_route_completed:
|
||||||
self._select_random_patrol_route("启动")
|
self._select_random_patrol_route("启动")
|
||||||
|
|
||||||
def _select_random_patrol_route(self, reason=""):
|
def _select_random_patrol_route(self, reason="", avoid_current=False):
|
||||||
if not self.patrol_routes:
|
if not self.patrol_routes:
|
||||||
return False
|
return False
|
||||||
route = random.choice(self.patrol_routes)
|
candidates = list(enumerate(self.patrol_routes))
|
||||||
|
if avoid_current and len(candidates) > 1 and self.current_patrol_route_index is not None:
|
||||||
|
candidates = [
|
||||||
|
item for item in candidates
|
||||||
|
if item[0] != self.current_patrol_route_index
|
||||||
|
]
|
||||||
|
route_index, route = random.choice(candidates)
|
||||||
points = list(route.get("points") or [])
|
points = list(route.get("points") or [])
|
||||||
if not points:
|
if not points:
|
||||||
return False
|
return False
|
||||||
self.current_patrol_route_name = str(route.get("name") or "未命名巡逻路线")
|
self.current_patrol_route_name = str(route.get("name") or "未命名巡逻路线")
|
||||||
|
self.current_patrol_route_index = route_index
|
||||||
|
self.current_patrol_route_laps = 0
|
||||||
reverse_route = random.choice((False, True))
|
reverse_route = random.choice((False, True))
|
||||||
direction_name = "逆向" if reverse_route else "正向"
|
direction_name = "逆向" if reverse_route else "正向"
|
||||||
if reverse_route:
|
if reverse_route:
|
||||||
points = list(reversed(points))
|
points = list(reversed(points))
|
||||||
|
points = self._apply_patrol_point_offset(points)
|
||||||
self.patrol_controller.waypoints = points
|
self.patrol_controller.waypoints = points
|
||||||
self.patrol_controller.current_index = 0
|
self.patrol_controller.current_index = 0
|
||||||
self.patrol_controller.reset_stuck()
|
self.patrol_controller.reset_stuck()
|
||||||
@@ -408,6 +429,48 @@ class AutoBotMove:
|
|||||||
)
|
)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def _apply_patrol_point_offset(self, points):
|
||||||
|
radius = self.patrol_point_offset_radius
|
||||||
|
if radius <= 0:
|
||||||
|
return [(float(point[0]), float(point[1])) for point in points]
|
||||||
|
|
||||||
|
shifted = []
|
||||||
|
for point in points:
|
||||||
|
x, y = float(point[0]), float(point[1])
|
||||||
|
angle = random.uniform(0.0, math.tau)
|
||||||
|
distance = radius * math.sqrt(random.random())
|
||||||
|
shifted.append((
|
||||||
|
x + math.cos(angle) * distance,
|
||||||
|
y + math.sin(angle) * distance,
|
||||||
|
))
|
||||||
|
print(f">>> [巡逻路线] 已为本次路线应用随机点位偏移,半径: {radius:.2f}")
|
||||||
|
return shifted
|
||||||
|
|
||||||
|
def _maybe_switch_patrol_route_after_lap(self, before_index, after_index):
|
||||||
|
if self.patrol_route_switch_laps <= 0:
|
||||||
|
return
|
||||||
|
waypoints = getattr(self.patrol_controller, "waypoints", None) or []
|
||||||
|
if not waypoints:
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
before_index = int(before_index)
|
||||||
|
after_index = int(after_index)
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
return
|
||||||
|
if before_index != len(waypoints) - 1 or after_index != 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.current_patrol_route_laps += 1
|
||||||
|
print(
|
||||||
|
f">>> [巡逻路线] {self.current_patrol_route_name} "
|
||||||
|
f"已完成 {self.current_patrol_route_laps}/{self.patrol_route_switch_laps} 圈"
|
||||||
|
)
|
||||||
|
if self.current_patrol_route_laps >= self.patrol_route_switch_laps:
|
||||||
|
self._select_random_patrol_route(
|
||||||
|
f"{self.current_patrol_route_name} 跑满 {self.patrol_route_switch_laps} 圈",
|
||||||
|
avoid_current=True,
|
||||||
|
)
|
||||||
|
|
||||||
def _has_prepare_route(self) -> bool:
|
def _has_prepare_route(self) -> bool:
|
||||||
return bool(self.prepare_route_waypoints)
|
return bool(self.prepare_route_waypoints)
|
||||||
|
|
||||||
@@ -1021,7 +1084,10 @@ class AutoBotMove:
|
|||||||
self._run_prepare_route_step(state)
|
self._run_prepare_route_step(state)
|
||||||
else:
|
else:
|
||||||
self.is_moving = True
|
self.is_moving = True
|
||||||
|
before_index = self.patrol_controller.current_index
|
||||||
self.patrol_controller.navigate(state)
|
self.patrol_controller.navigate(state)
|
||||||
|
after_index = self.patrol_controller.current_index
|
||||||
|
self._maybe_switch_patrol_route_after_lap(before_index, after_index)
|
||||||
|
|
||||||
# 5. 顺便每隔几秒按一下 Tab(主动找怪)
|
# 5. 顺便每隔几秒按一下 Tab(主动找怪)
|
||||||
if self.prepare_route_completed and not effective_target and (time.time() - self.last_tab_time > 2.0):
|
if self.prepare_route_completed and not effective_target and (time.time() - self.last_tab_time > 2.0):
|
||||||
|
|||||||
@@ -392,6 +392,8 @@ class GameLoopWorker(QThread):
|
|||||||
turn_error_hold_sec=None,
|
turn_error_hold_sec=None,
|
||||||
distance_interact_pause_sec=None,
|
distance_interact_pause_sec=None,
|
||||||
skip_prepare_route=False,
|
skip_prepare_route=False,
|
||||||
|
patrol_route_switch_laps=0,
|
||||||
|
patrol_point_offset_radius=0.15,
|
||||||
):
|
):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.mode = mode # 'monitor' | 'patrol' | 'combat' | 'quest_follow' | 'flight' | 'record'
|
self.mode = mode # 'monitor' | 'patrol' | 'combat' | 'quest_follow' | 'flight' | 'record'
|
||||||
@@ -407,6 +409,14 @@ class GameLoopWorker(QThread):
|
|||||||
self.vendor_path = vendor_path
|
self.vendor_path = vendor_path
|
||||||
self.mailbox_route_path = mailbox_route_path
|
self.mailbox_route_path = mailbox_route_path
|
||||||
self.skip_prepare_route = bool(skip_prepare_route)
|
self.skip_prepare_route = bool(skip_prepare_route)
|
||||||
|
try:
|
||||||
|
self.patrol_route_switch_laps = max(0, int(patrol_route_switch_laps or 0))
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
self.patrol_route_switch_laps = 0
|
||||||
|
try:
|
||||||
|
self.patrol_point_offset_radius = max(0.0, float(patrol_point_offset_radius or 0.0))
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
self.patrol_point_offset_radius = 0.0
|
||||||
self.record_filename = record_filename or 'waypoints'
|
self.record_filename = record_filename or 'waypoints'
|
||||||
self.record_min_distance = record_min_distance
|
self.record_min_distance = record_min_distance
|
||||||
self.attack_loop_path = attack_loop_path or None
|
self.attack_loop_path = attack_loop_path or None
|
||||||
@@ -499,6 +509,8 @@ class GameLoopWorker(QThread):
|
|||||||
distance_interact_pause_sec=self.distance_interact_pause_sec,
|
distance_interact_pause_sec=self.distance_interact_pause_sec,
|
||||||
mailbox_route_path=self.mailbox_route_path,
|
mailbox_route_path=self.mailbox_route_path,
|
||||||
skip_prepare_route=self.skip_prepare_route,
|
skip_prepare_route=self.skip_prepare_route,
|
||||||
|
patrol_route_switch_laps=self.patrol_route_switch_laps,
|
||||||
|
patrol_point_offset_radius=self.patrol_point_offset_radius,
|
||||||
)
|
)
|
||||||
self.bot_move._on_hearthstone_stop = self.stop_signal.emit
|
self.bot_move._on_hearthstone_stop = self.stop_signal.emit
|
||||||
except ImportError as e:
|
except ImportError as e:
|
||||||
@@ -810,11 +822,25 @@ class WoWMultiKeyGUI(QMainWindow):
|
|||||||
self.patrol_attack_loop_combo.setMinimumWidth(200)
|
self.patrol_attack_loop_combo.setMinimumWidth(200)
|
||||||
self.skip_prepare_route_check = QCheckBox("跳过准备路线")
|
self.skip_prepare_route_check = QCheckBox("跳过准备路线")
|
||||||
self.skip_prepare_route_check.setChecked(False)
|
self.skip_prepare_route_check.setChecked(False)
|
||||||
|
self.patrol_route_switch_laps_spin = QSpinBox()
|
||||||
|
self.patrol_route_switch_laps_spin.setRange(0, 999)
|
||||||
|
self.patrol_route_switch_laps_spin.setValue(0)
|
||||||
|
self.patrol_route_switch_laps_spin.setSuffix(" 圈")
|
||||||
|
self.patrol_route_switch_laps_spin.setSpecialValueText("不自动轮换")
|
||||||
|
self.patrol_point_offset_spin = QDoubleSpinBox()
|
||||||
|
self.patrol_point_offset_spin.setRange(0.0, 2.0)
|
||||||
|
self.patrol_point_offset_spin.setDecimals(2)
|
||||||
|
self.patrol_point_offset_spin.setSingleStep(0.05)
|
||||||
|
self.patrol_point_offset_spin.setValue(0.15)
|
||||||
|
self.patrol_point_offset_spin.setSuffix(" 坐标")
|
||||||
|
self.patrol_point_offset_spin.setSpecialValueText("不偏移")
|
||||||
self._refresh_recorder_combos()
|
self._refresh_recorder_combos()
|
||||||
refresh_btn = QPushButton("🔄 刷新列表")
|
refresh_btn = QPushButton("🔄 刷新列表")
|
||||||
refresh_btn.clicked.connect(self._refresh_recorder_combos)
|
refresh_btn.clicked.connect(self._refresh_recorder_combos)
|
||||||
patrol_layout.addRow("路线方案 JSON:", self.route_profile_combo)
|
patrol_layout.addRow("路线方案 JSON:", self.route_profile_combo)
|
||||||
patrol_layout.addRow("", self.skip_prepare_route_check)
|
patrol_layout.addRow("", self.skip_prepare_route_check)
|
||||||
|
patrol_layout.addRow("巡逻路线轮换:", self.patrol_route_switch_laps_spin)
|
||||||
|
patrol_layout.addRow("巡逻点偏移半径:", self.patrol_point_offset_spin)
|
||||||
patrol_layout.addRow("攻击循环:", self.patrol_attack_loop_combo)
|
patrol_layout.addRow("攻击循环:", self.patrol_attack_loop_combo)
|
||||||
patrol_layout.addRow("", refresh_btn)
|
patrol_layout.addRow("", refresh_btn)
|
||||||
self.patrol_group.setVisible(False)
|
self.patrol_group.setVisible(False)
|
||||||
@@ -1814,6 +1840,8 @@ class WoWMultiKeyGUI(QMainWindow):
|
|||||||
resurrection_route_a_path = None
|
resurrection_route_a_path = None
|
||||||
resurrection_route_b_path = None
|
resurrection_route_b_path = None
|
||||||
skip_prepare_route = False
|
skip_prepare_route = False
|
||||||
|
patrol_route_switch_laps = 0
|
||||||
|
patrol_point_offset_radius = 0.15
|
||||||
if mode == 'patrol':
|
if mode == 'patrol':
|
||||||
profile_path = self.route_profile_combo.currentData() or ""
|
profile_path = self.route_profile_combo.currentData() or ""
|
||||||
if not profile_path:
|
if not profile_path:
|
||||||
@@ -1835,6 +1863,8 @@ class WoWMultiKeyGUI(QMainWindow):
|
|||||||
return
|
return
|
||||||
route_profile_path = profile_path
|
route_profile_path = profile_path
|
||||||
skip_prepare_route = self.skip_prepare_route_check.isChecked()
|
skip_prepare_route = self.skip_prepare_route_check.isChecked()
|
||||||
|
patrol_route_switch_laps = int(self.patrol_route_switch_laps_spin.value())
|
||||||
|
patrol_point_offset_radius = float(self.patrol_point_offset_spin.value())
|
||||||
attack_loop_path = None
|
attack_loop_path = None
|
||||||
if mode == 'patrol':
|
if mode == 'patrol':
|
||||||
attack_loop_path = self.patrol_attack_loop_combo.currentData() or None
|
attack_loop_path = self.patrol_attack_loop_combo.currentData() or None
|
||||||
@@ -1945,6 +1975,8 @@ class WoWMultiKeyGUI(QMainWindow):
|
|||||||
turn_error_hold_sec=turn_error_hold_sec,
|
turn_error_hold_sec=turn_error_hold_sec,
|
||||||
distance_interact_pause_sec=distance_interact_pause_sec,
|
distance_interact_pause_sec=distance_interact_pause_sec,
|
||||||
skip_prepare_route=skip_prepare_route,
|
skip_prepare_route=skip_prepare_route,
|
||||||
|
patrol_route_switch_laps=patrol_route_switch_laps,
|
||||||
|
patrol_point_offset_radius=patrol_point_offset_radius,
|
||||||
)
|
)
|
||||||
self.game_worker.state_signal.connect(self.state_label.setText)
|
self.game_worker.state_signal.connect(self.state_label.setText)
|
||||||
self.game_worker.log_signal.connect(self.log)
|
self.game_worker.log_signal.connect(self.log)
|
||||||
|
|||||||
Reference in New Issue
Block a user