From 9b4a9d5ab8993a5cabf41f1c0eaf4368f7d30ae1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E9=B9=8F?= Date: Mon, 23 Mar 2026 16:05:27 +0800 Subject: [PATCH] =?UTF-8?q?add=20=E9=A3=9E=E8=A1=8C=E6=A8=A1=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- __pycache__/coordinate_patrol.cpython-311.pyc | Bin 17598 -> 23147 bytes __pycache__/flight_mode.cpython-311.pyc | Bin 0 -> 5180 bytes __pycache__/game_state.cpython-311.pyc | Bin 11617 -> 11617 bytes __pycache__/stuck_handler.cpython-311.pyc | Bin 3491 -> 3482 bytes __pycache__/wow_multikey_gui.cpython-311.pyc | Bin 0 -> 102328 bytes build_wow_multikey.spec | 1 + coordinate_patrol.py | 130 ++++++++++++++++++ docs/history.md | 14 ++ flight_mode.py | 113 +++++++++++++++ screenshot/game_state.png | Bin 667 -> 532 bytes screenshot/game_state_combat.png | Bin 91 -> 74 bytes screenshot/game_state_flight_block.png | Bin 87 -> 87 bytes screenshot/game_state_follow.png | Bin 84 -> 84 bytes screenshot/game_state_hp.png | Bin 90 -> 86 bytes screenshot/game_state_logistics_death.png | Bin 83 -> 83 bytes screenshot/game_state_mp.png | Bin 91 -> 74 bytes screenshot/game_state_target.png | Bin 88 -> 86 bytes screenshot/game_state_x.png | Bin 84 -> 84 bytes screenshot/game_state_y.png | Bin 83 -> 83 bytes wow_multikey_gui.py | 127 ++++++++++++++++- wow_multikey_qt.json | 11 ++ 21 files changed, 393 insertions(+), 3 deletions(-) create mode 100644 __pycache__/flight_mode.cpython-311.pyc create mode 100644 __pycache__/wow_multikey_gui.cpython-311.pyc create mode 100644 flight_mode.py create mode 100644 wow_multikey_qt.json diff --git a/__pycache__/coordinate_patrol.cpython-311.pyc b/__pycache__/coordinate_patrol.cpython-311.pyc index 0264042b8b29a3ad2bcc04a74b4eddd3624abec9..6a4e09b1c8a1be13518722067ab1214935be8f67 100644 GIT binary patch delta 6282 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 delta 1610 zcmaKs%WoT16vpR{W4lRgCr)gKnEH{5Jf=<4CPC>dO_36niXuyK9G4iQ;&>*uablZ0 zwk)VbQc;OVg#=tALoxY(Ys~Yh+ufni({<@a;nvJm&oRbw;t3Y+aKcN^-3`In91DmqSY)72 z@43KI-TFg%Fa7uGz6(mKNB0wZb?<_r^*6lg`8MWk2uFk^5tzFGH{b!ffgtcOu$y4E z6QWzsp9kq}_Go(1G`N}m`Hgp*n~n6GVHT_9O1fUIryEV@k+wh|cKZlcTcNa4z8_K? zb`1?6re@pFii2 zX?Kb9e&4=M8G~m^IfGBZ?&OAPRL`2#TDB;U=fmXIx_C(3@V~;sjZ=Zkwy|;Yaa7gJ zGnvZ-_BwSV#|^`@?3rq&G@;pLihM?RgL^2CM}t==j~|2kRl0%h&@2myMCe&sxE}g} zl6@Gg63(-GYSWqk*_F9fK<#I8J+U9c4cdvpRJTl zJ}c=zB!@wKcLdK zF1&;v`hf}{HIZ$O7HO6K%>UND(>eTkY>^ts@|W^=qwXlV zV$HwlImz3^fp{Mc!sGEhQ?f&3+1nXe?MA7#W^fv+2a1sQzCls64jP2bc-VtHEk~tX zC{;~9DsIIoH*q&U!BUN(g^L)z&l5L~$O~AdpGw(dG_*V20!_B2*j6-Nuvl?tg2k1^ zz3R(Me80Frq4p=AKC~UmVy29ysjXl#j@r`kZHl=q0FE6LUnj%rMJ8@1r*=nMCmzDW zVM0P zGlfTC%dvI90($7r>=*aW+*XsS=v$gzj&3b!F8&^Dd`Gw(A$cQSyh_P1uN)sZehIh? uybs9eAHs?PIPtGt(`#kDwrb4qO>upxe&U>+4g9A3JAaq{KmKD?2K@(-{#y?K diff --git a/__pycache__/flight_mode.cpython-311.pyc b/__pycache__/flight_mode.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..50759634fd56a3448f9373416968858d709987f5 GIT binary patch literal 5180 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 P;m>`w@4Fu%QB?Ck&T&|B literal 0 HcmV?d00001 diff --git a/__pycache__/game_state.cpython-311.pyc b/__pycache__/game_state.cpython-311.pyc index 40147b922b54985347e1272cf91767e8e0112ae7..4cde15b41dd83f243f82dae2b38ed61eabb7ac04 100644 GIT binary patch delta 19 ZcmaDD^)QNSIWI340}yzf*vJ*G3jjcO1)Tr@ delta 19 ZcmaDD^)QNSIWI340}$}c+Q=2I3jjVH1u*~s diff --git a/__pycache__/stuck_handler.cpython-311.pyc b/__pycache__/stuck_handler.cpython-311.pyc index 8a476f8994601915359935709b30ed8b0d656704..2e3f414d547fd58dd18e09d74e286cafa9c09630 100644 GIT binary patch delta 82 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 literal 0 HcmV?d00001 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 f0d994607b53de0c6aafe5425df99a1cb6a31141..d98fe3539220ee1783fe157219c04c91ac910e2b 100644 GIT binary patch delta 507 zcmVaGV5C!1(-pubfKq%le^jw01iaStKbBUaS z5{Wx-5~3(}{O`=8pmJ#!*oyY+NAt8h+SM-afBwm!$m*_cH`;BO#FvlnaVqlU65yPg z|LBv?srvoy`>7?n4`0t{^!dlh`M#aCP4~YZ{&gZ43awQX2!AvnF^EAzsI%5Q%%buk4q<=t_*?dczWD1l4MqQ)gC2G+>(){sR658a);X{)qqp002ovPDHLkV1m-7`WXNK delta 643 zcmV-}0(||H1e*nrBYy&7Nkl?X@zkRpVTkZ5QSLc9sj zL&y8@8X!7opje69vRQKX@$qGbI%U!%LjL++{$)$H#`x~_i>0$OYiHJZ;RFx?goOa! z|D3{(zSg*F1OT2o*&_wA^IE$n003U~`=maI5@wlJex%vX1R zQ0cN7kll3O?|<)3>LF@+@^CU4pPjeGs3~H8u(EAYQUJ%ZAN3A5TcLM#jLGb z6n=6&G3p1*7lL%?s@ZYzk}6Ox^5Xjq>4JKnIv?+s%jtXyTHP(?b#*o@s}lE`Mza4` zN@fW$7+S6S<;^!jMMMBT`WS!T-T@lh`>WB#gStL+)qm``R3|N^Iti}|wXjyN61PUL zAe24<_;AmY%PVE@@%6()SIv$~ML@&STBOuFT@f;Gf(Jwqwt-{xv(=(5%Caa9T{Sy0 zN*QgmDMS}csCy9CcVkl`>e{He3j!-7yc~@V-84Hc)&)R75UrFlq>oqG^+fslWplEYmI@6 dL0uIB{{X3|GMwJQ_bP0l+XkK D%UBX2 diff --git a/screenshot/game_state_follow.png b/screenshot/game_state_follow.png index 3f5cb771a156ad80d824719e8d3132bbf7fd5db3..ceb5a744705395e3c1496d07265a5146f9b44948 100644 GIT binary patch delta 45 zcmWFunV_SQ@#FveMwbIFtJ(r>7gx14$W<{ioS(=vdxiYZISfGH>FVdQ&MBb@0K=#f A82|tP delta 45 zcmWFunV_SQknrQYgHnQ0=;0Of0&Sfg`1dj}EVp2kS(p4jodF0uUHx3vIVCg!0DJ)t Am;e9( diff --git a/screenshot/game_state_hp.png b/screenshot/game_state_hp.png index 2d0c1fa121e40c160c176286f3835449bb3a95f3..479024f74121e7ee4bfee254bf669d2ce75cab38 100644 GIT binary patch delta 55 zcmazFo1kJO>*?YcQZXm_&v^%(gcidY9oL!|7FW4_?;2b&+~PaHUKz@>>zLgK3Ns`L%77m715yuZOD``_!t QaRwmpboFyt=akR{02sF!IsgCw diff --git a/screenshot/game_state_target.png b/screenshot/game_state_target.png index 704ad582e2760fe8c76502faca39dd31a8c9cd42..871eaa79a1dad969c1cea723102d0e777487d264 100644 GIT binary patch delta 55 zcmazDo1kJO>*?YcQZXkvK|#>vUwVR4C_nSz74rgZo8$QRGBC_K$XF^oSEQ8z2s~Z= KT-G@yGywqoa}mA( delta 57 zcmWHGn4n@J@9E+gQZXm_&v^$agWo3)xGWN5V`~evU0lVsSFVbYp~H&Fr(|pC5e6Xe MboFyt=akR{0A#QfApigX diff --git a/screenshot/game_state_x.png b/screenshot/game_state_x.png index d1b2d169bdb868847bf730ab9ee48ad616bd89e4..8a70a1e71c3a86e6382579add89cbade0d978ce0 100644 GIT binary patch delta 45 zcmWFunV_R_Met3Mf}p{~Rf!?zSIkpf&>qLY@XC+jY5tuk83rKmboFyt=akR{0FwI< AoB#j- delta 45 zcmWFunV_TbgwG&RLC|30s>G1>72Wer2Opqt=V1|aZs^>bP0l+XkKgJuvj delta 44 zcmWFzo}i;{GA&&})L`P8>5CzKx+kt^eW-1|aZs^>bP0l+XkKbPNy+ 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