add 飞行模式
BIN
__pycache__/flight_mode.cpython-311.pyc
Normal file
BIN
__pycache__/wow_multikey_gui.cpython-311.pyc
Normal file
@@ -21,6 +21,7 @@ a = Analysis(
|
||||
'coordinate_patrol', 'death_manager', 'logistics_manager',
|
||||
'stuck_handler', 'player_movement', 'player_position',
|
||||
'pydirectinput', 'pygetwindow', 'pyautogui', 'PIL',
|
||||
'flight_mode',
|
||||
],
|
||||
hookspath=[],
|
||||
hooksconfig={},
|
||||
|
||||
@@ -343,6 +343,136 @@ class CoordinatePatrol:
|
||||
|
||||
return False
|
||||
|
||||
def simple_flight_navigate(
|
||||
self,
|
||||
state,
|
||||
target_x,
|
||||
target_y,
|
||||
*,
|
||||
takeoff_key="space",
|
||||
takeoff_hold_sec=2.0,
|
||||
land_key="p",
|
||||
land_hold_sec=4.0,
|
||||
dismount_key=None,
|
||||
arrival_threshold=0.003,
|
||||
turn_deadzone_deg=5.0,
|
||||
mount_wait_sec=2.5,
|
||||
):
|
||||
"""
|
||||
极简飞行导航:只做 1.上马 2.起飞 3.飞向坐标 4.降落。
|
||||
|
||||
说明:
|
||||
- 当前项目的飞行状态使用 `state['flight']`(0/0.5/1),并换算到 0..255 再按阈值判断阶段。
|
||||
- 只做“极简控制”,不包含卡死检测与复杂脱困逻辑。
|
||||
"""
|
||||
if state is None:
|
||||
return False
|
||||
|
||||
curr_x, curr_y = state.get("x"), state.get("y")
|
||||
curr_facing = state.get("facing")
|
||||
if None in (curr_x, curr_y, curr_facing):
|
||||
return False
|
||||
|
||||
# 统一 mount 逻辑:优先使用 mounted 字段
|
||||
if "mounted" in state and state.get("mounted") is False:
|
||||
# 用与其它逻辑一致的上马方式,按 game_state_config 的 mount_key/mount_hold_sec
|
||||
ok = self._ensure_mounted(state)
|
||||
return False if not ok else False
|
||||
|
||||
# 归一化飞行信号:把 state['flight'] 映射为 0..255
|
||||
flight_raw = state.get("flight", None)
|
||||
|
||||
f_signal = None
|
||||
if isinstance(flight_raw, (int, float)):
|
||||
# 若是三档 0/0.5/1,则换算到 0..255
|
||||
if float(flight_raw) <= 1.0:
|
||||
f_signal = float(flight_raw) * 255.0
|
||||
else:
|
||||
f_signal = float(flight_raw)
|
||||
|
||||
# 没有飞行信息:只能退化为“先尝试上马,再尝试起飞”
|
||||
if f_signal is None:
|
||||
ok = self._ensure_mounted(state)
|
||||
if not ok:
|
||||
return False
|
||||
pyautogui.keyDown(str(takeoff_key))
|
||||
time.sleep(float(takeoff_hold_sec))
|
||||
pyautogui.keyUp(str(takeoff_key))
|
||||
return False
|
||||
|
||||
# 计算距离
|
||||
dist = math.sqrt((target_x - float(curr_x)) ** 2 + (target_y - float(curr_y)) ** 2)
|
||||
dismount_key = str(dismount_key).strip() if dismount_key else self.mount_key
|
||||
dismount_key = dismount_key or "x"
|
||||
|
||||
# --- 1. 上马阶段(兼容你的阈值写法:f_signal < 10) ---
|
||||
if f_signal < 10:
|
||||
# 退化:直接按 mount_key 一次并等待
|
||||
self.logger.info(">>> 飞行阶段检测:未上马,先按 %s", self.mount_key)
|
||||
pyautogui.press(self.mount_key)
|
||||
time.sleep(float(mount_wait_sec))
|
||||
return False
|
||||
|
||||
# --- 2. 起飞阶段(兼容你的阈值写法:100~200) ---
|
||||
if 100 < f_signal < 200:
|
||||
self.logger.info(">>> 起飞:按住 %s %.1f 秒", takeoff_key, float(takeoff_hold_sec))
|
||||
pyautogui.keyDown(str(takeoff_key))
|
||||
time.sleep(float(takeoff_hold_sec))
|
||||
pyautogui.keyUp(str(takeoff_key))
|
||||
return False
|
||||
|
||||
# --- 3. 巡航与降落判定(兼容你的阈值写法:>240) ---
|
||||
if f_signal > 240:
|
||||
if dist > float(arrival_threshold):
|
||||
# A. 始终按住 W 前进
|
||||
pyautogui.keyDown("w")
|
||||
|
||||
# B. 修正方向(使用与本模块一致的朝向计算)
|
||||
target_heading = self.get_target_heading_deg((float(curr_x), float(curr_y)), (float(target_x), float(target_y)))
|
||||
angle_diff = (target_heading - float(curr_facing) + 180) % 360 - 180
|
||||
abs_diff = abs(angle_diff)
|
||||
|
||||
# 在死区内:确保转向键松开,减少左右抖动
|
||||
if abs_diff <= float(turn_deadzone_deg):
|
||||
pyautogui.keyUp("a")
|
||||
pyautogui.keyUp("d")
|
||||
return False
|
||||
|
||||
# 防止“上一个修正刚结束,下一帧立刻反向再修正”造成摆头
|
||||
now = time.time()
|
||||
last_t = getattr(self, "_last_simple_flight_turn_time", 0.0)
|
||||
if now - last_t < 0.08:
|
||||
return False
|
||||
|
||||
key = "d" if angle_diff > 0 else "a"
|
||||
other_key = "a" if key == "d" else "d"
|
||||
pyautogui.keyUp(other_key)
|
||||
pyautogui.keyDown(key)
|
||||
|
||||
# 修正脉冲时长随角度误差变化,避免固定 0.05s 过冲/欠冲
|
||||
duration = self._turn_duration_from_angle(abs_diff)
|
||||
safe_duration = min(float(duration), 0.12)
|
||||
# 误差只略大于死区时,进一步缩短,减少接近目标时的来回摆
|
||||
if abs_diff < float(turn_deadzone_deg) * 1.8:
|
||||
safe_duration = min(safe_duration, 0.04)
|
||||
|
||||
time.sleep(safe_duration)
|
||||
pyautogui.keyUp(key)
|
||||
setattr(self, "_last_simple_flight_turn_time", now)
|
||||
return False
|
||||
|
||||
# C. 到达目标,执行降落
|
||||
pyautogui.keyUp("w")
|
||||
self.logger.info(">>> 到达目标,执行降落:按住 %s %.1f 秒", land_key, float(land_hold_sec))
|
||||
pyautogui.keyDown(str(land_key))
|
||||
time.sleep(float(land_hold_sec))
|
||||
pyautogui.keyUp(str(land_key))
|
||||
pyautogui.press(dismount_key)
|
||||
self.logger.info(">>> 降落完成:已按 %s", dismount_key)
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def navigate_path(self, get_state, path, forward=True, arrival_threshold=None):
|
||||
"""
|
||||
按 path 依次走完所有点后返回。每次调用都必须传入 path。
|
||||
|
||||
@@ -17,3 +17,17 @@
|
||||
1. 打开 GUI → **参数配置** → 设置 **剥皮等待时间** → 点击 **保存配置**
|
||||
2. 启动 **巡逻打怪** 或 **自动打怪**,剥皮等待时间按保存值生效。
|
||||
|
||||
## 2026-03-23
|
||||
|
||||
### 飞行模式接入 GUI
|
||||
|
||||
- **GUI 更新**:在 `wow_multikey_gui.py` 的「状态/巡逻/打怪」模式按钮中新增 **飞行模式**(放在「任务跟随」后面)。
|
||||
- **新建飞行类**:新增 `flight_mode.py`,实现飞行模式状态推进逻辑。
|
||||
- **目的地选择**:可在飞行模式配置中选择坐标 JSON,取 JSON 中 **最后一个有效坐标点** 作为目的地。
|
||||
- **运动逻辑**:飞行过程中每帧调用 `coordinate_patrol.py` 的 `simple_flight_navigate()` 完成上马、起飞、飞向坐标与降落。
|
||||
|
||||
### 飞行模式降落按键默认改为 p
|
||||
|
||||
- **GUI 更新**:在 `wow_multikey_gui.py` 的「飞行模式配置」页,「降落按键」默认值与占位提示从 `x` 改为 `p`。
|
||||
- **行为更新**:`flight_mode.py` 中 `FlightModeBot` 的默认 `land_key` 从 `x` 改为 `p`,保证未手动填写/未在配置中存在时仍使用 `p`。
|
||||
|
||||
|
||||
113
flight_mode.py
Normal file
@@ -0,0 +1,113 @@
|
||||
"""
|
||||
飞行模式逻辑:
|
||||
- 从坐标 JSON 读取最后一个坐标点作为目的地
|
||||
- 每帧调用 `CoordinatePatrol.simple_flight_navigate()` 执行:
|
||||
1) 上马(未上马则先上马,mount_key/mount_hold_sec 从 game_state_config.json 读取)
|
||||
2) 起飞
|
||||
3) 飞向目的地坐标
|
||||
4) 降落
|
||||
"""
|
||||
|
||||
import json
|
||||
import logging
|
||||
|
||||
from coordinate_patrol import CoordinatePatrol
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _load_last_waypoint_from_json(path):
|
||||
"""读取与巡逻相同的 JSON 格式:[[x,y], ...],返回最后一个有效点 (x, y)。"""
|
||||
if not path:
|
||||
return None
|
||||
try:
|
||||
with open(path, "r", encoding="utf-8") as f:
|
||||
data = json.load(f)
|
||||
except Exception as e:
|
||||
logger.warning("读取航线 JSON 失败: %s", e)
|
||||
return None
|
||||
if not isinstance(data, list):
|
||||
return None
|
||||
last = None
|
||||
for item in data:
|
||||
if isinstance(item, (list, tuple)) and len(item) >= 2:
|
||||
try:
|
||||
last = (float(item[0]), float(item[1]))
|
||||
except (TypeError, ValueError):
|
||||
continue
|
||||
return last
|
||||
|
||||
|
||||
class FlightModeBot:
|
||||
"""飞行模式控制器:每次执行逻辑只推动状态前进一步。"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
json_path,
|
||||
log_callback=None,
|
||||
*,
|
||||
takeoff_key="space",
|
||||
takeoff_hold_sec=2.0,
|
||||
land_key="p",
|
||||
land_hold_sec=2.0,
|
||||
):
|
||||
self._log = log_callback or (lambda _m: None)
|
||||
self.destination = _load_last_waypoint_from_json(json_path)
|
||||
self.done = False
|
||||
|
||||
# 上马按键与时长来自 game_state_config.json
|
||||
try:
|
||||
from game_state import load_layout_config
|
||||
|
||||
cfg = load_layout_config()
|
||||
mount_key = str(cfg.get("mount_key", "x") or "x")
|
||||
mount_hold_sec = float(cfg.get("mount_hold_sec", 1.6))
|
||||
mount_retry_after_sec = float(cfg.get("mount_retry_after_sec", 2.0))
|
||||
except Exception:
|
||||
mount_key, mount_hold_sec, mount_retry_after_sec = "x", 1.6, 2.0
|
||||
|
||||
# 只用于 simple_flight_navigate 内部的 _ensure_mounted 与一些公共参数
|
||||
self._patrol = CoordinatePatrol(
|
||||
waypoints=[(0.0, 0.0)] if self.destination else [],
|
||||
mount_key=mount_key,
|
||||
mount_hold_sec=mount_hold_sec,
|
||||
mount_retry_after_sec=mount_retry_after_sec,
|
||||
)
|
||||
|
||||
self.takeoff_key = takeoff_key
|
||||
self.takeoff_hold_sec = float(takeoff_hold_sec)
|
||||
self.land_key = land_key
|
||||
self.land_hold_sec = float(land_hold_sec)
|
||||
|
||||
if not self.destination:
|
||||
self._log("❌ 飞行模式:JSON 中无有效坐标点")
|
||||
self.done = True
|
||||
|
||||
def execute_logic(self, state):
|
||||
if self.done:
|
||||
return
|
||||
if state is None:
|
||||
return
|
||||
if not self.destination:
|
||||
self.done = True
|
||||
return
|
||||
|
||||
arrived = self._patrol.simple_flight_navigate(
|
||||
state,
|
||||
self.destination[0],
|
||||
self.destination[1],
|
||||
takeoff_key=self.takeoff_key,
|
||||
takeoff_hold_sec=self.takeoff_hold_sec,
|
||||
land_key=self.land_key,
|
||||
land_hold_sec=self.land_hold_sec,
|
||||
dismount_key=self._patrol.mount_key,
|
||||
arrival_threshold=self._patrol.arrival_threshold,
|
||||
mount_wait_sec=self._patrol.mount_hold_sec,
|
||||
)
|
||||
|
||||
if arrived:
|
||||
self._patrol.stop_all()
|
||||
self.done = True
|
||||
self._log("✅ 飞行模式:已到达目的地并降落完成")
|
||||
|
||||
|
Before Width: | Height: | Size: 667 B After Width: | Height: | Size: 532 B |
|
Before Width: | Height: | Size: 91 B After Width: | Height: | Size: 74 B |
|
Before Width: | Height: | Size: 87 B After Width: | Height: | Size: 87 B |
|
Before Width: | Height: | Size: 84 B After Width: | Height: | Size: 84 B |
|
Before Width: | Height: | Size: 90 B After Width: | Height: | Size: 86 B |
|
Before Width: | Height: | Size: 83 B After Width: | Height: | Size: 83 B |
|
Before Width: | Height: | Size: 91 B After Width: | Height: | Size: 74 B |
|
Before Width: | Height: | Size: 88 B After Width: | Height: | Size: 86 B |
|
Before Width: | Height: | Size: 84 B After Width: | Height: | Size: 84 B |
|
Before Width: | Height: | Size: 83 B After Width: | Height: | Size: 83 B |
@@ -292,7 +292,7 @@ class KeyWorker(QThread):
|
||||
# ============ 游戏主循环(状态监控 / 巡逻打怪 / 自动打怪 / 任务跟随 / 录制) ============
|
||||
|
||||
class GameLoopWorker(QThread):
|
||||
"""游戏状态主循环:支持状态监控、巡逻打怪、自动打怪、任务跟随、巡逻点录制"""
|
||||
"""游戏状态主循环:支持状态监控、巡逻打怪、自动打怪、任务跟随、飞行模式、巡逻点录制"""
|
||||
state_signal = pyqtSignal(str)
|
||||
log_signal = pyqtSignal(str)
|
||||
|
||||
@@ -308,13 +308,19 @@ class GameLoopWorker(QThread):
|
||||
quest_follow_follow_key=None,
|
||||
quest_follow_interact_key=None,
|
||||
quest_follow_interval_sec=None,
|
||||
flight_json_path=None,
|
||||
flight_takeoff_key=None,
|
||||
flight_takeoff_hold_sec=None,
|
||||
flight_land_key=None,
|
||||
flight_land_hold_sec=None,
|
||||
):
|
||||
super().__init__()
|
||||
self.mode = mode # 'monitor' | 'patrol' | 'combat' | 'quest_follow' | 'record'
|
||||
self.mode = mode # 'monitor' | 'patrol' | 'combat' | 'quest_follow' | 'flight' | 'record'
|
||||
self.running = True
|
||||
self.bot_move = None
|
||||
self.bot_combat = None
|
||||
self.quest_follow_bot = None
|
||||
self.flight_bot = None
|
||||
self.recorder = None
|
||||
self.waypoints_path = waypoints_path
|
||||
self.vendor_path = vendor_path
|
||||
@@ -328,6 +334,11 @@ class GameLoopWorker(QThread):
|
||||
self.quest_follow_interval_sec = float(quest_follow_interval_sec)
|
||||
except (TypeError, ValueError):
|
||||
self.quest_follow_interval_sec = 3.0
|
||||
self.flight_json_path = flight_json_path
|
||||
self.flight_takeoff_key = flight_takeoff_key
|
||||
self.flight_takeoff_hold_sec = flight_takeoff_hold_sec
|
||||
self.flight_land_key = flight_land_key
|
||||
self.flight_land_hold_sec = flight_land_hold_sec
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
@@ -373,6 +384,24 @@ class GameLoopWorker(QThread):
|
||||
self.log_signal.emit(f"❌ 任务跟随依赖加载失败: {e}")
|
||||
return
|
||||
|
||||
if self.mode == 'flight':
|
||||
if not self.flight_json_path:
|
||||
self.log_signal.emit("❌ 飞行模式:未选择航线 JSON")
|
||||
return
|
||||
try:
|
||||
from flight_mode import FlightModeBot
|
||||
self.flight_bot = FlightModeBot(
|
||||
json_path=self.flight_json_path,
|
||||
log_callback=lambda m: self.log_signal.emit(m),
|
||||
takeoff_key=self.flight_takeoff_key,
|
||||
takeoff_hold_sec=self.flight_takeoff_hold_sec,
|
||||
land_key=self.flight_land_key,
|
||||
land_hold_sec=self.flight_land_hold_sec,
|
||||
)
|
||||
except ImportError as e:
|
||||
self.log_signal.emit(f"❌ 飞行模式依赖加载失败: {e}")
|
||||
return
|
||||
|
||||
if self.mode == 'record':
|
||||
try:
|
||||
from recorder import WaypointRecorder
|
||||
@@ -403,6 +432,8 @@ class GameLoopWorker(QThread):
|
||||
self.bot_combat.execute_logic(state)
|
||||
elif self.mode == 'record' and self.recorder:
|
||||
self.recorder.record(state)
|
||||
elif self.mode == 'flight' and self.flight_bot:
|
||||
self.flight_bot.execute_logic(state)
|
||||
if self.mode == 'quest_follow' and self.quest_follow_bot:
|
||||
self.quest_follow_bot.execute_logic(state)
|
||||
time.sleep(0.1)
|
||||
@@ -491,6 +522,7 @@ class WoWMultiKeyGUI(QMainWindow):
|
||||
('patrol', '巡逻打怪'),
|
||||
('combat', '自动打怪'),
|
||||
('quest_follow', '任务跟随'),
|
||||
('flight', '飞行模式'),
|
||||
]:
|
||||
btn = QPushButton(name)
|
||||
btn.setCheckable(True)
|
||||
@@ -553,6 +585,53 @@ class WoWMultiKeyGUI(QMainWindow):
|
||||
self.quest_follow_group.setVisible(False)
|
||||
game_layout.addWidget(self.quest_follow_group)
|
||||
|
||||
ff_cfg = ((self.config or {}).get('bot') or {}).get('flight') or {}
|
||||
self.flight_group = QGroupBox("飞行模式配置")
|
||||
flight_layout = QFormLayout(self.flight_group)
|
||||
self.flight_json_combo = QComboBox()
|
||||
self.flight_json_combo.setMinimumWidth(200)
|
||||
self._refresh_flight_json_combo()
|
||||
flight_refresh_btn = QPushButton("🔄 刷新列表")
|
||||
flight_refresh_btn.clicked.connect(self._refresh_flight_json_combo)
|
||||
flight_layout.addRow("航线 JSON(取最后一点为目的地):", self.flight_json_combo)
|
||||
|
||||
self.flight_takeoff_key_edit = QLineEdit()
|
||||
self.flight_takeoff_key_edit.setPlaceholderText("如 space 或 空格")
|
||||
self.flight_takeoff_key_edit.setMaxLength(16)
|
||||
self.flight_takeoff_key_edit.setText(str(ff_cfg.get('takeoff_key', 'space')).strip() or 'space')
|
||||
|
||||
self.flight_takeoff_hold_spin = QDoubleSpinBox()
|
||||
self.flight_takeoff_hold_spin.setRange(0.1, 120.0)
|
||||
self.flight_takeoff_hold_spin.setSingleStep(0.1)
|
||||
try:
|
||||
self.flight_takeoff_hold_spin.setValue(float(ff_cfg.get('takeoff_hold_sec', 2.0)))
|
||||
except (TypeError, ValueError):
|
||||
self.flight_takeoff_hold_spin.setValue(2.0)
|
||||
self.flight_takeoff_hold_spin.setSuffix(" 秒")
|
||||
|
||||
self.flight_land_key_edit = QLineEdit()
|
||||
self.flight_land_key_edit.setPlaceholderText("如 p")
|
||||
self.flight_land_key_edit.setMaxLength(16)
|
||||
self.flight_land_key_edit.setText(str(ff_cfg.get('land_key', 'p')).strip() or 'p')
|
||||
|
||||
self.flight_land_hold_spin = QDoubleSpinBox()
|
||||
self.flight_land_hold_spin.setRange(0.1, 120.0)
|
||||
self.flight_land_hold_spin.setSingleStep(0.1)
|
||||
try:
|
||||
self.flight_land_hold_spin.setValue(float(ff_cfg.get('land_hold_sec', 2.0)))
|
||||
except (TypeError, ValueError):
|
||||
self.flight_land_hold_spin.setValue(2.0)
|
||||
self.flight_land_hold_spin.setSuffix(" 秒")
|
||||
|
||||
flight_layout.addRow("起飞按键:", self.flight_takeoff_key_edit)
|
||||
flight_layout.addRow("起飞按住时长:", self.flight_takeoff_hold_spin)
|
||||
flight_layout.addRow("降落按键:", self.flight_land_key_edit)
|
||||
flight_layout.addRow("降落按住时长:", self.flight_land_hold_spin)
|
||||
|
||||
flight_layout.addRow("", flight_refresh_btn)
|
||||
self.flight_group.setVisible(False)
|
||||
game_layout.addWidget(self.flight_group)
|
||||
|
||||
self.state_label = QLabel("状态: ---")
|
||||
self.state_label.setStyleSheet("font-family: monospace; padding: 6px; background: #1e1e1e; color: #d4d4d4;")
|
||||
self.state_label.setMinimumHeight(36)
|
||||
@@ -971,6 +1050,7 @@ class WoWMultiKeyGUI(QMainWindow):
|
||||
"""切换 Tab 时加载参数或刷新下拉列表"""
|
||||
if index == 1: # 状态/巡逻/打怪
|
||||
self._refresh_recorder_combos()
|
||||
self._refresh_flight_json_combo()
|
||||
elif index == 3: # 巡逻点预览
|
||||
self._refresh_preview_waypoints()
|
||||
elif index == 4: # 攻击循环
|
||||
@@ -1029,6 +1109,18 @@ class WoWMultiKeyGUI(QMainWindow):
|
||||
elif combo.count() > 1:
|
||||
combo.setCurrentIndex(1)
|
||||
|
||||
def _refresh_flight_json_combo(self):
|
||||
"""刷新飞行模式航线 JSON 下拉列表"""
|
||||
items = list_recorder_json()
|
||||
self.flight_json_combo.blockSignals(True)
|
||||
self.flight_json_combo.clear()
|
||||
self.flight_json_combo.addItem("-- 请选择 --", "")
|
||||
for name, path in items:
|
||||
self.flight_json_combo.addItem(name, path)
|
||||
self.flight_json_combo.blockSignals(False)
|
||||
if self.flight_json_combo.count() > 1:
|
||||
self.flight_json_combo.setCurrentIndex(1)
|
||||
|
||||
def set_game_mode(self, mode):
|
||||
if self.game_worker and self.game_worker.isRunning():
|
||||
self.log("请先停止当前模式再切换")
|
||||
@@ -1045,6 +1137,9 @@ class WoWMultiKeyGUI(QMainWindow):
|
||||
self.patrol_group.setVisible(mode == 'patrol')
|
||||
self.combat_group.setVisible(mode == 'combat')
|
||||
self.quest_follow_group.setVisible(mode == 'quest_follow')
|
||||
self.flight_group.setVisible(mode == 'flight')
|
||||
if mode == 'flight':
|
||||
self._refresh_flight_json_combo()
|
||||
|
||||
def load_config(self):
|
||||
if os.path.exists(self.config_path):
|
||||
@@ -1124,10 +1219,11 @@ class WoWMultiKeyGUI(QMainWindow):
|
||||
def start_game_loop(self):
|
||||
mode = self._current_game_mode
|
||||
if mode is None:
|
||||
QMessageBox.warning(self, "提示", "请先选择模式(状态监控 / 巡逻打怪 / 自动打怪 / 任务跟随)")
|
||||
QMessageBox.warning(self, "提示", "请先选择模式(状态监控 / 巡逻打怪 / 自动打怪 / 任务跟随 / 飞行模式)")
|
||||
return
|
||||
waypoints_path = None
|
||||
vendor_path = None
|
||||
flight_json_path = None
|
||||
if mode == 'patrol':
|
||||
wp = self.waypoints_combo.currentData() or ""
|
||||
vp = self.vendor_combo.currentData() or ""
|
||||
@@ -1163,6 +1259,25 @@ class WoWMultiKeyGUI(QMainWindow):
|
||||
}
|
||||
self._save_main_config()
|
||||
|
||||
if mode == 'flight':
|
||||
fp = self.flight_json_combo.currentData() or ""
|
||||
if not fp:
|
||||
QMessageBox.warning(self, "提示", "飞行模式需选择航线 JSON 文件")
|
||||
return
|
||||
if not os.path.exists(fp):
|
||||
QMessageBox.warning(self, "提示", f"航线文件不存在: {fp}")
|
||||
return
|
||||
flight_json_path = fp
|
||||
self.config = self.config or {}
|
||||
self.config.setdefault('bot', {})
|
||||
self.config['bot']['flight'] = {
|
||||
'takeoff_key': self.flight_takeoff_key_edit.text().strip() or 'space',
|
||||
'takeoff_hold_sec': float(self.flight_takeoff_hold_spin.value()),
|
||||
'land_key': self.flight_land_key_edit.text().strip() or 'p',
|
||||
'land_hold_sec': float(self.flight_land_hold_spin.value()),
|
||||
}
|
||||
self._save_main_config()
|
||||
|
||||
skinning_wait_sec = None
|
||||
try:
|
||||
skinning_wait_sec = float(((self.config or {}).get('bot') or {}).get('skinning_wait_sec', 1.5))
|
||||
@@ -1176,6 +1291,11 @@ class WoWMultiKeyGUI(QMainWindow):
|
||||
quest_follow_follow_key=self.quest_follow_follow_edit.text(),
|
||||
quest_follow_interact_key=self.quest_follow_interact_edit.text(),
|
||||
quest_follow_interval_sec=self.quest_follow_interval_spin.value(),
|
||||
flight_json_path=flight_json_path,
|
||||
flight_takeoff_key=self.flight_takeoff_key_edit.text(),
|
||||
flight_takeoff_hold_sec=self.flight_takeoff_hold_spin.value(),
|
||||
flight_land_key=self.flight_land_key_edit.text(),
|
||||
flight_land_hold_sec=self.flight_land_hold_spin.value(),
|
||||
)
|
||||
self.game_worker.state_signal.connect(self.state_label.setText)
|
||||
self.game_worker.log_signal.connect(self.log)
|
||||
@@ -1188,6 +1308,7 @@ class WoWMultiKeyGUI(QMainWindow):
|
||||
'patrol': '巡逻打怪',
|
||||
'combat': '自动打怪',
|
||||
'quest_follow': '任务跟随',
|
||||
'flight': '飞行模式',
|
||||
}
|
||||
self.status_bar.showMessage(f"🟢 {mode_names[mode]} 运行中")
|
||||
self.log(f"🎮 {mode_names[mode]} 已启动")
|
||||
|
||||
11
wow_multikey_qt.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"keys": {},
|
||||
"bot": {
|
||||
"flight": {
|
||||
"takeoff_key": "space",
|
||||
"takeoff_hold_sec": 2.0,
|
||||
"land_key": "p",
|
||||
"land_hold_sec": 2.0
|
||||
}
|
||||
}
|
||||
}
|
||||