diff --git a/__pycache__/coordinate_patrol.cpython-311.pyc b/__pycache__/coordinate_patrol.cpython-311.pyc index 0264042..6a4e09b 100644 Binary files a/__pycache__/coordinate_patrol.cpython-311.pyc and b/__pycache__/coordinate_patrol.cpython-311.pyc differ diff --git a/__pycache__/flight_mode.cpython-311.pyc b/__pycache__/flight_mode.cpython-311.pyc new file mode 100644 index 0000000..5075963 Binary files /dev/null and b/__pycache__/flight_mode.cpython-311.pyc differ diff --git a/__pycache__/game_state.cpython-311.pyc b/__pycache__/game_state.cpython-311.pyc index 40147b9..4cde15b 100644 Binary files a/__pycache__/game_state.cpython-311.pyc and b/__pycache__/game_state.cpython-311.pyc differ diff --git a/__pycache__/stuck_handler.cpython-311.pyc b/__pycache__/stuck_handler.cpython-311.pyc index 8a476f8..2e3f414 100644 Binary files a/__pycache__/stuck_handler.cpython-311.pyc and b/__pycache__/stuck_handler.cpython-311.pyc differ diff --git a/__pycache__/wow_multikey_gui.cpython-311.pyc b/__pycache__/wow_multikey_gui.cpython-311.pyc new file mode 100644 index 0000000..44049fb Binary files /dev/null and b/__pycache__/wow_multikey_gui.cpython-311.pyc differ diff --git a/build_wow_multikey.spec b/build_wow_multikey.spec index 5024875..1291b22 100644 --- a/build_wow_multikey.spec +++ b/build_wow_multikey.spec @@ -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={}, diff --git a/coordinate_patrol.py b/coordinate_patrol.py index e14f2f5..99e8aba 100644 --- a/coordinate_patrol.py +++ b/coordinate_patrol.py @@ -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。 diff --git a/docs/history.md b/docs/history.md index f4f532f..fa5e8d3 100644 --- a/docs/history.md +++ b/docs/history.md @@ -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`。 + diff --git a/flight_mode.py b/flight_mode.py new file mode 100644 index 0000000..e2f3792 --- /dev/null +++ b/flight_mode.py @@ -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("✅ 飞行模式:已到达目的地并降落完成") + diff --git a/screenshot/game_state.png b/screenshot/game_state.png index f0d9946..d98fe35 100644 Binary files a/screenshot/game_state.png and b/screenshot/game_state.png differ diff --git a/screenshot/game_state_combat.png b/screenshot/game_state_combat.png index 0359d24..2da3e80 100644 Binary files a/screenshot/game_state_combat.png and b/screenshot/game_state_combat.png differ diff --git a/screenshot/game_state_flight_block.png b/screenshot/game_state_flight_block.png index 231f311..c2b7168 100644 Binary files a/screenshot/game_state_flight_block.png and b/screenshot/game_state_flight_block.png differ diff --git a/screenshot/game_state_follow.png b/screenshot/game_state_follow.png index 3f5cb77..ceb5a74 100644 Binary files a/screenshot/game_state_follow.png and b/screenshot/game_state_follow.png differ diff --git a/screenshot/game_state_hp.png b/screenshot/game_state_hp.png index 2d0c1fa..479024f 100644 Binary files a/screenshot/game_state_hp.png and b/screenshot/game_state_hp.png differ diff --git a/screenshot/game_state_logistics_death.png b/screenshot/game_state_logistics_death.png index 89e6979..3277361 100644 Binary files a/screenshot/game_state_logistics_death.png and b/screenshot/game_state_logistics_death.png differ diff --git a/screenshot/game_state_mp.png b/screenshot/game_state_mp.png index 89e3074..2da3e80 100644 Binary files a/screenshot/game_state_mp.png and b/screenshot/game_state_mp.png differ diff --git a/screenshot/game_state_target.png b/screenshot/game_state_target.png index 704ad58..871eaa7 100644 Binary files a/screenshot/game_state_target.png and b/screenshot/game_state_target.png differ diff --git a/screenshot/game_state_x.png b/screenshot/game_state_x.png index d1b2d16..8a70a1e 100644 Binary files a/screenshot/game_state_x.png and b/screenshot/game_state_x.png differ diff --git a/screenshot/game_state_y.png b/screenshot/game_state_y.png index 5b8c1f9..304e98e 100644 Binary files a/screenshot/game_state_y.png and b/screenshot/game_state_y.png differ diff --git a/wow_multikey_gui.py b/wow_multikey_gui.py index 825437a..63b176a 100644 --- a/wow_multikey_gui.py +++ b/wow_multikey_gui.py @@ -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]} 已启动") diff --git a/wow_multikey_qt.json b/wow_multikey_qt.json new file mode 100644 index 0000000..712d558 --- /dev/null +++ b/wow_multikey_qt.json @@ -0,0 +1,11 @@ +{ + "keys": {}, + "bot": { + "flight": { + "takeoff_key": "space", + "takeoff_hold_sec": 2.0, + "land_key": "p", + "land_hold_sec": 2.0 + } + } +} \ No newline at end of file