diff --git a/.gitignore b/.gitignore index 4c62ef3..495b271 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ # 忽略打包后的静态资源(你的核心需求) dist/ build/ +dist.zip # 忽略依赖库 node_modules/ diff --git a/quest_follow.py b/quest_follow.py new file mode 100644 index 0000000..db769ed --- /dev/null +++ b/quest_follow.py @@ -0,0 +1,38 @@ +""" +任务跟随:按固定间隔向游戏发送「跟随」与「交互」按键(pydirectinput,与 auto_bot 一致)。 +""" + +import time + +import pydirectinput + + +class QuestFollowBot: + """每隔 interval_sec 秒依次按下跟随键、交互键;不读取游戏状态。""" + + def __init__( + self, + follow_key="f", + interact_key="4", + interval_sec=3.0, + log_callback=None, + ): + self.follow_key = (follow_key or "f").strip().lower() or "f" + self.interact_key = (interact_key or "4").strip().lower() or "4" + self.interval_sec = max(0.5, float(interval_sec)) + self._last_cycle = 0.0 + self._log = log_callback or (lambda _m: None) + + def execute_logic(self, state): + """state 可为 None;仅用于与时间间隔配合的定时按键。""" + now = time.time() + if now - self._last_cycle < self.interval_sec: + return + self._last_cycle = now + try: + pydirectinput.press(self.follow_key) + time.sleep(0.08) + pydirectinput.press(self.interact_key) + self._log(f"➡️ 任务跟随: {self.follow_key} → {self.interact_key}") + except Exception as e: + self._log(f"❌ 任务跟随按键失败: {e}") diff --git a/wow_multikey_gui.py b/wow_multikey_gui.py index b3ab0f1..cfc1f04 100644 --- a/wow_multikey_gui.py +++ b/wow_multikey_gui.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 """ WoW 多功能控制器 GUI -集成:状态监控、巡逻打怪、自动打怪、巡逻点录制、多键控制 +集成:状态监控、巡逻打怪、自动打怪、任务跟随、巡逻点录制、多键控制 """ import json @@ -289,10 +289,10 @@ class KeyWorker(QThread): self.log_signal.emit(f"❌ 按键 {self.key_name} 失败: {e}") -# ============ 游戏主循环(状态监控 / 巡逻打怪 / 自动打怪 / 录制) ============ +# ============ 游戏主循环(状态监控 / 巡逻打怪 / 自动打怪 / 任务跟随 / 录制) ============ class GameLoopWorker(QThread): - """游戏状态主循环:支持状态监控、巡逻打怪、自动打怪、巡逻点录制""" + """游戏状态主循环:支持状态监控、巡逻打怪、自动打怪、任务跟随、巡逻点录制""" state_signal = pyqtSignal(str) log_signal = pyqtSignal(str) @@ -305,12 +305,16 @@ class GameLoopWorker(QThread): record_min_distance=None, attack_loop_path=None, skinning_wait_sec=None, + quest_follow_follow_key=None, + quest_follow_interact_key=None, + quest_follow_interval_sec=None, ): super().__init__() - self.mode = mode # 'monitor' | 'patrol' | 'combat' | 'record' + self.mode = mode # 'monitor' | 'patrol' | 'combat' | 'quest_follow' | 'record' self.running = True self.bot_move = None self.bot_combat = None + self.quest_follow_bot = None self.recorder = None self.waypoints_path = waypoints_path self.vendor_path = vendor_path @@ -318,6 +322,12 @@ class GameLoopWorker(QThread): self.record_min_distance = record_min_distance self.attack_loop_path = attack_loop_path or None self.skinning_wait_sec = skinning_wait_sec + self.quest_follow_follow_key = (quest_follow_follow_key or "f").strip().lower() or "f" + self.quest_follow_interact_key = (quest_follow_interact_key or "4").strip().lower() or "4" + try: + self.quest_follow_interval_sec = float(quest_follow_interval_sec) + except (TypeError, ValueError): + self.quest_follow_interval_sec = 3.0 def run(self): try: @@ -350,6 +360,19 @@ class GameLoopWorker(QThread): self.log_signal.emit(f"❌ 自动打怪依赖加载失败: {e}") return + if self.mode == 'quest_follow': + try: + from quest_follow import QuestFollowBot + self.quest_follow_bot = QuestFollowBot( + follow_key=self.quest_follow_follow_key, + interact_key=self.quest_follow_interact_key, + interval_sec=self.quest_follow_interval_sec, + log_callback=lambda m: self.log_signal.emit(m), + ) + except ImportError as e: + self.log_signal.emit(f"❌ 任务跟随依赖加载失败: {e}") + return + if self.mode == 'record': try: from recorder import WaypointRecorder @@ -392,6 +415,8 @@ class GameLoopWorker(QThread): self.bot_combat.execute_logic(state) elif self.mode == 'record' and self.recorder: self.recorder.record(state) + if self.mode == 'quest_follow' and self.quest_follow_bot: + self.quest_follow_bot.execute_logic(state) time.sleep(0.1) def save_recorder(self): @@ -477,6 +502,7 @@ class WoWMultiKeyGUI(QMainWindow): ('monitor', '状态监控'), ('patrol', '巡逻打怪'), ('combat', '自动打怪'), + ('quest_follow', '任务跟随'), ]: btn = QPushButton(name) btn.setCheckable(True) @@ -514,6 +540,31 @@ class WoWMultiKeyGUI(QMainWindow): self.combat_group.setVisible(False) game_layout.addWidget(self.combat_group) + qf_cfg = ((self.config or {}).get('bot') or {}).get('quest_follow') or {} + self.quest_follow_group = QGroupBox("任务跟随配置") + qf_layout = QFormLayout(self.quest_follow_group) + self.quest_follow_follow_edit = QLineEdit() + self.quest_follow_follow_edit.setPlaceholderText("如 f(跟随队友)") + self.quest_follow_follow_edit.setMaxLength(8) + self.quest_follow_follow_edit.setText(str(qf_cfg.get('follow_key', 'f')).strip() or 'f') + self.quest_follow_interact_edit = QLineEdit() + self.quest_follow_interact_edit.setPlaceholderText("如 4(与互动键一致)") + self.quest_follow_interact_edit.setMaxLength(8) + self.quest_follow_interact_edit.setText(str(qf_cfg.get('interact_key', '4')).strip() or '4') + self.quest_follow_interval_spin = QDoubleSpinBox() + self.quest_follow_interval_spin.setRange(0.5, 120.0) + self.quest_follow_interval_spin.setSingleStep(0.5) + try: + self.quest_follow_interval_spin.setValue(float(qf_cfg.get('interval_sec', 3.0))) + except (TypeError, ValueError): + self.quest_follow_interval_spin.setValue(3.0) + self.quest_follow_interval_spin.setSuffix(" 秒") + qf_layout.addRow("跟随键:", self.quest_follow_follow_edit) + qf_layout.addRow("交互键:", self.quest_follow_interact_edit) + qf_layout.addRow("按键间隔:", self.quest_follow_interval_spin) + self.quest_follow_group.setVisible(False) + game_layout.addWidget(self.quest_follow_group) + self.state_label = QLabel("状态: ---") self.state_label.setStyleSheet("font-family: monospace; padding: 6px; background: #1e1e1e; color: #d4d4d4;") self.state_label.setMinimumHeight(36) @@ -981,6 +1032,7 @@ class WoWMultiKeyGUI(QMainWindow): self._refresh_attack_loop_combo() self.patrol_group.setVisible(mode == 'patrol') self.combat_group.setVisible(mode == 'combat') + self.quest_follow_group.setVisible(mode == 'quest_follow') def load_config(self): if os.path.exists(self.config_path): @@ -1060,7 +1112,7 @@ 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 @@ -1089,6 +1141,16 @@ class WoWMultiKeyGUI(QMainWindow): if attack_loop_path and not os.path.exists(attack_loop_path): attack_loop_path = None + if mode == 'quest_follow': + self.config = self.config or {} + self.config.setdefault('bot', {}) + self.config['bot']['quest_follow'] = { + 'follow_key': self.quest_follow_follow_edit.text().strip() or 'f', + 'interact_key': self.quest_follow_interact_edit.text().strip() or '4', + 'interval_sec': float(self.quest_follow_interval_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)) @@ -1099,6 +1161,9 @@ class WoWMultiKeyGUI(QMainWindow): mode, waypoints_path=waypoints_path, vendor_path=vendor_path, attack_loop_path=attack_loop_path, skinning_wait_sec=skinning_wait_sec, + 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(), ) self.game_worker.state_signal.connect(self.state_label.setText) self.game_worker.log_signal.connect(self.log) @@ -1106,7 +1171,12 @@ class WoWMultiKeyGUI(QMainWindow): self.game_worker.start() self.game_start_btn.setEnabled(False) self.game_stop_btn.setEnabled(True) - mode_names = {'monitor': '状态监控', 'patrol': '巡逻打怪', 'combat': '自动打怪'} + mode_names = { + 'monitor': '状态监控', + 'patrol': '巡逻打怪', + 'combat': '自动打怪', + 'quest_follow': '任务跟随', + } self.status_bar.showMessage(f"🟢 {mode_names[mode]} 运行中") self.log(f"🎮 {mode_names[mode]} 已启动")