From d0df69184d09170fc3a1d921968935cbe829b68f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E9=B9=8F?= Date: Sat, 11 Apr 2026 22:58:44 +0800 Subject: [PATCH] =?UTF-8?q?add=20=E5=A4=8D=E6=B4=BB=E7=82=B9=E8=B7=AF?= =?UTF-8?q?=E7=BA=BF=E4=B8=8E=E6=AD=BB=E4=BA=A1=E6=8C=89=E9=94=AE=E5=8F=AF?= =?UTF-8?q?=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 死亡管理新增复活点路线JSON两阶段跑尸逻辑 - GUI参数配置支持释放灵魂/复活按键自定义 - 复活点路线JSON选择控件移到攻击循环前面 Co-Authored-By: Claude Opus 4.6 --- auto_bot_move.py | 12 ++++++-- combat_loops/DK.json | 4 +++ death_manager.py | 56 +++++++++++++++++++++++++++++------- game_state_config.json | 9 ++++-- wow_multikey_gui.py | 65 ++++++++++++++++++++++++++++++++++++++---- wow_multikey_qt.json | 6 +++- 6 files changed, 130 insertions(+), 22 deletions(-) diff --git a/auto_bot_move.py b/auto_bot_move.py index e61904b..7461949 100644 --- a/auto_bot_move.py +++ b/auto_bot_move.py @@ -93,6 +93,9 @@ class AutoBotMove: eat_hp_threshold=None, eat_max_wait_sec=None, stop_check=None, + resurrection_waypoints_path=None, + release_spirit_key=None, + resurrect_key=None, ): self.last_tab_time = 0 self.is_running = True @@ -120,7 +123,11 @@ class AutoBotMove: mount_retry_after_sec=float(layout.get("mount_retry_after_sec", 2.0)), enable_mount=bool(layout.get("enable_mount", True)), ) - self.death_manager = DeathManager(self.patrol_controller) + self.death_manager = DeathManager( + self.patrol_controller, resurrection_waypoints_path, + release_spirit_key=layout.get('release_spirit_key', '9') if release_spirit_key is None else release_spirit_key, + resurrect_key=layout.get('resurrect_key', '0') if resurrect_key is None else resurrect_key, + ) vendor_file = vendor_path or get_config_path('vendor.json') self.logistics_manager = LogisticsManager(vendor_file) self.logistics_manager.bag_full_hearthstone = bool(layout.get("bag_full_hearthstone", False)) @@ -230,7 +237,8 @@ class AutoBotMove: self.death_manager.on_death(state) return if death == 2: - self.death_manager.run_to_corpse(state) + get_state = (lambda: None if self._should_stop() else parse_game_state()) + self.death_manager.run_to_corpse(state, get_state) return # 2. 后勤检查(脱战时):空格或耐久不足则回城 diff --git a/combat_loops/DK.json b/combat_loops/DK.json index 980124c..901db7f 100644 --- a/combat_loops/DK.json +++ b/combat_loops/DK.json @@ -2,6 +2,10 @@ "name": "DK", "trigger_chance": 0.5, "steps": [ + { + "key": "4", + "delay": 0.5 + }, { "key": "2", "delay": 0.5 diff --git a/death_manager.py b/death_manager.py index cff5dac..0b45132 100644 --- a/death_manager.py +++ b/death_manager.py @@ -4,17 +4,39 @@ import pydirectinput class DeathManager: - def __init__(self, patrol_system): + def __init__(self, patrol_system, resurrection_waypoints_path=None, + release_spirit_key='9', resurrect_key='0'): self.corpse_pos = None self.patrol_system = patrol_system self.is_running_to_corpse = False self._spirit_release_sent = False + self.resurrection_waypoints = self._load_waypoints(resurrection_waypoints_path) + self._resurrection_completed = False + self.release_spirit_key = str(release_spirit_key).strip() or '9' + self.resurrect_key = str(resurrect_key).strip() or '0' + + def _load_waypoints(self, path): + """加载复活点路线 JSON,可置空。""" + if not path: + return None + import json, os + if not os.path.exists(path): + return None + try: + with open(path, 'r', encoding='utf-8') as f: + data = json.load(f) + if isinstance(data, list) and len(data) > 0: + return [(float(p[0]), float(p[1])) for p in data] + except Exception: + pass + return None def reset_when_alive(self): """存活时清标志,避免下次死亡无法再次释放灵魂/记录尸体坐标。""" self._spirit_release_sent = False self.corpse_pos = None self.is_running_to_corpse = False + self._resurrection_completed = False def on_death(self, state): """1. 死亡瞬间调用:从 player_position 获取坐标并记录""" @@ -24,25 +46,37 @@ class DeathManager: self.corpse_pos = (state['x'], state['y']) self.is_running_to_corpse = True print(f">>> [系统] 记录死亡坐标: {self.corpse_pos},准备释放灵魂...") - pydirectinput.press('9') # 绑定宏: /run RepopMe() + pydirectinput.press(self.release_spirit_key) time.sleep(5) # 等待加载界面 - def run_to_corpse(self, state): - """2. 跑尸寻路逻辑:坐标与朝向从 player_position 获取""" + def run_to_corpse(self, state, get_state=None): + """ + 2. 跑尸寻路逻辑:坐标与朝向从 player_position 获取。 + + 两阶段逻辑: + - 阶段1:若配置了复活点路线,先跑完该路线 + - 阶段2:路线跑完后(或无路线),跑向尸体 + """ if not self.corpse_pos: - return - + return + + # 阶段1:复活路线未走完 → navigate_path 跑完路线 + if self.resurrection_waypoints and not self._resurrection_completed: + if get_state is None: + return + if self.patrol_system.navigate_path(get_state, self.resurrection_waypoints): + self._resurrection_completed = True + print(">>> 复活路线跑完,开始跑向尸体...") + return + + # 阶段2:复活路线走完(或无) → 直接跑向尸体 is_arrived = self.patrol_system.navigate_to_point(state, self.corpse_pos) # 如果距离尸体很近(0.005 约等于 10-20 码) if is_arrived: print(">>> 已到达尸体附近,尝试复活...") - pydirectinput.press('0') # 绑定宏: /run RetrieveCorpse() + pydirectinput.press(self.resurrect_key) time.sleep(5) self.is_running_to_corpse = False self.corpse_pos = None return - def handle_resurrection_popup(self): - """处理复活确认框""" - # 配合 Lua 插件自动点击,或者 Python 模拟按键 - pydirectinput.press('enter') \ No newline at end of file diff --git a/game_state_config.json b/game_state_config.json index 8da3a81..aa1f31a 100644 --- a/game_state_config.json +++ b/game_state_config.json @@ -5,7 +5,10 @@ "scan_region_height": 15, "offset_left": 20, "offset_top": 45, + "enable_mount": false, "mount_key": "x", - "mount_hold_sec": 1.6, - "mount_retry_after_sec": 2.0 -} + "mount_hold_sec": 2.0, + "mount_retry_after_sec": 2.5, + "hearthstone_key": "6", + "bag_full_hearthstone": true +} \ No newline at end of file diff --git a/wow_multikey_gui.py b/wow_multikey_gui.py index 7443e68..6a679c2 100644 --- a/wow_multikey_gui.py +++ b/wow_multikey_gui.py @@ -317,6 +317,9 @@ class GameLoopWorker(QThread): flight_takeoff_hold_sec=None, flight_land_key=None, flight_land_hold_sec=None, + resurrection_waypoints_path=None, + release_spirit_key=None, + resurrect_key=None, ): super().__init__() self.mode = mode # 'monitor' | 'patrol' | 'combat' | 'quest_follow' | 'flight' | 'record' @@ -352,6 +355,9 @@ class GameLoopWorker(QThread): 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 + self.resurrection_waypoints_path = resurrection_waypoints_path + self.release_spirit_key = release_spirit_key + self.resurrect_key = resurrect_key def run(self): try: @@ -372,6 +378,9 @@ class GameLoopWorker(QThread): eat_hp_threshold=self.eat_hp_threshold, eat_max_wait_sec=self.eat_max_wait_sec, stop_check=lambda: not self.running, + resurrection_waypoints_path=self.resurrection_waypoints_path, + release_spirit_key=self.release_spirit_key, + resurrect_key=self.resurrect_key, ) self.bot_move._on_hearthstone_stop = self.stop_signal.emit except ImportError as e: @@ -606,6 +615,9 @@ class WoWMultiKeyGUI(QMainWindow): refresh_btn.clicked.connect(self._refresh_recorder_combos) patrol_layout.addRow("巡逻点 JSON:", self.waypoints_combo) patrol_layout.addRow("修理商 JSON:", self.vendor_combo) + self.resurrection_waypoints_combo = QComboBox() + self.resurrection_waypoints_combo.setMinimumWidth(200) + patrol_layout.addRow("复活点路线 JSON:", self.resurrection_waypoints_combo) patrol_layout.addRow("攻击循环:", self.patrol_attack_loop_combo) patrol_layout.addRow("", refresh_btn) self.patrol_group.setVisible(False) @@ -945,6 +957,17 @@ class WoWMultiKeyGUI(QMainWindow): params_right.addRow("上马按住时长 (mount_hold_sec):", self.gs_mount_hold) params_right.addRow("上马重试间隔 (mount_retry_after_sec):", self.gs_mount_retry) + self.gs_release_spirit_key = QLineEdit() + self.gs_release_spirit_key.setPlaceholderText("如 9") + self.gs_release_spirit_key.setMaxLength(16) + self.gs_release_spirit_key.setText("9") + self.gs_resurrect_key = QLineEdit() + self.gs_resurrect_key.setPlaceholderText("如 0") + self.gs_resurrect_key.setMaxLength(16) + self.gs_resurrect_key.setText("0") + params_right.addRow("释放灵魂按键:", self.gs_release_spirit_key) + params_right.addRow("复活按键:", self.gs_resurrect_key) + params_row.addLayout(params_left) params_row.addLayout(params_right) params_layout.addWidget(params_content) @@ -985,6 +1008,8 @@ class WoWMultiKeyGUI(QMainWindow): self.gs_hearthstone_key.setText(str(cfg.get('hearthstone_key', 'b') or 'b')) self.gs_bag_full_hearthstone.setChecked(bool(cfg.get('bag_full_hearthstone', False))) self.gs_mount_retry.setValue(float(cfg.get('mount_retry_after_sec', 2.0))) + self.gs_release_spirit_key.setText(str(cfg.get('release_spirit_key', '9') or '9')) + self.gs_resurrect_key.setText(str(cfg.get('resurrect_key', '0') or '0')) except Exception as e: self.log(f"加载参数配置失败: {e}") try: @@ -1017,6 +1042,8 @@ class WoWMultiKeyGUI(QMainWindow): cfg['mount_retry_after_sec'] = float(self.gs_mount_retry.value()) cfg['hearthstone_key'] = (self.gs_hearthstone_key.text().strip() or 'b') cfg['bag_full_hearthstone'] = self.gs_bag_full_hearthstone.isChecked() + cfg['release_spirit_key'] = (self.gs_release_spirit_key.text().strip() or '9') + cfg['resurrect_key'] = (self.gs_resurrect_key.text().strip() or '0') path = save_layout_config(cfg) # bot 参数写入主配置文件 self.config = self.config or {} @@ -1212,22 +1239,34 @@ class WoWMultiKeyGUI(QMainWindow): self.preview_canvas.reset_view() def _refresh_recorder_combos(self): - """刷新巡逻点、修理商下拉列表""" + """刷新巡逻点、修理商、复活点路线下拉列表""" items = list_recorder_json() - combos = [(self.waypoints_combo, 'waypoints.json'), (self.vendor_combo, 'vendor.json')] + combos_with_default = [ + (self.waypoints_combo, 'waypoints.json'), + (self.vendor_combo, 'vendor.json'), + ] if hasattr(self, "repair_vendor_combo"): - combos.append((self.repair_vendor_combo, 'vendor.json')) - for combo, default_name in combos: + combos_with_default.append((self.repair_vendor_combo, 'vendor.json')) + for combo, default_name in combos_with_default: + combo.blockSignals(True) combo.clear() combo.addItem("-- 请选择 --", "") for name, path in items: combo.addItem(name, path) - # 尝试选中默认项 idx = combo.findData(os.path.join(get_recorder_dir(), default_name)) if idx >= 0: combo.setCurrentIndex(idx) elif combo.count() > 1: combo.setCurrentIndex(1) + combo.blockSignals(False) + # 复活点路线下拉(无默认值,可置空) + if hasattr(self, "resurrection_waypoints_combo"): + self.resurrection_waypoints_combo.blockSignals(True) + self.resurrection_waypoints_combo.clear() + self.resurrection_waypoints_combo.addItem("-- 置空(直接跑尸体) --", "") + for name, path in items: + self.resurrection_waypoints_combo.addItem(name, path) + self.resurrection_waypoints_combo.blockSignals(False) def _refresh_repair_vendor_json_combo(self): """刷新“回城修理配置”的修理商下拉框。""" @@ -1365,9 +1404,11 @@ class WoWMultiKeyGUI(QMainWindow): waypoints_path = None vendor_path = None flight_json_path = None + resurrection_waypoints_path = None if mode == 'patrol': wp = self.waypoints_combo.currentData() or "" vp = self.vendor_combo.currentData() or "" + rwp = self.resurrection_waypoints_combo.currentData() or "" if not wp: QMessageBox.warning(self, "提示", "巡逻打怪模式需选择巡逻点 JSON 文件") return @@ -1382,6 +1423,7 @@ class WoWMultiKeyGUI(QMainWindow): return waypoints_path = wp vendor_path = vp + resurrection_waypoints_path = rwp if rwp and os.path.exists(rwp) else None attack_loop_path = None if mode == 'patrol': attack_loop_path = self.patrol_attack_loop_combo.currentData() or None @@ -1447,6 +1489,16 @@ class WoWMultiKeyGUI(QMainWindow): except Exception: eat_max_wait_sec = 30.0 + # 从 layout_config 读取死亡/复活按键 + try: + from game_state import load_layout_config + layout_cfg = load_layout_config() + release_spirit_key = str(layout_cfg.get('release_spirit_key', '9') or '9') + resurrect_key = str(layout_cfg.get('resurrect_key', '0') or '0') + except Exception: + release_spirit_key = '9' + resurrect_key = '0' + self.game_worker = GameLoopWorker( mode, waypoints_path=waypoints_path, vendor_path=vendor_path, attack_loop_path=attack_loop_path, @@ -1462,6 +1514,9 @@ class WoWMultiKeyGUI(QMainWindow): 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(), + resurrection_waypoints_path=resurrection_waypoints_path, + release_spirit_key=release_spirit_key, + resurrect_key=resurrect_key, ) self.game_worker.state_signal.connect(self.state_label.setText) self.game_worker.log_signal.connect(self.log) diff --git a/wow_multikey_qt.json b/wow_multikey_qt.json index 712d558..ed20bd3 100644 --- a/wow_multikey_qt.json +++ b/wow_multikey_qt.json @@ -6,6 +6,10 @@ "takeoff_hold_sec": 2.0, "land_key": "p", "land_hold_sec": 2.0 - } + }, + "skinning_wait_sec": 1.5, + "food_key": "f1", + "eat_hp_threshold": 30, + "eat_max_wait_sec": 30.0 } } \ No newline at end of file