diff --git a/README.md b/README.md index 5b7f069..ead92d3 100644 --- a/README.md +++ b/README.md @@ -354,6 +354,9 @@ CLI版本在MDT v0.2.3版本进行拆分,拆分后对CLI版本只做基础可 ## Changelog +*v0.2.14 beta* +* 添加ydk卡组自动导入功能。感谢@chunibyo-wly 的贡献。 + *v0.2.14 beta* * 支持4月新卡图像识别。感谢@wtof1996 的贡献。 diff --git a/mdt.py b/mdt.py index f609ca0..c87e757 100644 --- a/mdt.py +++ b/mdt.py @@ -211,10 +211,12 @@ def config_load(): except Exception: pass + def get_current_cid(): translate() return int(cid_show_gui) + def main(): uac_reload() # 加载游戏 diff --git a/mdt_control.py b/mdt_control.py index 6a60e50..f07cc37 100644 --- a/mdt_control.py +++ b/mdt_control.py @@ -1,9 +1,7 @@ -from glob import glob import time import win32gui import pyautogui import pyperclip -import numpy as np from mdt import get_current_cid import mdt_deck_reader @@ -25,7 +23,7 @@ def _add(a: tuple, b: tuple, scale=1.0): - return a[0]+b[0]*scale, a[1]+b[1]*scale + return a[0] + b[0] * scale, a[1] + b[1] * scale def ydk_converter(ydk_deck: list[tuple], locale: str, window): @@ -53,37 +51,36 @@ def ydk_converter(ydk_deck: list[tuple], locale: str, window): card = _add(search, card_offset, scale) target_card_position = None - pyautogui.click(reset, interval=.5) + pyautogui.click(reset, interval=0.5) for index, tup in enumerate(ydk_deck): element, cid = tup - if index == 0 or element != ydk_deck[index-1][0]: - pyautogui.click(clear, interval=.1) - pyautogui.click(blank, interval=.1) + if index == 0 or element != ydk_deck[index - 1][0]: + pyautogui.click(clear, interval=0.1) + pyautogui.click(blank, interval=0.1) pyautogui.click(search) # 粘贴卡片名 pyperclip.copy(element) pyperclip.paste() - pyautogui.hotkey('ctrl', 'v') - pyautogui.press('enter') + pyautogui.hotkey("ctrl", "v") + pyautogui.press("enter") # 等待搜索完成 time.sleep(1.2) # 处理搜索得到多卡片的情况 - target_card_position = travel_through_deck(card, - card_width_offset*scale, - card_height_offset*scale, - int(cid)) + target_card_position = travel_through_deck( + card, card_width_offset * scale, card_height_offset * scale, int(cid) + ) if target_card_position is None: continue - + print(f"{element}\n") pyautogui.rightClick(target_card_position) - + # 对卡组进行校验 result = mdt_deck_reader.check_deck([int(i[1]) for i in ydk_deck], locale) - if len(result['error1']) != 0 or len(result['error2']) != 0: - window.write_event_value('DECK_CHECK_ERROR', result) + if len(result["error1"]) != 0 or len(result["error2"]) != 0: + window.write_event_value("DECK_CHECK_ERROR", result) else: - window.write_event_value('DECK_CHECK_OK', result) + window.write_event_value("DECK_CHECK_OK", result) def travel_through_deck(start, width_step, height_step, target_cid=-1): @@ -93,7 +90,7 @@ def travel_through_deck(start, width_step, height_step, target_cid=-1): for i in range(5): # 横 for j in range(6): - click_position = start[0]+width_step*j, start[1]+height_step*i + click_position = start[0] + width_step * j, start[1] + height_step * i pyautogui.click(click_position) cid = get_current_cid() if cid == target_cid: diff --git a/mdt_cv.py b/mdt_cv.py index 850abdc..ce0f6d9 100644 --- a/mdt_cv.py +++ b/mdt_cv.py @@ -160,11 +160,14 @@ def get_scan(): return result[1] return 0 + def get_search_button_postion(): - return position_by_template_matching('search') + return position_by_template_matching("search") + def get_reset_button_postion(): - return position_by_template_matching('reset') + return position_by_template_matching("reset") + def get_scale(): # 窗口分辨率可能在程序运行时改变,所以不能是静态变量 @@ -182,12 +185,20 @@ def position_by_template_matching(template_name): imgx, imgy, _ = full_img.shape # 按钮模板 template_image = cv2.imread(f"./data/template/1920x1080{template_name}.png") - template_image = cv2.resize(template_image, (int(get_scale()*template_image.shape[1]), int(get_scale()*template_image.shape[0]))) + template_image = cv2.resize( + template_image, + ( + int(get_scale() * template_image.shape[1]), + int(get_scale() * template_image.shape[0]), + ), + ) # 模板匹配 result = cv2.matchTemplate(full_img, template_image, cv2.TM_CCOEFF_NORMED) # 先横后竖 _, _, _, top_left = cv2.minMaxLoc(result) - return int(top_left[0]+template_image.shape[1]/2), int(top_left[1]+template_image.shape[0]/2) + return int(top_left[0] + template_image.shape[1] / 2), int( + top_left[1] + template_image.shape[0] / 2 + ) def main(): diff --git a/mdt_deck_reader.py b/mdt_deck_reader.py index 95d1080..f5efea8 100644 --- a/mdt_deck_reader.py +++ b/mdt_deck_reader.py @@ -31,7 +31,7 @@ def deck_bytes_to_list(bytes: bytes, count: int): card_list = [] for i in range(count): card_list.append( - int.from_bytes(bytes[i * inc: i * inc + 2], byteorder="little") + int.from_bytes(bytes[i * inc : i * inc + 2], byteorder="little") ) return card_list @@ -81,15 +81,13 @@ def get_deck_dict(): ) ma_cid_list = deck_bytes_to_list( read_memory_bytes( - pm, pointer_to_address( - pm, ma_cards_addr, ma_cards_offsets), ma_count + pm, pointer_to_address(pm, ma_cards_addr, ma_cards_offsets), ma_count ), ma_count, ) ex_cid_list = deck_bytes_to_list( read_memory_bytes( - pm, pointer_to_address( - pm, ex_cards_addr, ex_cards_offsets), ex_count + pm, pointer_to_address(pm, ex_cards_addr, ex_cards_offsets), ex_count ), ex_count, ) @@ -167,23 +165,24 @@ def ydk_converter(ydk_deck: str, game_client_locale: str = "en"): cards_db = get_database(db_name) # id -> en, jp, cid cards_db_cache = { - card_info['id']: { - "jp_name": card_info['jp_name'], - "en_name": card_info['en_name'], - "cid": cid - } for(cid, card_info) in cards_db.items() + card_info["id"]: { + "jp_name": card_info["jp_name"], + "en_name": card_info["en_name"], + "cid": cid, + } + for (cid, card_info) in cards_db.items() } except Exception: print(_("无法读取卡组信息")) return None result = [] - for line in ydk_deck.split('\n'): + for line in ydk_deck.split("\n"): cards_id = line.strip() - if cards_id.startswith('#') or cards_id == '': + if cards_id.startswith("#") or cards_id == "": # 跳过注释和空行 continue - elif cards_id.startswith('!'): + elif cards_id.startswith("!"): # side deck break elif not cards_id.isdigit(): @@ -201,7 +200,9 @@ def ydk_converter(ydk_deck: str, game_client_locale: str = "en"): try: # 获取卡片名称 - result.append((f"{card_info[f'{game_client_locale}_name']}", card_info['cid'])) + result.append( + (f"{card_info[f'{game_client_locale}_name']}", card_info["cid"]) + ) except Exception as e: print(e) print(_("格式有误")) @@ -209,6 +210,7 @@ def ydk_converter(ydk_deck: str, game_client_locale: str = "en"): return result + def _check_two_array_not_same(deck1: list[int], deck2: list[int]): """ 给两个拥有重复元素的列表,返回各自中独立存在的元素 @@ -222,7 +224,7 @@ def _check_two_array_not_same(deck1: list[int], deck2: list[int]): if l < len(deck1) and r < len(deck2) and deck1[l] == deck2[r]: l += 1 r += 1 - elif l < len(deck1) and (r == len(deck2)-1 or deck1[l] < deck2[r]): + elif l < len(deck1) and (r == len(deck2) - 1 or deck1[l] < deck2[r]): error1.append(deck1[l]) l += 1 elif r < len(deck2): @@ -233,7 +235,7 @@ def _check_two_array_not_same(deck1: list[int], deck2: list[int]): def check_deck(ydk_deck: list[int], locale): _dict = get_deck_dict() - if not "error" in _dict: + if "error" not in _dict: deck1 = sorted(list(map(int, _dict["ma_cid_list"] + _dict["ex_cid_list"]))) deck2 = sorted(ydk_deck) error1, error2 = _check_two_array_not_same(deck1, deck2) diff --git a/mdt_gui.py b/mdt_gui.py index 0573afb..998f937 100644 --- a/mdt_gui.py +++ b/mdt_gui.py @@ -403,7 +403,7 @@ def main(): ], ] window = sg.Window( - "MDT v0.2.14 beta GPLv3", + "MDT v0.2.15 GPLv3", card_frame, default_element_size=(12, 1), font=("Microsoft YaHei", font_size), @@ -543,12 +543,19 @@ def main(): webbrowser.open("https://github.com/SkywalkerJi/mdt#contact-us") elif event == "DECK_CHECK_ERROR": print(values) - sg.popup(f"{', '.join([f'【{card}】' for card in values['DECK_CHECK_ERROR']['error2']])} 未成功导入!", - title="ERROR", font=("Microsoft YaHei", font_size)) + sg.popup( + f"{', '.join([f'【{card}】' for card in values['DECK_CHECK_ERROR']['error2']])} 未成功导入!", + title="ERROR", + font=("Microsoft YaHei", font_size), + ) elif event == "DECK_CHECK_OK": sg.popup("卡组导入成功,请点击确认", title="ERROR", font=("Microsoft YaHei", font_size)) elif event == "CARD_NOT_FOUND_ERROR": - sg.popup("卡组格式错误,请重新从微信小程序游戏王卡查器获取", title="ERROR", font=("Microsoft YaHei", font_size)) + sg.popup( + "卡组格式错误,请重新从微信小程序游戏王卡查器获取", + title="ERROR", + font=("Microsoft YaHei", font_size), + ) if not settings_active and event == _("设置"): settings_active = True @@ -853,21 +860,25 @@ def main(): # win32api 发送消息没成功 # spy++ 检测手动与自动消息是一样的,不知道原因 warning_layout = [ - [sg.Text('1. 请保证处于卡组添加区域且卡组为空。')], - [sg.Text('2. 请保证右侧卡组搜索面板无遮挡。')], - [sg.Text('3. 请打开未拥有卡片。')], - [sg.Text('4. 添加卡片使用的是PyAutoGUI,如果过程中出现意外,请将鼠标移动到左上角解除操作。')], - [sg.Text('5. 取消可以终止此次操作。')], - [sg.Button('Ok'), sg.Button('Cancel')] + [sg.Text("1. 请保证处于卡组添加区域且卡组为空。")], + [sg.Text("2. 请保证右侧卡组搜索面板无遮挡。")], + [sg.Text("3. 请打开未拥有卡片。")], + [sg.Text("4. 添加卡片使用的是PyAutoGUI,如果过程中出现意外,请将鼠标移动到左上角解除操作。")], + [sg.Text("5. 取消可以终止此次操作。")], + [sg.Button("Ok"), sg.Button("Cancel")], ] - warning_window = sg.Window('Warning', warning_layout, font=("Microsoft YaHei", font_size)) + warning_window = sg.Window( + "Warning", warning_layout, font=("Microsoft YaHei", font_size) + ) flag = False while True: event, values = warning_window.read() - if event == sg.WIN_CLOSED or event == 'Cancel': # if user closes window or clicks cancel + if ( + event == sg.WIN_CLOSED or event == "Cancel" + ): # if user closes window or clicks cancel flag = False break - elif event == 'Ok': + elif event == "Ok": flag = True break warning_window.close() @@ -875,14 +886,21 @@ def main(): try: # 直接从剪贴板读入ydk卡组 win32clipboard.OpenClipboard() - text = win32clipboard.GetClipboardData(win32con.CF_TEXT).decode("utf-8") + text = win32clipboard.GetClipboardData(win32con.CF_TEXT).decode( + "utf-8" + ) except TypeError: - sg.popup('Error: No text on the clipboard!') + sg.popup("Error: No text on the clipboard!") finally: win32clipboard.CloseClipboard() - + try: - service.ydk_converter(ydk_deck=text, window=window, game_client_locale=game_client_locale, locale=locale) + service.ydk_converter( + ydk_deck=text, + window=window, + game_client_locale=game_client_locale, + locale=locale, + ) except Exception as e: print(e) window.close() diff --git a/mdt_service.py b/mdt_service.py index a1d4ccb..835dfd7 100644 --- a/mdt_service.py +++ b/mdt_service.py @@ -1,10 +1,9 @@ from threading import Thread - import mdt as mdt -from mdt_cv import get_search_button_postion import mdt_control import mdt_deck_reader as reader + def start(): mdt_service = Thread(target=mdt.main) mdt_service.start() @@ -84,15 +83,19 @@ def get_deck_dict(): def get_deck_string(locale: str): return reader.get_deck_string(locale) -def ydk_converter(ydk_deck: str, window, game_client_locale: str='en',locale: str='zh-CN'): - tmp=reader.ydk_converter(ydk_deck, game_client_locale) + +def ydk_converter( + ydk_deck: str, window, game_client_locale: str = "en", locale: str = "zh-CN" +): + tmp = reader.ydk_converter(ydk_deck, game_client_locale) if tmp is None: - window.write_event_value('CARD_NOT_FOUND_ERROR', None) + window.write_event_value("CARD_NOT_FOUND_ERROR", None) tmp.sort(key=lambda tup: tup[0]) # TODO: 防止二次生成 thread = Thread(target=mdt_control.ydk_converter, args=(tmp, locale, window)) thread.start() + if __name__ == "__main__": - ydk_converter("", "") \ No newline at end of file + ydk_converter("", "")