add 巡逻模式优先上马
@@ -5,7 +5,7 @@ import sys
|
||||
import time
|
||||
import pydirectinput
|
||||
|
||||
from game_state import parse_game_state
|
||||
from game_state import parse_game_state, load_layout_config
|
||||
from coordinate_patrol import CoordinatePatrol
|
||||
from death_manager import DeathManager
|
||||
from logistics_manager import LogisticsManager
|
||||
@@ -91,7 +91,13 @@ class AutoBotMove:
|
||||
if waypoints is None:
|
||||
path = waypoints_path or get_config_path(WAYPOINTS_FILE)
|
||||
waypoints = load_waypoints(path) or DEFAULT_WAYPOINTS
|
||||
self.patrol_controller = CoordinatePatrol(waypoints)
|
||||
layout = load_layout_config()
|
||||
self.patrol_controller = CoordinatePatrol(
|
||||
waypoints,
|
||||
mount_key=str(layout.get("mount_key", "x") or "x"),
|
||||
mount_hold_sec=float(layout.get("mount_hold_sec", 1.6)),
|
||||
mount_retry_after_sec=float(layout.get("mount_retry_after_sec", 2.0)),
|
||||
)
|
||||
self.death_manager = DeathManager(self.patrol_controller)
|
||||
vendor_file = vendor_path or get_config_path('vendor.json')
|
||||
self.logistics_manager = LogisticsManager(vendor_file)
|
||||
|
||||
@@ -15,7 +15,6 @@ ANGLE_DEADZONE_DEG = 5.0 # 死区,此范围内不按 A/D、只按 W,减
|
||||
# 随机行为:跳跃概率(每帧触发概率,0.005 ≈ 平均 200 帧一次)
|
||||
RANDOM_JUMP_PROB = 0.05
|
||||
|
||||
|
||||
class CoordinatePatrol:
|
||||
"""按航点坐标巡逻,用 pyautogui 按键转向与前进。"""
|
||||
|
||||
@@ -26,6 +25,9 @@ class CoordinatePatrol:
|
||||
angle_threshold_deg=ANGLE_THRESHOLD_DEG,
|
||||
angle_deadzone_deg=ANGLE_DEADZONE_DEG,
|
||||
random_jump_prob=RANDOM_JUMP_PROB,
|
||||
mount_key="x",
|
||||
mount_hold_sec=1.6,
|
||||
mount_retry_after_sec=2.0,
|
||||
):
|
||||
"""
|
||||
Args:
|
||||
@@ -33,6 +35,7 @@ class CoordinatePatrol:
|
||||
arrival_threshold: 到达判定距离(游戏坐标单位),默认 0.5
|
||||
angle_threshold_deg: 朝向容差(度),超过此值才按 A/D 转向,默认 ANGLE_THRESHOLD_DEG
|
||||
angle_deadzone_deg: 转向死区(度),此范围内不按 A/D、只按 W,默认 ANGLE_DEADZONE_DEG
|
||||
mount_key / mount_hold_sec / mount_retry_after_sec: 未上马时先上马(与 game_state_config / GUI 一致)
|
||||
"""
|
||||
self.waypoints = waypoints
|
||||
self.current_index = 0
|
||||
@@ -43,6 +46,33 @@ class CoordinatePatrol:
|
||||
self.logger = logging.getLogger(__name__)
|
||||
self.last_turn_end_time = 0 # 最近一次结束 A/D 转向的时间,供卡死检测排除原地转向
|
||||
self.stuck_handler = StuckHandler()
|
||||
self._next_mount_allowed = 0.0
|
||||
self.mount_key = str(mount_key).strip() or "x"
|
||||
self.mount_hold_sec = float(mount_hold_sec)
|
||||
self.mount_retry_after_sec = float(mount_retry_after_sec)
|
||||
|
||||
def _ensure_mounted(self, state):
|
||||
"""
|
||||
已上马返回 True。
|
||||
未上马则松开移动键、按住上马键 MOUNT_HOLD_SEC,本帧不走路;返回 False。
|
||||
state 无 mounted 字段时视为无法判断,不拦巡逻(兼容未开 LogicBeacon)。
|
||||
"""
|
||||
if "mounted" not in state:
|
||||
return True
|
||||
if state.get("mounted"):
|
||||
return True
|
||||
self.stop_all()
|
||||
now = time.time()
|
||||
if now < self._next_mount_allowed:
|
||||
return False
|
||||
self.logger.info(
|
||||
f">>> 未上马,先按 {self.mount_key} {self.mount_hold_sec:.1f}s 上马"
|
||||
)
|
||||
pyautogui.keyDown(self.mount_key)
|
||||
time.sleep(self.mount_hold_sec)
|
||||
pyautogui.keyUp(self.mount_key)
|
||||
self._next_mount_allowed = time.time() + self.mount_retry_after_sec
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def get_distance(p1, p2):
|
||||
@@ -168,6 +198,9 @@ class CoordinatePatrol:
|
||||
self.stop_all()
|
||||
return
|
||||
|
||||
if not self._ensure_mounted(state):
|
||||
return
|
||||
|
||||
# 2. 卡死检测:位移检测逻辑保持在巡逻中
|
||||
if self.stuck_handler.check_stuck(state, self.last_turn_end_time):
|
||||
self.logger.error("!!! 检测到卡死,启动脱困程序 !!!")
|
||||
@@ -250,6 +283,9 @@ class CoordinatePatrol:
|
||||
self.stop_all()
|
||||
return False
|
||||
|
||||
if not self._ensure_mounted(state):
|
||||
return False
|
||||
|
||||
# 1. 卡死检测
|
||||
if self.stuck_handler.check_stuck(state, self.last_turn_end_time):
|
||||
self.stop_all()
|
||||
@@ -311,6 +347,7 @@ class CoordinatePatrol:
|
||||
"""
|
||||
按 path 依次走完所有点后返回。每次调用都必须传入 path。
|
||||
get_state: 可调用对象,每次调用返回当前状态 dict,需包含 'x','y','facing'。
|
||||
若含 'mounted'(LogicBeacon),路径开始前会先上马,每段 navigate_to_point 也会校验。
|
||||
path: [[x,y], ...] 或 [(x,y), ...](如 json.load 得到),本次要走的全部航点。
|
||||
forward: True=正序(0→n),False=倒序(n→0)。
|
||||
阻塞直到走完 path 中所有点或出错,走完返回 True。内含卡死检测。
|
||||
@@ -321,6 +358,17 @@ class CoordinatePatrol:
|
||||
points = [(float(p[0]), float(p[1])) for p in path]
|
||||
if not forward:
|
||||
points = points[::-1]
|
||||
|
||||
# 路径开始前:若 state 含 mounted 且未上马,先完成上马再逐点移动
|
||||
while True:
|
||||
state = get_state()
|
||||
if state is None:
|
||||
self.stop_all()
|
||||
return False
|
||||
if self._ensure_mounted(state):
|
||||
break
|
||||
time.sleep(0.05)
|
||||
|
||||
for i, target in enumerate(points):
|
||||
self.logger.info(f">>> 路径点 {i + 1}/{len(points)}: {target}")
|
||||
while True:
|
||||
|
||||
@@ -21,6 +21,10 @@ _DEFAULTS = {
|
||||
"scan_region_height": 15,
|
||||
"offset_left": 20,
|
||||
"offset_top": 45,
|
||||
# 巡逻上马(coordinate_patrol,与 GUI「参数配置」一致)
|
||||
"mount_key": "x",
|
||||
"mount_hold_sec": 1.6,
|
||||
"mount_retry_after_sec": 2.0,
|
||||
}
|
||||
|
||||
SCREENSHOT_DIR = 'screenshot'
|
||||
|
||||
@@ -4,5 +4,8 @@
|
||||
"scan_region_width": 190,
|
||||
"scan_region_height": 15,
|
||||
"offset_left": 20,
|
||||
"offset_top": 45
|
||||
"offset_top": 45,
|
||||
"mount_key": "x",
|
||||
"mount_hold_sec": 1.6,
|
||||
"mount_retry_after_sec": 2.0
|
||||
}
|
||||
|
||||
142
recorder/地狱火半岛剥皮310-375.json
Normal file
@@ -0,0 +1,142 @@
|
||||
[
|
||||
[
|
||||
12.1,
|
||||
54.99
|
||||
],
|
||||
[
|
||||
11.62,
|
||||
54.79
|
||||
],
|
||||
[
|
||||
11.16,
|
||||
54.54
|
||||
],
|
||||
[
|
||||
10.82,
|
||||
54.13
|
||||
],
|
||||
[
|
||||
10.54,
|
||||
53.7
|
||||
],
|
||||
[
|
||||
10.3,
|
||||
53.24
|
||||
],
|
||||
[
|
||||
10.06,
|
||||
52.75
|
||||
],
|
||||
[
|
||||
9.82,
|
||||
52.31
|
||||
],
|
||||
[
|
||||
9.55,
|
||||
51.86
|
||||
],
|
||||
[
|
||||
9.42,
|
||||
51.35
|
||||
],
|
||||
[
|
||||
9.39,
|
||||
50.83
|
||||
],
|
||||
[
|
||||
9.24,
|
||||
50.35
|
||||
],
|
||||
[
|
||||
9.15,
|
||||
49.85
|
||||
],
|
||||
[
|
||||
9.5,
|
||||
49.44
|
||||
],
|
||||
[
|
||||
9.94,
|
||||
49.14
|
||||
],
|
||||
[
|
||||
10.38,
|
||||
48.85
|
||||
],
|
||||
[
|
||||
10.81,
|
||||
48.56
|
||||
],
|
||||
[
|
||||
11.23,
|
||||
48.26
|
||||
],
|
||||
[
|
||||
11.65,
|
||||
47.94
|
||||
],
|
||||
[
|
||||
12.11,
|
||||
47.65
|
||||
],
|
||||
[
|
||||
12.59,
|
||||
47.49
|
||||
],
|
||||
[
|
||||
12.54,
|
||||
48.01
|
||||
],
|
||||
[
|
||||
12.44,
|
||||
48.53
|
||||
],
|
||||
[
|
||||
12.32,
|
||||
49.03
|
||||
],
|
||||
[
|
||||
12.2,
|
||||
49.54
|
||||
],
|
||||
[
|
||||
12.1,
|
||||
50.07
|
||||
],
|
||||
[
|
||||
12.1,
|
||||
50.6
|
||||
],
|
||||
[
|
||||
12.33,
|
||||
51.1
|
||||
],
|
||||
[
|
||||
12.55,
|
||||
51.59
|
||||
],
|
||||
[
|
||||
12.68,
|
||||
52.11
|
||||
],
|
||||
[
|
||||
12.78,
|
||||
52.62
|
||||
],
|
||||
[
|
||||
12.86,
|
||||
53.12
|
||||
],
|
||||
[
|
||||
12.83,
|
||||
53.65
|
||||
],
|
||||
[
|
||||
12.76,
|
||||
54.17
|
||||
],
|
||||
[
|
||||
12.1,
|
||||
54.99
|
||||
]
|
||||
]
|
||||
|
Before Width: | Height: | Size: 88 B After Width: | Height: | Size: 667 B |
|
Before Width: | Height: | Size: 74 B After Width: | Height: | Size: 91 B |
|
Before Width: | Height: | Size: 74 B After Width: | Height: | Size: 87 B |
|
Before Width: | Height: | Size: 74 B After Width: | Height: | Size: 84 B |
|
Before Width: | Height: | Size: 74 B After Width: | Height: | Size: 90 B |
|
Before Width: | Height: | Size: 74 B After Width: | Height: | Size: 83 B |
|
Before Width: | Height: | Size: 74 B After Width: | Height: | Size: 91 B |
|
Before Width: | Height: | Size: 74 B After Width: | Height: | Size: 88 B |
|
Before Width: | Height: | Size: 74 B After Width: | Height: | Size: 84 B |
|
Before Width: | Height: | Size: 74 B After Width: | Height: | Size: 83 B |
@@ -69,11 +69,11 @@ class StuckHandler:
|
||||
pyautogui.keyDown(turn_key)
|
||||
|
||||
# 倒车时间稍长一点,离开障碍物
|
||||
time.sleep(0.3)
|
||||
time.sleep(0.5)
|
||||
|
||||
# 3. 尝试跳跃脱离地形卡位
|
||||
pyautogui.press("space")
|
||||
time.sleep(0.3)
|
||||
time.sleep(0.5)
|
||||
|
||||
pyautogui.keyUp(turn_key)
|
||||
pyautogui.keyUp("s")
|
||||
|
||||
@@ -746,6 +746,24 @@ class WoWMultiKeyGUI(QMainWindow):
|
||||
self.skinning_wait_spin.setSuffix(" 秒")
|
||||
params_right.addRow("剥皮等待时间:", self.skinning_wait_spin)
|
||||
|
||||
self.gs_mount_key = QLineEdit()
|
||||
self.gs_mount_key.setPlaceholderText("如 x")
|
||||
self.gs_mount_key.setMaxLength(16)
|
||||
self.gs_mount_key.setText("x")
|
||||
self.gs_mount_hold = QDoubleSpinBox()
|
||||
self.gs_mount_hold.setRange(0.1, 30.0)
|
||||
self.gs_mount_hold.setSingleStep(0.1)
|
||||
self.gs_mount_hold.setValue(1.6)
|
||||
self.gs_mount_hold.setSuffix(" 秒")
|
||||
self.gs_mount_retry = QDoubleSpinBox()
|
||||
self.gs_mount_retry.setRange(0.0, 60.0)
|
||||
self.gs_mount_retry.setSingleStep(0.1)
|
||||
self.gs_mount_retry.setValue(2.0)
|
||||
self.gs_mount_retry.setSuffix(" 秒")
|
||||
params_right.addRow("上马按键 (mount_key):", self.gs_mount_key)
|
||||
params_right.addRow("上马按住时长 (mount_hold_sec):", self.gs_mount_hold)
|
||||
params_right.addRow("上马重试间隔 (mount_retry_after_sec):", self.gs_mount_retry)
|
||||
|
||||
params_row.addLayout(params_left)
|
||||
params_row.addLayout(params_right)
|
||||
params_layout.addWidget(params_content)
|
||||
@@ -780,6 +798,9 @@ class WoWMultiKeyGUI(QMainWindow):
|
||||
self.gs_scan_height.setValue(cfg.get('scan_region_height', 15))
|
||||
self.gs_offset_left.setValue(cfg.get('offset_left', 20))
|
||||
self.gs_offset_top.setValue(cfg.get('offset_top', 45))
|
||||
self.gs_mount_key.setText(str(cfg.get('mount_key', 'x') or 'x'))
|
||||
self.gs_mount_hold.setValue(float(cfg.get('mount_hold_sec', 1.6)))
|
||||
self.gs_mount_retry.setValue(float(cfg.get('mount_retry_after_sec', 2.0)))
|
||||
except Exception as e:
|
||||
self.log(f"加载参数配置失败: {e}")
|
||||
try:
|
||||
@@ -800,6 +821,9 @@ class WoWMultiKeyGUI(QMainWindow):
|
||||
cfg['scan_region_height'] = self.gs_scan_height.value()
|
||||
cfg['offset_left'] = self.gs_offset_left.value()
|
||||
cfg['offset_top'] = self.gs_offset_top.value()
|
||||
cfg['mount_key'] = (self.gs_mount_key.text().strip() or 'x')
|
||||
cfg['mount_hold_sec'] = float(self.gs_mount_hold.value())
|
||||
cfg['mount_retry_after_sec'] = float(self.gs_mount_retry.value())
|
||||
path = save_layout_config(cfg)
|
||||
# bot 参数写入主配置文件
|
||||
self.config = self.config or {}
|
||||
|
||||