import sys import os import threading import time import ctypes import re from pynput import keyboard as kb controller = kb.Controller() pressed_keys = set() active_key = None combo_lock = threading.Lock() is_game_active = False combo_enabled = True DIGIT_VK_MAP = { 48: '0', 49: '1', 50: '2', 51: '3', 52: '4', 53: '5', } GAME_WINDOW_TITLE = "西游释厄传SUPER" # 修改成你游戏窗口名字的一部分 # -------------------------- # 窗口前置变化监控(不使用sleep) def win_event_proc(hWinEventHook, event, hwnd, idObject, idChild, dwEventThread, dwmsEventTime): global is_game_active title = ctypes.create_unicode_buffer(512) ctypes.windll.user32.GetWindowTextW(hwnd, title, 512) is_game_active = (GAME_WINDOW_TITLE in title.value) def start_window_watcher(): WINEVENT_OUTOFCONTEXT = 0x0000 EVENT_SYSTEM_FOREGROUND = 0x0003 user32 = ctypes.windll.user32 ole32 = ctypes.windll.ole32 ole32.CoInitialize(0) WinEventProcType = ctypes.WINFUNCTYPE( None, ctypes.wintypes.HANDLE, ctypes.wintypes.DWORD, ctypes.wintypes.HWND, ctypes.wintypes.LONG, ctypes.wintypes.LONG, ctypes.wintypes.DWORD, ctypes.wintypes.DWORD) event_proc = WinEventProcType(win_event_proc) hook = user32.SetWinEventHook( EVENT_SYSTEM_FOREGROUND, EVENT_SYSTEM_FOREGROUND, 0, event_proc, 0, 0, WINEVENT_OUTOFCONTEXT) if hook == 0: print("SetWinEventHook failed") return import pythoncom pythoncom.PumpMessages() threading.Thread(target=start_window_watcher, daemon=True).start() # -------------------------- def find_super_files(): return [f for f in os.listdir('.') if f.lower().startswith('super')] def load_combos_from_file(filepath): try: with open(filepath, 'r', encoding='mbcs', errors='ignore') as f: content = f.read() except: print(f"无法读取文件: {filepath}") return {} combos = {} blocks = re.findall(r"\[(.*?)\]", content, re.DOTALL) for block in blocks: lines = block.strip().splitlines() if '||||' in lines: try: key_line_index = lines.index('||||') + 1 key_line = lines[key_line_index] key_match = re.search(r"\((\d+)\)", key_line) if not key_match: continue key_char = chr(int(key_match.group(1))).lower() second_pipe_index = lines.index('||||', key_line_index) action_lines = lines[second_pipe_index + 1:] sequence = [] for line in action_lines: match = re.match(r"([\d.]+)\|([v\^])\|([A-Z])", line.strip()) if match: delay = float(match.group(1)) action = match.group(2) key = match.group(3).lower() sequence.append((delay, action, key)) combos[key_char] = sequence except Exception as e: print(f"解析失败: {e}") return combos def perform_combo(sequence, key): global active_key with combo_lock: if active_key and active_key != key: return active_key = key for delay, action, k in sequence: time.sleep(delay) if action == 'v': controller.press(k) elif action == '^': controller.release(k) active_key = None def start_listener(initial_combos, script_files): current_combos = initial_combos combo_enabled = True def on_press(key): nonlocal current_combos, combo_enabled if key == kb.Key.f12: msg="按下 F12,程序退出" print(msg) time.sleep(3.5) return False if key in (kb.Key.ctrl_l, kb.Key.ctrl_r): pressed_keys.add('ctrl') elif isinstance(key, kb.KeyCode) and key.vk in DIGIT_VK_MAP: digit = DIGIT_VK_MAP[key.vk] pressed_keys.add(digit) if 'ctrl' in pressed_keys: if digit == '0': combo_enabled = not combo_enabled state = "启用" if combo_enabled else "停用" msg=f"\n>>> 连招功能已{state} <<<" print(msg) return combo_enabled = True idx = int(digit) - 1 if idx < len(script_files): new_file = script_files[idx] print(f"\n切换脚本: {new_file}") current_combos = load_combos_from_file(new_file) msg=f"热键重新加载完毕(来自 {new_file})" print(msg) for k in current_combos: print(f"[{k.upper()}] 就绪") return elif isinstance(key, kb.KeyCode) and key.char: if not is_game_active or not combo_enabled: return k = key.char.lower() if k in current_combos and active_key is None: threading.Thread(target=perform_combo, args=(current_combos[k], k), daemon=True).start() def on_release(key): if key in (kb.Key.ctrl_l, kb.Key.ctrl_r): pressed_keys.discard('ctrl') elif isinstance(key, kb.KeyCode) and key.vk in DIGIT_VK_MAP: pressed_keys.discard(DIGIT_VK_MAP[key.vk]) with kb.Listener(on_press=on_press, on_release=on_release) as listener: # notification.notify( message="连招系统已启动,按 F12 退出,Ctrl+数字键(1~5)切换脚本",timeout=1 ) print("连招系统已启动,按 F12 退出,Ctrl+数字键(1~5)切换脚本") listener.join() def main(): script_files = find_super_files() if not script_files: print("未找到任何 super 开头的脚本文件") sys.exit(1) print("可用脚本列表:") for idx, name in enumerate(script_files): print(f"{idx+1}. {name}") msg=f"\n默认加载第一个脚本: {script_files[0]}" print(msg) combos = load_combos_from_file(script_files[0]) if not combos: print("没有加载任何连招配置") else: for k in combos: print(f"热键 [{k.upper()}] 已就绪") start_listener(combos, script_files) if __name__ == "__main__": main()
配置文件的内容示例:
[ Q左上跑 |||| Q(81) |||| 0.000|v|W|87|17|0-> 0.001|v|A|65|30|0-> 0.020|^|W|87|17|0-> 0.000|^|A|65|30|0-> 0.051|v|W|87|17|0-> 0.000|v|A|65|30|0-> 0.020|^|W|87|17|0-> 0.000|^|A|65|30|0-> 0.051|v|W|87|17|0-> 0.000|v|A|65|30|0-> 0.020|^|W|87|17|0-> 0.000|^|A|65|30|0-> 0.051|v|W|87|17|0-> 0.000|v|A|65|30|0-> 0.020|^|W|87|17|0-> 0.000|^|A|65|30|0-> ]
浙公网安备 33010602011771号