add 脱战血量低就地吃面包
This commit is contained in:
Binary file not shown.
Binary file not shown.
@@ -14,6 +14,9 @@ from logistics_manager import LogisticsManager
|
|||||||
KEY_TAB = '3'
|
KEY_TAB = '3'
|
||||||
KEY_LOOT = '4' # 假设你在游戏里设置了互动按键为 F
|
KEY_LOOT = '4' # 假设你在游戏里设置了互动按键为 F
|
||||||
KEY_ATTACK = '2' # 假设你的主攻击技能是 1
|
KEY_ATTACK = '2' # 假设你的主攻击技能是 1
|
||||||
|
DEFAULT_FOOD_KEY = 'f1' # 面包快捷键
|
||||||
|
DEFAULT_EAT_HP_THRESHOLD = 30
|
||||||
|
DEFAULT_EAT_MAX_WAIT_SEC = 30.0
|
||||||
|
|
||||||
# 巡逻点配置文件
|
# 巡逻点配置文件
|
||||||
WAYPOINTS_FILE = 'waypoints.json'
|
WAYPOINTS_FILE = 'waypoints.json'
|
||||||
@@ -79,7 +82,18 @@ def load_attack_loop(path):
|
|||||||
|
|
||||||
|
|
||||||
class AutoBotMove:
|
class AutoBotMove:
|
||||||
def __init__(self, waypoints=None, waypoints_path=None, vendor_path=None, attack_loop_path=None, skinning_wait_sec=None):
|
def __init__(
|
||||||
|
self,
|
||||||
|
waypoints=None,
|
||||||
|
waypoints_path=None,
|
||||||
|
vendor_path=None,
|
||||||
|
attack_loop_path=None,
|
||||||
|
skinning_wait_sec=None,
|
||||||
|
food_key=None,
|
||||||
|
eat_hp_threshold=None,
|
||||||
|
eat_max_wait_sec=None,
|
||||||
|
stop_check=None,
|
||||||
|
):
|
||||||
self.last_tab_time = 0
|
self.last_tab_time = 0
|
||||||
self.is_running = True
|
self.is_running = True
|
||||||
self.is_moving = False
|
self.is_moving = False
|
||||||
@@ -87,7 +101,14 @@ class AutoBotMove:
|
|||||||
# 记录上一帧是否处于战斗/有目标,用于检测“刚刚脱战”的瞬间
|
# 记录上一帧是否处于战斗/有目标,用于检测“刚刚脱战”的瞬间
|
||||||
self._was_in_combat_or_target = False
|
self._was_in_combat_or_target = False
|
||||||
self.skinning_wait_sec = float(skinning_wait_sec) if skinning_wait_sec is not None else 1.5
|
self.skinning_wait_sec = float(skinning_wait_sec) if skinning_wait_sec is not None else 1.5
|
||||||
|
self.food_key = (food_key or DEFAULT_FOOD_KEY).strip().lower() or DEFAULT_FOOD_KEY
|
||||||
|
self.eat_hp_threshold = int(eat_hp_threshold) if eat_hp_threshold is not None else DEFAULT_EAT_HP_THRESHOLD
|
||||||
|
self.eat_max_wait_sec = float(eat_max_wait_sec) if eat_max_wait_sec is not None else DEFAULT_EAT_MAX_WAIT_SEC
|
||||||
self.attack_loop_config = load_attack_loop(attack_loop_path)
|
self.attack_loop_config = load_attack_loop(attack_loop_path)
|
||||||
|
self._prev_death_state = 0
|
||||||
|
self._eating_started_at = None
|
||||||
|
# stop_check: 返回 True 表示需要立即停止(用于中断阻塞中的后勤/路线导航)
|
||||||
|
self._stop_check = stop_check if callable(stop_check) else (lambda: False)
|
||||||
if waypoints is None:
|
if waypoints is None:
|
||||||
path = waypoints_path or get_config_path(WAYPOINTS_FILE)
|
path = waypoints_path or get_config_path(WAYPOINTS_FILE)
|
||||||
waypoints = load_waypoints(path) or DEFAULT_WAYPOINTS
|
waypoints = load_waypoints(path) or DEFAULT_WAYPOINTS
|
||||||
@@ -102,6 +123,12 @@ class AutoBotMove:
|
|||||||
vendor_file = vendor_path or get_config_path('vendor.json')
|
vendor_file = vendor_path or get_config_path('vendor.json')
|
||||||
self.logistics_manager = LogisticsManager(vendor_file)
|
self.logistics_manager = LogisticsManager(vendor_file)
|
||||||
|
|
||||||
|
def _should_stop(self) -> bool:
|
||||||
|
try:
|
||||||
|
return bool(self._stop_check())
|
||||||
|
except Exception:
|
||||||
|
return False
|
||||||
|
|
||||||
def execute_disengage_loot(self):
|
def execute_disengage_loot(self):
|
||||||
"""从有战斗/目标切换到完全脱战的瞬间,执行拾取 + 剥皮。"""
|
"""从有战斗/目标切换到完全脱战的瞬间,执行拾取 + 剥皮。"""
|
||||||
try:
|
try:
|
||||||
@@ -140,8 +167,39 @@ class AutoBotMove:
|
|||||||
if random.random() < 0.3:
|
if random.random() < 0.3:
|
||||||
pydirectinput.press(KEY_ATTACK)
|
pydirectinput.press(KEY_ATTACK)
|
||||||
|
|
||||||
|
def _start_eating(self):
|
||||||
|
"""开始就地吃面包恢复。"""
|
||||||
|
self._eating_started_at = time.time()
|
||||||
|
pydirectinput.press(self.food_key)
|
||||||
|
|
||||||
|
def _should_keep_eating(self, state) -> bool:
|
||||||
|
"""
|
||||||
|
返回 True 表示继续等待回血(暂停巡逻与找怪);
|
||||||
|
返回 False 表示结束进食等待。
|
||||||
|
"""
|
||||||
|
if self._eating_started_at is None:
|
||||||
|
return False
|
||||||
|
hp = state.get('hp')
|
||||||
|
if hp is not None and hp >= 100:
|
||||||
|
self._eating_started_at = None
|
||||||
|
return False
|
||||||
|
if (time.time() - self._eating_started_at) >= self.eat_max_wait_sec:
|
||||||
|
self._eating_started_at = None
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
def execute_logic(self, state):
|
def execute_logic(self, state):
|
||||||
|
if self._should_stop():
|
||||||
|
# 在停止按钮点击后,确保马上松开移动键,并避免继续执行后勤交互。
|
||||||
|
self.patrol_controller.stop_all()
|
||||||
|
self.logistics_manager.is_returning = False
|
||||||
|
self.is_moving = False
|
||||||
|
return
|
||||||
|
|
||||||
death = state.get('death_state', 0)
|
death = state.get('death_state', 0)
|
||||||
|
if self._prev_death_state in (1, 2) and death == 0:
|
||||||
|
self.death_manager.reset_when_alive()
|
||||||
|
self._prev_death_state = death
|
||||||
# 1. 死亡状态:尸体(1) 记录坐标并释放灵魂;灵魂(2) 跑尸
|
# 1. 死亡状态:尸体(1) 记录坐标并释放灵魂;灵魂(2) 跑尸
|
||||||
if death == 1:
|
if death == 1:
|
||||||
self.patrol_controller.stop_all()
|
self.patrol_controller.stop_all()
|
||||||
@@ -160,12 +218,16 @@ class AutoBotMove:
|
|||||||
self.patrol_controller.stop_all()
|
self.patrol_controller.stop_all()
|
||||||
self.is_moving = False
|
self.is_moving = False
|
||||||
self.patrol_controller.reset_stuck()
|
self.patrol_controller.reset_stuck()
|
||||||
self.logistics_manager.run_route1_round(parse_game_state, self.patrol_controller)
|
# 中断策略:一旦 GUI 停止,后续 get_state 返回 None,使 navigate_path 立即退出。
|
||||||
|
get_state = (lambda: None if self._should_stop() else parse_game_state())
|
||||||
|
self.logistics_manager.run_route1_round(get_state, self.patrol_controller)
|
||||||
return
|
return
|
||||||
|
|
||||||
# 3. 战斗/有目标:停止移动,执行攻击逻辑;仅在「跑向怪」短窗口内做卡死检测
|
# 3. 战斗/有目标:停止移动,执行攻击逻辑;仅在「跑向怪」短窗口内做卡死检测
|
||||||
in_combat_or_target = bool(state['combat'] or state['target'])
|
in_combat_or_target = bool(state['combat'] or state['target'])
|
||||||
if in_combat_or_target:
|
if in_combat_or_target:
|
||||||
|
# 被动进战时立即打断进食等待,转入正常战斗流程
|
||||||
|
self._eating_started_at = None
|
||||||
if self.target_acquired_time is None:
|
if self.target_acquired_time is None:
|
||||||
self.target_acquired_time = time.time()
|
self.target_acquired_time = time.time()
|
||||||
if self.is_moving:
|
if self.is_moving:
|
||||||
@@ -206,6 +268,18 @@ class AutoBotMove:
|
|||||||
pass
|
pass
|
||||||
self.target_acquired_time = None # 脱战/无目标时清零,下次获得目标再计时
|
self.target_acquired_time = None # 脱战/无目标时清零,下次获得目标再计时
|
||||||
self._was_in_combat_or_target = False
|
self._was_in_combat_or_target = False
|
||||||
|
|
||||||
|
# 4. 脱战低血量:就地吃面包(最多等待 30 秒或回满)
|
||||||
|
hp = state.get('hp')
|
||||||
|
if self._eating_started_at is None and hp is not None and hp < self.eat_hp_threshold:
|
||||||
|
if self.is_moving:
|
||||||
|
self.patrol_controller.stop_all()
|
||||||
|
self.is_moving = False
|
||||||
|
self._start_eating()
|
||||||
|
if self._should_keep_eating(state):
|
||||||
|
# 进食期间不巡逻、不主动找目标(不按 Tab)
|
||||||
|
return
|
||||||
|
|
||||||
# 4. 没战斗没目标:巡逻(卡死检测在 patrol_controller.navigate 内)
|
# 4. 没战斗没目标:巡逻(卡死检测在 patrol_controller.navigate 内)
|
||||||
self.is_moving = True
|
self.is_moving = True
|
||||||
self.patrol_controller.navigate(state)
|
self.patrol_controller.navigate(state)
|
||||||
|
|||||||
@@ -56,11 +56,16 @@ class CoordinatePatrol:
|
|||||||
已上马返回 True。
|
已上马返回 True。
|
||||||
未上马则松开移动键、按住上马键 MOUNT_HOLD_SEC,本帧不走路;返回 False。
|
未上马则松开移动键、按住上马键 MOUNT_HOLD_SEC,本帧不走路;返回 False。
|
||||||
state 无 mounted 字段时视为无法判断,不拦巡逻(兼容未开 LogicBeacon)。
|
state 无 mounted 字段时视为无法判断,不拦巡逻(兼容未开 LogicBeacon)。
|
||||||
|
尸体/灵魂不按上马键,但返回 True 以便寻路继续(跑尸时 navigate_to_point 依赖此项)。
|
||||||
"""
|
"""
|
||||||
if "mounted" not in state:
|
if "mounted" not in state:
|
||||||
return True
|
return True
|
||||||
if state.get("mounted"):
|
if state.get("mounted"):
|
||||||
return True
|
return True
|
||||||
|
# game_state.death_state: 0 存活 / 1 尸体 / 2 灵魂 — 幽灵无法/不需上马,不得拦走路
|
||||||
|
ds = state.get("death_state")
|
||||||
|
if ds in (1, 2):
|
||||||
|
return True
|
||||||
self.stop_all()
|
self.stop_all()
|
||||||
now = time.time()
|
now = time.time()
|
||||||
if now < self._next_mount_allowed:
|
if now < self._next_mount_allowed:
|
||||||
|
|||||||
@@ -8,9 +8,19 @@ class DeathManager:
|
|||||||
self.corpse_pos = None
|
self.corpse_pos = None
|
||||||
self.patrol_system = patrol_system
|
self.patrol_system = patrol_system
|
||||||
self.is_running_to_corpse = False
|
self.is_running_to_corpse = False
|
||||||
|
self._spirit_release_sent = False
|
||||||
|
|
||||||
|
def reset_when_alive(self):
|
||||||
|
"""存活时清标志,避免下次死亡无法再次释放灵魂/记录尸体坐标。"""
|
||||||
|
self._spirit_release_sent = False
|
||||||
|
self.corpse_pos = None
|
||||||
|
self.is_running_to_corpse = False
|
||||||
|
|
||||||
def on_death(self, state):
|
def on_death(self, state):
|
||||||
"""1. 死亡瞬间调用:从 player_position 获取坐标并记录"""
|
"""1. 死亡瞬间调用:从 player_position 获取坐标并记录"""
|
||||||
|
if self._spirit_release_sent:
|
||||||
|
return
|
||||||
|
self._spirit_release_sent = True
|
||||||
self.corpse_pos = (state['x'], state['y'])
|
self.corpse_pos = (state['x'], state['y'])
|
||||||
self.is_running_to_corpse = True
|
self.is_running_to_corpse = True
|
||||||
print(f">>> [系统] 记录死亡坐标: {self.corpse_pos},准备释放灵魂...")
|
print(f">>> [系统] 记录死亡坐标: {self.corpse_pos},准备释放灵魂...")
|
||||||
|
|||||||
@@ -31,3 +31,22 @@
|
|||||||
- **GUI 更新**:在 `wow_multikey_gui.py` 的「飞行模式配置」页,「降落按键」默认值与占位提示从 `x` 改为 `p`。
|
- **GUI 更新**:在 `wow_multikey_gui.py` 的「飞行模式配置」页,「降落按键」默认值与占位提示从 `x` 改为 `p`。
|
||||||
- **行为更新**:`flight_mode.py` 中 `FlightModeBot` 的默认 `land_key` 从 `x` 改为 `p`,保证未手动填写/未在配置中存在时仍使用 `p`。
|
- **行为更新**:`flight_mode.py` 中 `FlightModeBot` 的默认 `land_key` 从 `x` 改为 `p`,保证未手动填写/未在配置中存在时仍使用 `p`。
|
||||||
|
|
||||||
|
## 2026-03-25
|
||||||
|
|
||||||
|
### 脱战低血就地吃面包
|
||||||
|
|
||||||
|
- **新增逻辑**:`auto_bot_move.py` 在脱战状态下,当 `hp < 30%` 时按一次面包键开始恢复。
|
||||||
|
- **等待规则**:进入恢复后,最多等待 30 秒;若血量先到 `100%` 则提前结束。
|
||||||
|
- **行为约束**:恢复期间不巡逻、不主动按 `Tab` 寻找目标。
|
||||||
|
- **战斗中断**:若恢复过程中被动进入战斗(`combat/target` 变为真),立即中断恢复并进入正常战斗逻辑。
|
||||||
|
|
||||||
|
### 吃面包参数接入 GUI 配置
|
||||||
|
|
||||||
|
- **参数配置页新增**:`wow_multikey_gui.py` 的「参数配置」页新增:
|
||||||
|
- `吃面包按键`(`food_key`,默认 `f1`)
|
||||||
|
- `吃面包血量阈值`(`eat_hp_threshold`,默认 `30`)
|
||||||
|
- `吃面包最长等待`(`eat_max_wait_sec`,默认 `30.0` 秒)
|
||||||
|
- **配置持久化**:上述参数保存到 `wow_multikey_qt.json` 的 `bot` 节点。
|
||||||
|
- **运行时透传**:`start_game_loop` → `GameLoopWorker` → `AutoBotMove` 全链路透传并生效。
|
||||||
|
- **兼容性**:`AutoBotMove` 保留默认值,旧配置文件可直接运行。
|
||||||
|
|
||||||
|
|||||||
@@ -305,6 +305,9 @@ 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,
|
||||||
|
food_key=None,
|
||||||
|
eat_hp_threshold=None,
|
||||||
|
eat_max_wait_sec=None,
|
||||||
quest_follow_follow_key=None,
|
quest_follow_follow_key=None,
|
||||||
quest_follow_interact_key=None,
|
quest_follow_interact_key=None,
|
||||||
quest_follow_interval_sec=None,
|
quest_follow_interval_sec=None,
|
||||||
@@ -328,6 +331,15 @@ 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.food_key = (food_key or "f1").strip().lower() or "f1"
|
||||||
|
try:
|
||||||
|
self.eat_hp_threshold = int(eat_hp_threshold)
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
self.eat_hp_threshold = 30
|
||||||
|
try:
|
||||||
|
self.eat_max_wait_sec = float(eat_max_wait_sec)
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
self.eat_max_wait_sec = 30.0
|
||||||
self.quest_follow_follow_key = (quest_follow_follow_key or "f").strip().lower() or "f"
|
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"
|
self.quest_follow_interact_key = (quest_follow_interact_key or "4").strip().lower() or "4"
|
||||||
try:
|
try:
|
||||||
@@ -355,6 +367,10 @@ class GameLoopWorker(QThread):
|
|||||||
vendor_path=self.vendor_path,
|
vendor_path=self.vendor_path,
|
||||||
attack_loop_path=self.attack_loop_path,
|
attack_loop_path=self.attack_loop_path,
|
||||||
skinning_wait_sec=self.skinning_wait_sec,
|
skinning_wait_sec=self.skinning_wait_sec,
|
||||||
|
food_key=self.food_key,
|
||||||
|
eat_hp_threshold=self.eat_hp_threshold,
|
||||||
|
eat_max_wait_sec=self.eat_max_wait_sec,
|
||||||
|
stop_check=lambda: not self.running,
|
||||||
)
|
)
|
||||||
except ImportError as e:
|
except ImportError as e:
|
||||||
self.log_signal.emit(f"❌ 巡逻打怪依赖加载失败: {e}")
|
self.log_signal.emit(f"❌ 巡逻打怪依赖加载失败: {e}")
|
||||||
@@ -825,6 +841,23 @@ class WoWMultiKeyGUI(QMainWindow):
|
|||||||
self.skinning_wait_spin.setSuffix(" 秒")
|
self.skinning_wait_spin.setSuffix(" 秒")
|
||||||
params_right.addRow("剥皮等待时间:", self.skinning_wait_spin)
|
params_right.addRow("剥皮等待时间:", self.skinning_wait_spin)
|
||||||
|
|
||||||
|
self.food_key_edit = QLineEdit()
|
||||||
|
self.food_key_edit.setPlaceholderText("如 f1")
|
||||||
|
self.food_key_edit.setMaxLength(16)
|
||||||
|
self.food_key_edit.setText("f1")
|
||||||
|
self.eat_hp_threshold_spin = QSpinBox()
|
||||||
|
self.eat_hp_threshold_spin.setRange(1, 100)
|
||||||
|
self.eat_hp_threshold_spin.setValue(30)
|
||||||
|
self.eat_hp_threshold_spin.setSuffix(" %")
|
||||||
|
self.eat_max_wait_spin = QDoubleSpinBox()
|
||||||
|
self.eat_max_wait_spin.setRange(1.0, 120.0)
|
||||||
|
self.eat_max_wait_spin.setSingleStep(1.0)
|
||||||
|
self.eat_max_wait_spin.setValue(30.0)
|
||||||
|
self.eat_max_wait_spin.setSuffix(" 秒")
|
||||||
|
params_right.addRow("吃面包按键:", self.food_key_edit)
|
||||||
|
params_right.addRow("吃面包血量阈值:", self.eat_hp_threshold_spin)
|
||||||
|
params_right.addRow("吃面包最长等待:", self.eat_max_wait_spin)
|
||||||
|
|
||||||
self.gs_mount_key = QLineEdit()
|
self.gs_mount_key = QLineEdit()
|
||||||
self.gs_mount_key.setPlaceholderText("如 x")
|
self.gs_mount_key.setPlaceholderText("如 x")
|
||||||
self.gs_mount_key.setMaxLength(16)
|
self.gs_mount_key.setMaxLength(16)
|
||||||
@@ -886,8 +919,14 @@ class WoWMultiKeyGUI(QMainWindow):
|
|||||||
bot_cfg = (self.config or {}).get('bot') or {}
|
bot_cfg = (self.config or {}).get('bot') or {}
|
||||||
v = bot_cfg.get('skinning_wait_sec', 1.5)
|
v = bot_cfg.get('skinning_wait_sec', 1.5)
|
||||||
self.skinning_wait_spin.setValue(float(v))
|
self.skinning_wait_spin.setValue(float(v))
|
||||||
|
self.food_key_edit.setText(str(bot_cfg.get('food_key', 'f1')).strip() or 'f1')
|
||||||
|
self.eat_hp_threshold_spin.setValue(int(bot_cfg.get('eat_hp_threshold', 30)))
|
||||||
|
self.eat_max_wait_spin.setValue(float(bot_cfg.get('eat_max_wait_sec', 30.0)))
|
||||||
except Exception:
|
except Exception:
|
||||||
self.skinning_wait_spin.setValue(1.5)
|
self.skinning_wait_spin.setValue(1.5)
|
||||||
|
self.food_key_edit.setText('f1')
|
||||||
|
self.eat_hp_threshold_spin.setValue(30)
|
||||||
|
self.eat_max_wait_spin.setValue(30.0)
|
||||||
|
|
||||||
def _save_params_config(self):
|
def _save_params_config(self):
|
||||||
"""保存「参数配置」界面到 game_state_config.json(多分辨率)并写入 wow_multikey_qt.json(bot 参数)"""
|
"""保存「参数配置」界面到 game_state_config.json(多分辨率)并写入 wow_multikey_qt.json(bot 参数)"""
|
||||||
@@ -908,6 +947,9 @@ class WoWMultiKeyGUI(QMainWindow):
|
|||||||
self.config = self.config or {}
|
self.config = self.config or {}
|
||||||
self.config.setdefault('bot', {})
|
self.config.setdefault('bot', {})
|
||||||
self.config['bot']['skinning_wait_sec'] = float(self.skinning_wait_spin.value())
|
self.config['bot']['skinning_wait_sec'] = float(self.skinning_wait_spin.value())
|
||||||
|
self.config['bot']['food_key'] = self.food_key_edit.text().strip() or 'f1'
|
||||||
|
self.config['bot']['eat_hp_threshold'] = int(self.eat_hp_threshold_spin.value())
|
||||||
|
self.config['bot']['eat_max_wait_sec'] = float(self.eat_max_wait_spin.value())
|
||||||
self._save_main_config()
|
self._save_main_config()
|
||||||
|
|
||||||
self.log(f"✅ 参数配置已保存至 {path},并更新 bot 参数")
|
self.log(f"✅ 参数配置已保存至 {path},并更新 bot 参数")
|
||||||
@@ -1283,11 +1325,26 @@ class WoWMultiKeyGUI(QMainWindow):
|
|||||||
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))
|
||||||
except Exception:
|
except Exception:
|
||||||
skinning_wait_sec = 1.5
|
skinning_wait_sec = 1.5
|
||||||
|
try:
|
||||||
|
food_key = str(((self.config or {}).get('bot') or {}).get('food_key', 'f1')).strip() or 'f1'
|
||||||
|
except Exception:
|
||||||
|
food_key = 'f1'
|
||||||
|
try:
|
||||||
|
eat_hp_threshold = int(((self.config or {}).get('bot') or {}).get('eat_hp_threshold', 30))
|
||||||
|
except Exception:
|
||||||
|
eat_hp_threshold = 30
|
||||||
|
try:
|
||||||
|
eat_max_wait_sec = float(((self.config or {}).get('bot') or {}).get('eat_max_wait_sec', 30.0))
|
||||||
|
except Exception:
|
||||||
|
eat_max_wait_sec = 30.0
|
||||||
|
|
||||||
self.game_worker = GameLoopWorker(
|
self.game_worker = GameLoopWorker(
|
||||||
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,
|
||||||
|
food_key=food_key,
|
||||||
|
eat_hp_threshold=eat_hp_threshold,
|
||||||
|
eat_max_wait_sec=eat_max_wait_sec,
|
||||||
quest_follow_follow_key=self.quest_follow_follow_edit.text(),
|
quest_follow_follow_key=self.quest_follow_follow_edit.text(),
|
||||||
quest_follow_interact_key=self.quest_follow_interact_edit.text(),
|
quest_follow_interact_key=self.quest_follow_interact_edit.text(),
|
||||||
quest_follow_interval_sec=self.quest_follow_interval_spin.value(),
|
quest_follow_interval_sec=self.quest_follow_interval_spin.value(),
|
||||||
@@ -1355,6 +1412,13 @@ class WoWMultiKeyGUI(QMainWindow):
|
|||||||
"""统一停止游戏循环,更新所有相关 UI"""
|
"""统一停止游戏循环,更新所有相关 UI"""
|
||||||
if self.game_worker:
|
if self.game_worker:
|
||||||
self.game_worker.running = False
|
self.game_worker.running = False
|
||||||
|
# 额外释放一次移动按键,避免刚好卡在阻塞逻辑里导致按键滞留。
|
||||||
|
try:
|
||||||
|
if getattr(self.game_worker, "bot_move", None):
|
||||||
|
self.game_worker.bot_move.patrol_controller.stop_all()
|
||||||
|
self.game_worker.bot_move.logistics_manager.is_returning = False
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
# 不在这里 wait(),避免阻塞 GUI;线程会在下一轮循环自然退出
|
# 不在这里 wait(),避免阻塞 GUI;线程会在下一轮循环自然退出
|
||||||
self.game_start_btn.setEnabled(True)
|
self.game_start_btn.setEnabled(True)
|
||||||
self.game_stop_btn.setEnabled(False)
|
self.game_stop_btn.setEnabled(False)
|
||||||
|
|||||||
Reference in New Issue
Block a user