add 任务跟随模式
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,6 +1,7 @@
|
|||||||
# 忽略打包后的静态资源(你的核心需求)
|
# 忽略打包后的静态资源(你的核心需求)
|
||||||
dist/
|
dist/
|
||||||
build/
|
build/
|
||||||
|
dist.zip
|
||||||
|
|
||||||
# 忽略依赖库
|
# 忽略依赖库
|
||||||
node_modules/
|
node_modules/
|
||||||
|
|||||||
38
quest_follow.py
Normal file
38
quest_follow.py
Normal file
@@ -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}")
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""
|
"""
|
||||||
WoW 多功能控制器 GUI
|
WoW 多功能控制器 GUI
|
||||||
集成:状态监控、巡逻打怪、自动打怪、巡逻点录制、多键控制
|
集成:状态监控、巡逻打怪、自动打怪、任务跟随、巡逻点录制、多键控制
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import json
|
import json
|
||||||
@@ -289,10 +289,10 @@ class KeyWorker(QThread):
|
|||||||
self.log_signal.emit(f"❌ 按键 {self.key_name} 失败: {e}")
|
self.log_signal.emit(f"❌ 按键 {self.key_name} 失败: {e}")
|
||||||
|
|
||||||
|
|
||||||
# ============ 游戏主循环(状态监控 / 巡逻打怪 / 自动打怪 / 录制) ============
|
# ============ 游戏主循环(状态监控 / 巡逻打怪 / 自动打怪 / 任务跟随 / 录制) ============
|
||||||
|
|
||||||
class GameLoopWorker(QThread):
|
class GameLoopWorker(QThread):
|
||||||
"""游戏状态主循环:支持状态监控、巡逻打怪、自动打怪、巡逻点录制"""
|
"""游戏状态主循环:支持状态监控、巡逻打怪、自动打怪、任务跟随、巡逻点录制"""
|
||||||
state_signal = pyqtSignal(str)
|
state_signal = pyqtSignal(str)
|
||||||
log_signal = pyqtSignal(str)
|
log_signal = pyqtSignal(str)
|
||||||
|
|
||||||
@@ -305,12 +305,16 @@ class GameLoopWorker(QThread):
|
|||||||
record_min_distance=None,
|
record_min_distance=None,
|
||||||
attack_loop_path=None,
|
attack_loop_path=None,
|
||||||
skinning_wait_sec=None,
|
skinning_wait_sec=None,
|
||||||
|
quest_follow_follow_key=None,
|
||||||
|
quest_follow_interact_key=None,
|
||||||
|
quest_follow_interval_sec=None,
|
||||||
):
|
):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.mode = mode # 'monitor' | 'patrol' | 'combat' | 'record'
|
self.mode = mode # 'monitor' | 'patrol' | 'combat' | 'quest_follow' | 'record'
|
||||||
self.running = True
|
self.running = True
|
||||||
self.bot_move = None
|
self.bot_move = None
|
||||||
self.bot_combat = None
|
self.bot_combat = None
|
||||||
|
self.quest_follow_bot = None
|
||||||
self.recorder = None
|
self.recorder = None
|
||||||
self.waypoints_path = waypoints_path
|
self.waypoints_path = waypoints_path
|
||||||
self.vendor_path = vendor_path
|
self.vendor_path = vendor_path
|
||||||
@@ -318,6 +322,12 @@ class GameLoopWorker(QThread):
|
|||||||
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
|
||||||
self.skinning_wait_sec = skinning_wait_sec
|
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):
|
def run(self):
|
||||||
try:
|
try:
|
||||||
@@ -350,6 +360,19 @@ class GameLoopWorker(QThread):
|
|||||||
self.log_signal.emit(f"❌ 自动打怪依赖加载失败: {e}")
|
self.log_signal.emit(f"❌ 自动打怪依赖加载失败: {e}")
|
||||||
return
|
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':
|
if self.mode == 'record':
|
||||||
try:
|
try:
|
||||||
from recorder import WaypointRecorder
|
from recorder import WaypointRecorder
|
||||||
@@ -392,6 +415,8 @@ class GameLoopWorker(QThread):
|
|||||||
self.bot_combat.execute_logic(state)
|
self.bot_combat.execute_logic(state)
|
||||||
elif self.mode == 'record' and self.recorder:
|
elif self.mode == 'record' and self.recorder:
|
||||||
self.recorder.record(state)
|
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)
|
time.sleep(0.1)
|
||||||
|
|
||||||
def save_recorder(self):
|
def save_recorder(self):
|
||||||
@@ -477,6 +502,7 @@ class WoWMultiKeyGUI(QMainWindow):
|
|||||||
('monitor', '状态监控'),
|
('monitor', '状态监控'),
|
||||||
('patrol', '巡逻打怪'),
|
('patrol', '巡逻打怪'),
|
||||||
('combat', '自动打怪'),
|
('combat', '自动打怪'),
|
||||||
|
('quest_follow', '任务跟随'),
|
||||||
]:
|
]:
|
||||||
btn = QPushButton(name)
|
btn = QPushButton(name)
|
||||||
btn.setCheckable(True)
|
btn.setCheckable(True)
|
||||||
@@ -514,6 +540,31 @@ class WoWMultiKeyGUI(QMainWindow):
|
|||||||
self.combat_group.setVisible(False)
|
self.combat_group.setVisible(False)
|
||||||
game_layout.addWidget(self.combat_group)
|
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 = QLabel("状态: ---")
|
||||||
self.state_label.setStyleSheet("font-family: monospace; padding: 6px; background: #1e1e1e; color: #d4d4d4;")
|
self.state_label.setStyleSheet("font-family: monospace; padding: 6px; background: #1e1e1e; color: #d4d4d4;")
|
||||||
self.state_label.setMinimumHeight(36)
|
self.state_label.setMinimumHeight(36)
|
||||||
@@ -981,6 +1032,7 @@ class WoWMultiKeyGUI(QMainWindow):
|
|||||||
self._refresh_attack_loop_combo()
|
self._refresh_attack_loop_combo()
|
||||||
self.patrol_group.setVisible(mode == 'patrol')
|
self.patrol_group.setVisible(mode == 'patrol')
|
||||||
self.combat_group.setVisible(mode == 'combat')
|
self.combat_group.setVisible(mode == 'combat')
|
||||||
|
self.quest_follow_group.setVisible(mode == 'quest_follow')
|
||||||
|
|
||||||
def load_config(self):
|
def load_config(self):
|
||||||
if os.path.exists(self.config_path):
|
if os.path.exists(self.config_path):
|
||||||
@@ -1060,7 +1112,7 @@ class WoWMultiKeyGUI(QMainWindow):
|
|||||||
def start_game_loop(self):
|
def start_game_loop(self):
|
||||||
mode = self._current_game_mode
|
mode = self._current_game_mode
|
||||||
if mode is None:
|
if mode is None:
|
||||||
QMessageBox.warning(self, "提示", "请先选择模式(状态监控 / 巡逻打怪 / 自动打怪)")
|
QMessageBox.warning(self, "提示", "请先选择模式(状态监控 / 巡逻打怪 / 自动打怪 / 任务跟随)")
|
||||||
return
|
return
|
||||||
waypoints_path = None
|
waypoints_path = None
|
||||||
vendor_path = None
|
vendor_path = None
|
||||||
@@ -1089,6 +1141,16 @@ class WoWMultiKeyGUI(QMainWindow):
|
|||||||
if attack_loop_path and not os.path.exists(attack_loop_path):
|
if attack_loop_path and not os.path.exists(attack_loop_path):
|
||||||
attack_loop_path = None
|
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
|
skinning_wait_sec = None
|
||||||
try:
|
try:
|
||||||
skinning_wait_sec = float(((self.config or {}).get('bot') or {}).get('skinning_wait_sec', 1.5))
|
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,
|
mode, waypoints_path=waypoints_path, vendor_path=vendor_path,
|
||||||
attack_loop_path=attack_loop_path,
|
attack_loop_path=attack_loop_path,
|
||||||
skinning_wait_sec=skinning_wait_sec,
|
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.state_signal.connect(self.state_label.setText)
|
||||||
self.game_worker.log_signal.connect(self.log)
|
self.game_worker.log_signal.connect(self.log)
|
||||||
@@ -1106,7 +1171,12 @@ class WoWMultiKeyGUI(QMainWindow):
|
|||||||
self.game_worker.start()
|
self.game_worker.start()
|
||||||
self.game_start_btn.setEnabled(False)
|
self.game_start_btn.setEnabled(False)
|
||||||
self.game_stop_btn.setEnabled(True)
|
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.status_bar.showMessage(f"🟢 {mode_names[mode]} 运行中")
|
||||||
self.log(f"🎮 {mode_names[mode]} 已启动")
|
self.log(f"🎮 {mode_names[mode]} 已启动")
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user