PoE(패스오브엑자일) 크래프팅을 도와주는 매크로
————————————–
2019-10-08
역병리그 업데이트가 되면서 전쟁지팡이 클래스가 추가 되었군요 !! 이제 전쟁지팡이 클래스도 해당 애드온 적용이 가능 할 겁니다. ! ㅎㅎ
안녕하세요 라이프온룸 입니다.
오늘은 크래프팅을 도와 주는 매크로의 일환으로 내 인벤토리 무기 방어구에 대체의 오브, 카오스 오브를 발랐을 때 출현 가능한 Affix를 보여주고 변환의 오브나 카오스 오브를 바를 때 내가 입력한 Affix 조건에 만족시 마우스 클릭을 막아주는 매크로를 만들어 보겠습니다. !!! 자세한 사용 방법은 아래 영상을 참조해 주세요 !
그리고 해당 매크로는 영정의 가능성이 없을 것 같아 EXE 파일도 공개 하겠습니다. 아래 링크를 이용하시면 됩니다.
- 가능한 mod 확인 기능 영상
- 원하는 Affix 출현시 마우스를 막아주는 기능 영상
1. Affix
Affix(Prefix, Suffix) 는 PoE 매직, 레어, 유니크 아이템등에 붙을 수 있는 옵션입니다. 그리고 이 추가적인 옵션의 집합을 Explict 라고 하지요
매직아이템의 경우에는 0~1 개의 Prefix와 Suffix가 붙을 수 있습니다. 레어아이템의 경우엔 1~3 개 이구요
즉 아래 아이템의 경우에는 희귀 아이템이고 3개의 Prefix와 3개의 Suffix가 붙어 있으니 이제 더 이상 추가적인 Affix는 붙을 수 없습니다.
그런데 이 Affix는 아이템 Base 종류별, 레벨별, 요구사항별 붙을 수 있는 옵션이 다 다릅니다. 그래서 이 매크로를 통해서 좀 더 쉽게 어떤 Affix가 붙을 수 있는지 알 수 있을거에요 ㅎㅎ
그럼 바로 필요한 라이브러리가 어떤게 있는지 보겠습니다.
2. 라이브러리
- pip install wxPython
wxPython은 cross-platform GUI toolkit 으로 윈도우, 맥, 리눅스 등에서 GUI 프로그램을 가능하게 해주는 라이브러리 입니다. 역사가 오래된 만큼 자료도 많고 예제도 많은 것 같습니다. 하지만 기본적인 위젯만을 지원하여 휘양 찬란한 GUI 프로그램을 만드는데는 적합하진 않은 것 같아요 ㅎㅎ 여기서는 뭐 기능 위주로만 하면 되니깐 한번 사용해 봤습니다.
추가적으로 저번시간에 만들었던 inventoryFunc.py 파일이 필요 합니다. 이 파일은 코드 내용이 좀 바뀌어서 아래 코드란에 다시 올려 둘게요 ㅎㅎ
3. config.cfg
[orb_helper] key_possible_mod = F5 key_possible_mod_detail = F6 key_orb_mouse_block = F7 [tobe_item_explict] exp = '|' or '&' and Prefix 0~1, Suffix 0~1 Prefix 1~3, Suffix 1~3 prefix = 회피 #% 증가 suffix =
이번 매크로 기능에 필요한 데이터 입니다. 각 센션 및 옵션별 역할을 한번 볼게요 ㅎㅎ
[orb_helper] 섹션
- key_possible_mod = F5: 마우스를 아이템에 갖다 대고 F5를 누르면 해당 아이템 레벨에 붙을 수 있는 모든 Mod들이 Config.cfg에 써집니다.
- key_possible_mod =F6: Mod들의 Affix 이름과 구체적인 정보들이 Config.cfg에 써집니다.
- key_orb_mouse_block=F7: F7을 누르면 윈도우 창 하나가 뜨면서 [tobe_item_explict] 섹션의 조건들이 만족 되었을 때 마우스를 Block 시키는 기능이 실행 됩니다. 음 .. 말로 설명하려니 어렵내요 .. 영상 참조 부탁드립니다. ㅋㅋ
[tobe_item_explict] 섹션은 F7 기능을 수행하기 위한 조건식을 입력하는 섹션입니다. 해당 섹션을 설명하기 이전에 프로그램 또는 파이썬 코드 실행 후 PoE 아이템에 마우스를 대고 F5를 누른 뒤 config.cfg의 내용을 봅시다.
아래 내용이 추가되어 있겁니다.
[current_item_explict] val = 회피 74% 증가 화염 저항 +15% [possible_item_explict] val = -- Possible Mod on 투구(Dex) Item Level : 60 - prefix ---------------------------------------- 생명력 최대치 # 회피 #% 증가 회피 #% 증가 /기절 및 막기 회복 #% 증가 회피 # 회피 # /생명력 최대치 # 발견한 아이템 희귀도 #% 증가 장착된 소환수 스킬 젬 레벨 # 근접 공격자에게 #의 물리 피해 반사 - suffix ---------------------------------------- 민첩 # 지능 # 발견한 아이템 희귀도 #% 증가 정확도 # 초당 생명력 # 재생 화염 저항 #% 냉기 저항 #% 번개 저항 #% 카오스 저항 #% 기절 및 막기 회복 #% 증가 능력치 요구사항 #% 감소 시야 반경 #% 증가 /일반 정확도 #% 증가 ----------------------------------------
참고로 위 데이터는 다음 아이템에 대한 데이터 입니다.
여튼 [possible_item_explict] 섹션의 prefix와 suffix 중에 필요한 옵션을 [tobe_item_explict] 에 복붙 하시면 됩니다.
예를 들어 내가 이 삼각모의 옵션에 생명력 최대치랑 카오스 저항이 들어가게 하고 싶다면 아래와 같이 입력하면 됩니다.
[tobe_item_explict] exp = '|' or '&' and Prefix 0~1, Suffix 0~1 Prefix 1~3, Suffix 1~3 prefix = 생명력 최대치 # suffix = 카오스 저항 #%
희귀 아이템의 경우 prefix, suffix 가 세개 까지 붙을 수 있죠 ? 이 경우에는 |(or), &(and) 로 조건식을 세팅할 수 있습니다.
예를 들어 위 삼각모가 희귀아이템이라고 하고 내가 원하는 옵션이
생명력 최대치와 기절 및 막기 회복 증가 중 하나가 있어야 하고
발견한 아이템 희귀도 증가가 있어야 할 경우 아래처럼 세팅 하시면 됩니다.
[tobe_item_explict] exp = '|' or '&' and Prefix 0~1, Suffix 0~1 Prefix 1~3, Suffix 1~3 prefix = 생명력 최대치 # | 기절 및 막기 회복 #% 증가 & 발견한 아이템 희귀도 #% 증가 suffix =
참고로 프로그래밍의 세계에서는 &(and)가 우선이지만 여기서는 |(or) 가 우선입니다. 그리고 prefix 와 suffix 는 무조건 &(and) 입니다. !!
조건식에 입력된 Mod가 [possible_item_explict] 에서 찾을 수 없는 것이거나 희귀도 에 부합하지 않은 식일 경우 아래처럼 CMD 창에 아래와 같이 Condition Expresion Error 라는 문구가 뜰겁니다.
조건식이 정상일 경우 아래와 같은 창이 뜰거에요 !!!
그리고 오브를 바르다가 조건식을 만족하는 Explict가 나오면 아래와 같이 되면서 마우스 클릭을 못하게 됩니다. !!!
4. 코드
세개의 Python 파일과 Json DB 파일이 필요 합니다. Json DB 파일은 EXE 다운로드 링크에서 data 폴더를 가져오시면 됩니다. 즉 폴더 구조는 아래와 같아요 !
- orbHelper.py
- inventoryFunc.py
- gui.py
- data
- item_bases_armour_kr.json
- item_bases_kr.json
- item_bases_weapon_kr.json
- mods.json
#-*- coding:utf-8 -*- import win32clipboard import pyautogui as pa import mouse as mo import keyboard as keys import time import numpy as np import pprint import cv2.cv2 as cv2 import mss import os from PIL import Image import codecs import json import pywinauto as pwa import copy K_RARENESS = '희귀도' K_ITEMNAME = 'item_name' K_ITEMBASENAME = 'item_base_name' K_ISSELL = 'isSell' K_EXPLICT = 'explicitMods' K_ITEMCLASS = 'itemClass' K_CORRUPTION = '타락복제여부' V_CURRENCY = '화폐' V_NORMAL = '보통' V_MAGIC = '마법' V_RARE = '희귀' V_UNIQUE = '고유' V_CORRUPTION = '타락' V_DUP = '복제' K_CHECK = '미확인여부' CHECK_ITME = '감정 주문서' KG_SOCKET = '홈' KG_REQUIRE = '요구사항' KG_ITEMLV = '아이템 레벨' STAT_USE = ['장갑', '장화', '갑옷', '투구', '방패'] STAT_TRANS = {'힘':'Str', '민첩':'Dex', '지능':'Int'} EXCEPT_CURRENCY = ['감정 주문서', '포탈 주문서'] EXCEPT_SELLING = ['고유'] # Make Active PoE window def setActivePoe(): app = pwa.application.Application() try: app.connect(title_re='Path of Exile') app_dialog = app.window() app_dialog.set_focus() except Exception as e: print(e, ' SetActive POE Fail') class InventoryTool(): def __init__(self, inventorySize, listexceptCurr): # inventorySize x, y, w, h self.itemInfoInInven = {} self.inventorySize = inventorySize self.inven = np.empty(shape=(12, 5), dtype=tuple) self.invenUnitSize = (int(inventorySize[2] / 12), int(inventorySize[2] / 12)) self.invenUnitBox = (inventorySize[0] + 5, inventorySize[1] + 5, \ self.invenUnitSize[0]-5, self.invenUnitSize[1]-5) self.listexceptCurr = listexceptCurr for xunit in range(12): for yunit in range(5): x = self.inventorySize[0] + xunit * self.invenUnitSize[0] y = self.inventorySize[1] + yunit * self.invenUnitSize[0] cord = pa.center((x, y, self.invenUnitSize[0], self.invenUnitSize[1])) self.inven[xunit][yunit] = cord # init Data of inventory def initItemInfoInven(self): self.itemInfoInInven = {} # make unitPoint(0, 0) to realPoint(1200, 700) def realPoint(self, unitPointX, unitPointY): return (self.inven[unitPointX][unitPointY][0], self.inven[unitPointX][unitPointY][1]) # make realPoint(1200, 700) to unitPoint(0, 0) def makeUnitPoint(self, realPointX, realPointY): a = [(ix, iy) for ix, row in enumerate(it.inven) for iy, i in enumerate(row) if i == (realPointX, realPointY)] return a[0] # clipboard string to dict def makeItemInfoOfClipboard(self, cText): itemInfo = {} specList = cText.split('--------') specList = [i.strip() for i in specList] # 확인 여부에 따라 아이템 Base 이름이 달라짐 # -- Item Class, Name Info itemClassAndName = specList.pop(0) # -- 희귀도 infoList = itemClassAndName.split('\n') infoList = [i.strip() for i in infoList] rareness = infoList.pop(0) rareness_key, rareness_value = rareness.split(':') rareness_key = rareness_key.strip() rareness_value = rareness_value.strip() itemInfo[rareness_key] = rareness_value # -- 요구 사항 itemInfo[KG_REQUIRE] = {} for idx, data in enumerate(specList): if data.find(KG_REQUIRE) == 0: reqList = data.split('\n') reqList = [i.strip() for i in reqList] reqList.pop(0) for spec in reqList: specK, specV = spec.split(':') itemInfo[KG_REQUIRE][specK] = specV.strip() specList.pop(idx) break # -- 홈 and Item Level tempList = specList.copy() for idx, data in enumerate(tempList): if (data.find(KG_SOCKET) == 0) or (data.find(KG_ITEMLV) == 0): specK, specV = data.split(':') itemInfo[specK.strip()] = specV.strip() specList.remove(data) # -- 타락 여부 itemInfo[K_CORRUPTION] = '' for idx, data in enumerate(tempList): if data.find(V_CORRUPTION) == 0: itemInfo[K_CORRUPTION] = V_CORRUPTION specList.remove(data) # -- 복제 여부 itemInfo[K_CORRUPTION] = '' for idx, data in enumerate(tempList): if data.find(V_DUP) == 0: itemInfo[K_CORRUPTION] = V_DUP specList.remove(data) # -- 확인여부 itemInfo[K_EXPLICT] = [] try: if specList.index('미확인') >= 0: isCheck = True else: isCheck = False itemInfo[K_CHECK] = isCheck except Exception as e: isCheck = False itemInfo[K_CHECK] = isCheck # -- 미확인 Item if isCheck == True: itemInfo[K_CHECK] = isCheck # -- 아이템 Base 이름 itemName = infoList.pop(0) itemInfo[K_ITEMBASENAME] = itemName # -- 확인 아이템 else: # 희귀 혹은 고유 아이템의 경우 아이템 이름 존재 if rareness_value == V_RARE or rareness_value == V_UNIQUE: itemName = infoList.pop(0) itemInfo[K_ITEMNAME] = itemName itemName = infoList.pop(0) itemInfo[K_ITEMBASENAME] = itemName # Normal 아이템 혹은 Magic 아이템의 경우 Base Name에 추가 내용이 붙음 else: # -- 아이템 Base 이름만 존재 # -- 마법아이템의 경우 아이템 Base 이름에 Prefix, Suffix 붙음 itemName = infoList.pop(0) itemInfo[K_ITEMBASENAME] = itemName # -- explict if rareness_value == V_RARE or rareness_value == V_UNIQUE or rareness_value == V_MAGIC: # item 설명 제거 if rareness_value == V_UNIQUE: specList.pop() if itemInfo[K_ITEMBASENAME].find('주얼') >= 0 or itemInfo[K_ITEMBASENAME].find('플라스크') >= 0: specList.pop() # --------------------------------------------- explicts = specList.pop() explictList = explicts.split('\n') explictList = [i.strip() for i in explictList] itemInfo[K_EXPLICT] = explictList #print('Not Parsed: ', specList) return itemInfo def getItemSize(self, itemSpec): try: return (itemSpec['Width'], itemSpec['Height']) except: return (1, 1) # 현제 좌표에서 아이템 영역 def makeItemUnitSize(self, itemSize, CurrentCoord): x, y = itemSize coord = [] for xi in range(x): for yi in range(y): coord.append((CurrentCoord[0] + xi, CurrentCoord[1] + yi)) return coord # .json 파일의 결과인 itemDB 에서 item base name 검색 def findItemOnDB(self, itemDB, invenItemDict, findKey=''): try: itemName = invenItemDict[K_ITEMBASENAME] itemRareness = invenItemDict[K_RARENESS] except Exception as e: print (e, 'Not Found',invenItemDict) return None, None for itemSubClass in itemDB: pkey = [*itemSubClass][0] for key, itemSpec in itemSubClass[pkey].items(): # if itemRareness == V_MAGIC or itemRareness == V_NORMAL: if itemName.find(key) >=0: print('Item Name : ', key, ', Item Name On Inventory: ', itemName) if findKey != '': try: return findKey, itemSpec[findKey] except: print('Invalid Key ') return None, None else: return key, self.getItemSize(itemSpec) # else: # if key == itemName: # print ('Found ', key) # return key, self.getItemSize(itemSpec) return None, None # get clipboard data def getItemInfoFromClipboard(self, unitPoint): itemInfo = {} keys.send("ctrl+c") time.sleep(0.05) try: win32clipboard.OpenClipboard() itemData = win32clipboard.GetClipboardData() win32clipboard.EmptyClipboard() win32clipboard.CloseClipboard() itemInfo = self.makeItemInfoOfClipboard(itemData) self.itemInfoInInven[unitPoint] = itemInfo return itemInfo except Exception as e: print('No Item ', unitPoint) return None def checkItemInInvertory(self, boxRegions): for x in range(np.shape(self.inven)[0]): for y in range(np.shape(self.inven)[1]): rpoint = self.realPoint(x, y) if self.checkEmptyUnitPoint(boxRegions, rpoint): mo.move(rpoint[0], rpoint[1]) time.sleep(0.03) self.getItemInfoFromClipboard((x, y)) def checkItemInInventoryWithInfo(self, boxRegions, itemDB = []): oned_array = np.reshape(self.inven, (np.prod(self.inven.shape),)) exceptUnitCoord = [] for coord in oned_array: if self.checkEmptyUnitPoint(boxRegions, coord): unitPoint = self.makeUnitPoint(coord[0], coord[1]) if (unitPoint in exceptUnitCoord) == False: mo.move(coord[0], coord[1]) time.sleep(0.03) itemInfo = self.getItemInfoFromClipboard(unitPoint) if itemDB != [] and itemInfo != None: itemName, itemSize = self.findItemOnDB(itemDB, itemInfo) # inventory item이 DB에 있을 시 if itemSize != None: exceptUnitCoord = exceptUnitCoord + self.makeItemUnitSize(itemSize, unitPoint) if self.itemInfoInInven[unitPoint][K_RARENESS] in EXCEPT_SELLING: self.itemInfoInInven[unitPoint][K_ISSELL] = False else: self.itemInfoInInven[unitPoint][K_ISSELL] = True else: self.itemInfoInInven[unitPoint][K_ISSELL] = False def moveCurrencyToStash(self): print('---moveCurrencyToStash---') keys.press('ctrl') for unitPoint, iteminfo in self.itemInfoInInven.items(): rpoint = self.realPoint(unitPoint[0], unitPoint[1]) try: # print('move', iteminfo[K_ITEMBASENAME]) if iteminfo[K_RARENESS] == V_CURRENCY \ and not iteminfo[K_ITEMBASENAME] in self.listexceptCurr: mo.move(rpoint[0], rpoint[1]) time.sleep(0.05) mo.click() print('Move Inven To Stash: ', iteminfo[K_ITEMBASENAME]) except: pass keys.release('ctrl') print('--------------------') self.itemInfoInInven = {} def findItemOnInventory(self, itemName): for key, item in self.itemInfoInInven.items(): if item[K_ITEMBASENAME] == itemName: return key return None def itemConfirm(self): checkItemUnitCoord = self.findItemOnInventory(CHECK_ITME) if checkItemUnitCoord == None: return checkItemPoint = self.realPoint(checkItemUnitCoord[0], checkItemUnitCoord[1]) print('-----itemConfirm-----') for key, item in self.itemInfoInInven.items(): if item[K_CHECK] == True: # -- 감정주문서로 이동 후 우클릭 mo.move(checkItemPoint[0], checkItemPoint[1]) time.sleep(0.05) mo.click(mo.RIGHT) time.sleep(0.05) # -- 감정할 아이템이로 가서 클릭 itemPoint = self.realPoint(key[0], key[1]) mo.move(itemPoint[0], itemPoint[1]) time.sleep(0.05) mo.click() time.sleep(0.05) print('Item Confirmed : ', item[K_ITEMBASENAME]) print('--------------------') def itemSell(self): print('-----itemSell-----') keys.press('ctrl') for key, item in self.itemInfoInInven.items(): if item[K_ISSELL] == True: itemPoint = self.realPoint(key[0], key[1]) mo.move(itemPoint[0], itemPoint[1]) time.sleep(0.05) mo.click() time.sleep(0.05) print('Item On Sell : ', item[K_ITEMBASENAME]) self.itemInfoInInven = {} keys.release('ctrl') print('--------------------') def checkEmptyUnitPoint(self, boxRegions, centerRpoint): for br in boxRegions: x, y, w, h = br if (x < centerRpoint[0] < x + w) and (y < centerRpoint[1] < y + h): # nothing on inven return False # something on inven return True # img's bgr min, max def getBGRMinMax(self, img): # frame = cv2.GaussianBlur(frame, (5, 5), 0) min_ch = (np.amin(img[:, :, 0]), np.amin(img[:, :, 1]), np.amin(img[:, :, 2])) max_ch = (np.amax(img[:, :, 0]), np.amax(img[:, :, 1]), np.amax(img[:, :, 2])) #print("max_ch = ", max_ch, min_ch) return min_ch, max_ch def findImage(self, templateName, show=0, confidence=0.6): boxRegions = [] x, y, w, h = self.inventorySize mon = {'top': y, 'left': x, 'width': w, 'height': h} sct = mss.mss() sct.grab(mon) img = Image.frombytes('RGB', (w, h), sct.grab(mon).rgb) frame = cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR) copyImg = copy.copy(frame) gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) imgPath = os.path.dirname(os.path.realpath(__file__)) + '\\' + templateName template = cv2.imread(imgPath) tmin, tmax = self.getBGRMinMax(template) template = cv2.cvtColor(template, cv2.COLOR_BGR2GRAY) w, h = template.shape[::-1] # 입력된 templateName 과 같은 그림을 이벤토리 에서 찾음 res = cv2.matchTemplate(gray, template, cv2.TM_CCOEFF_NORMED) threshold = confidence loc = np.where(res >= threshold) for pt in zip(*loc[::-1]): cropedImg = copyImg[pt[1]:pt[1] + h, pt[0]:pt[0] + w] dmin, dmax = self.getBGRMinMax(cropedImg) # compare only min if dmin[0] <= tmin[0] and dmin[1] <= tmin[1] and dmin[2] <= tmin[2]: if show == 1: cv2.rectangle(frame, pt, (pt[0] + w, pt[1] + h), (0, 0, 255), 2) realX = self.inventorySize[0] + pt[0] realY = self.inventorySize[1] + pt[1] boxRegions.append((realX, realY, w, h)) if show == 1: cv2.imshow('image', frame) cv2.waitKey(0) cv2.destroyAllWindows() return boxRegions def makeTemplate(self, templateName, templatePoint): x, y, w, h = templatePoint mon = {'top': y, 'left': x, 'width': w, 'height': h} sct = mss.mss() sct.grab(mon) img = Image.frombytes('RGB', (w, h), sct.grab(mon).rgb) frame = cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR) imgPath = os.path.dirname(os.path.realpath(__file__)) + '\\' + templateName cv2.imwrite(imgPath, frame) # ----------- mods def getItemInfoForMods(self, unitPoint, itemDB): itemInfo = self.getItemInfoFromClipboard(unitPoint) if itemInfo != None: itemSubClassKey, itemSubClass = self.findItemOnDB(itemDB, itemInfo, 'Item Class') if itemSubClass == None: print('No Item In DB') return None #return itemInfo itemChar = '' if KG_REQUIRE in itemInfo and itemSubClass in STAT_USE: for key, val in itemInfo[KG_REQUIRE].items(): if key in STAT_TRANS: itemChar += STAT_TRANS[key] if itemChar != '': itemChar = '(' + itemChar + ')' itemSubClassForMods = itemSubClass + itemChar itemInfo[K_ITEMCLASS] = itemSubClassForMods return itemInfo
—orbHelper.py—
#-*- coding:utf-8 -*- import inventoryFunc as invenF import gui as gui import mouse as mo import keyboard as keys import pprint import time import os import codecs import json import re import multiprocessing as mp import configparser from ast import literal_eval jsonFileDBs = ['mods'] modsDB = {} for jDB in jsonFileDBs: path = os.path.dirname(os.path.realpath(__file__)) + '\\' + 'data' + '\\' + jDB + '.json' with codecs.open(path, 'r', 'utf-8-sig') as f: json_data = json.load(f) modsDB = json_data f.close() jsonFileDBs = ['item_bases_weapon_kr', 'item_bases_kr', 'item_bases_armour_kr'] itemDB = [] for jDB in jsonFileDBs: path = os.path.dirname(os.path.realpath(__file__)) + '\\' + 'data' + '\\' + jDB + '.json' with codecs.open(path, 'r', 'utf-8-sig') as f: json_data = json.load(f) itemDB.append(json_data) f.close() GUITHREAD = None GUIQ = mp.Queue() ORBHELPERQ = mp.Queue() # mod.json의 Prefix Suffix 항목들을 Lv 별로 재정리 def sortAndFilt(moddict, itemLevel): itemLevel = int(itemLevel) valList = [] for key, val in moddict.items(): tmpDict = {key: val} valList.append(tmpDict) newlist = sorted(valList, key=lambda k: int([*k.values()][0]['lv'])) filtList = list(filter(lambda k: int([*k.values()][0]['lv']) < itemLevel, newlist)) #print(filtList) if filtList == []: return None, None, None minVal = [*filtList[0].values()][0]['val'] maxVal = [*filtList[-1].values()][0]['val'] detailStr = '' for data in filtList: for k, v in data.items(): #detailStr += '-- affix: %-8s lv: %-3s stat: %s \n' % (k, v['lv'], v['val']) detailStr += '-- affix: ' + k.ljust(8) + 'lv: ' + v['lv'].ljust(4) + 'stat: ' + v['val'] + '\n' return minVal, maxVal, detailStr def makeItemModStr(modStr, strToAdd): if modStr.find('\n') >= 0: tmpStr = modStr.split('\n') strToAdd += ' /'.join(tmpStr) + '\n' else: strToAdd += modStr + '\n' return strToAdd def writeConfigFile(configFile, section, writeStr, opt = 'val'): config = configparser.ConfigParser(interpolation=None) config.read_file(codecs.open(configFile, 'r', 'UTF-8-sig')) result = section in config.sections() if result == False: config.add_section(section) config[section][opt] = writeStr with open(configFile, 'w', encoding='UTF-8-sig') as config_file: config.write(config_file) def getCurrentItemExplict(modValReg, invenItem): comExpStringList = [] if invenF.K_EXPLICT in invenItem: for expString in invenItem[invenF.K_EXPLICT]: comExpString = expString while True: ms = modValReg.search(comExpString) if ms == None: break start, end = ms.span() comExpString = comExpString[:start] + '#' + comExpString[end:] comExpStringList.append(comExpString) return comExpStringList # 특정 아이템의 레벨별 모드 정보를 불러옴 def getPossibleMod(modValReg, invenItem, configFile, detail=False): if invenItem == None: return # pprint.pprint(result) print('-----------') affixDict = {} try: itemLv = int(invenItem[invenF.KG_ITEMLV]) except: print('No ITEM LEVEL') itemClass = invenItem[invenF.K_ITEMCLASS] comExpStringList = [] configModStr = '' if invenF.K_EXPLICT in invenItem: for expString in invenItem[invenF.K_EXPLICT]: comExpString = expString configModStr += expString + '\n' while True: ms = modValReg.search(comExpString) if ms == None: break start, end = ms.span() comExpString = comExpString[:start] + '#' + comExpString[end:] comExpStringList.append(comExpString) configModStr = '\n' + configModStr writeConfigFile(configFile, SECTION_CUR, configModStr) print('Curren Item Explict:' + configModStr) print('----------------------------------------') configModStr = '' for mainClass, itemData in modsDB.items(): if itemClass in itemData: configModStr += '-- Possible Mod on ' + itemClass + ' Item Level : ' + str(itemLv) + '\n' for affix in ['prefix', 'suffix']: configModStr += '- ' + affix + '\n' configModStr += '----------------------------------------' + '\n' affixDict[affix] = [] for invenItemMod, modVal in itemData[itemClass][affix].items(): modMin, modMax, modStr = sortAndFilt(modVal['modval'], itemLv) if modMin != None: affixDict[affix].append(invenItemMod) configModStr = makeItemModStr(invenItemMod, configModStr) if detail == True: configModStr += modStr configModStr += '----------------------------------------' + '\n' print(configModStr) # minMax = sortAndFilt(modVal['modval'], itemLv) # print('\t minmax: ', minMax) configModStr = '\n' + configModStr writeConfigFile(configFile, SECTION_POS, configModStr) break # for invenItemMod in comExpStringList: # if invenItemMod in [*itemData[itemClass]['prefix']]: # print(invenItemMod, 'is', ' prefix') # elif invenItemMod in [*itemData[itemClass]['suffix']]: # print(invenItemMod, 'is', ' suffix') return affixDict # 입력한 조건식이 유효한지 판단 def checkConditionValidation(conditionList, rarity, possibleMods): affixCntDict = {} returnModList = [] for affix, condition in conditionList.items(): affixCntDict[affix] = 0 andModList = condition.split('&') andModList = [i.strip() for i in andModList] if andModList[0] == '': continue returnModList += andModList # 정규표현식 # modStrRegEx = re.compile('([^|&]+)') # operatorRegEx = re.compile('([|&]+)') # # modList = modStrRegEx.findall(condition) # modList = [i.strip() for i in modList] # # opList = operatorRegEx.findall(condition) # opList = [i.strip() for i in opList] print('----------------------------------------') print(affix, ' Condition Mode List:', andModList) checkValidCondition = 0 for orExpresion in andModList: condMods = orExpresion.split('|') condMods = [i.strip() for i in condMods] for condMod in condMods: for modDB in possibleMods[affix]: if modDB.find(condMod) >= 0: print(affix, ' cond: ', condMod, affix, ' db : ', modDB) checkValidCondition = 1 break if checkValidCondition == 0: print('no condition found on DB') return False checkValidCondition = 0 affixCntDict[affix] += 1 print(affix + 'CNT : ' + str(affixCntDict[affix])) print('----------------------------------------') totalCnt = 0 for affix, cnt in affixCntDict.items(): totalCnt += cnt if rarity == invenF.V_MAGIC: if cnt > 1: return False elif rarity == invenF.V_RARE: if cnt > 3: return False if totalCnt == 0: return False return returnModList # 입력한 조건식과 실제 아이템의 Explict List 가 부합하는지 확인 def compareConditionExpress(explictList, andModList): condition = 0 condList = [] for orExpresion in andModList: condMods = orExpresion.split('|') condMods = [i.strip() for i in condMods] for condMod in condMods: for itemMod in explictList: if itemMod.find(condMod) >= 0: condition = True condList.append(condition) condition = False if False in condList: return False return True pass # Gui 프로세스 def guiProcess(gui_q, orbHelper_q, guiInitPosSize, configFile): if guiInitPosSize != None: initPos = guiInitPosSize[:2] initSize = guiInitPosSize[2:] else: initSize = None initPos = None app = gui.wx.App(False) frm = gui.AppFrame(gui_q, initSize, configFile) if initPos != None: gui.setFrmPosition(frm, initPos) frm.Show() app.MainLoop() orbHelper_q.put_nowait('end') def runGui(guiInitPosSize, configFile): global GUITHREAD global GUIQ global ORBHELPERQ if GUITHREAD == None: GUITHREAD = mp.Process(target=guiProcess, args=(GUIQ, ORBHELPERQ, guiInitPosSize, configFile)) GUITHREAD.start() #GUITHREAD.join() else: print('gui Alive') def mainOrbHelper(keyboardKey, configFile): global GUITHREAD it = invenF.InventoryTool((2599, 1152, 1213, 513), []) modValReg = re.compile('([+.0-9]+)') moLState = 0 moRState = 0 listKeyState = [0] * len(keyboardKey) orbPicked = 0 blockMouseOn = 0 conditionList = False while True: time.sleep(0.001) valLeft = mo.is_pressed('left') valRight = mo.is_pressed('right') try: dataFromGui = ORBHELPERQ.get_nowait() if dataFromGui == 'end': GUITHREAD = None blockMouseOn = False conditionList = False orbPicked = 0 except: dataFromGui = None pass if blockMouseOn == True: # -- left mouse if valLeft != moLState and valLeft == True and orbPicked == 1: GUIQ.put_nowait('item/wait') time.sleep(0.3) #GUIQ.put_nowait('item/match') result = it.getItemInfoForMods((0, 0), itemDB) if result != None: itemExplictList = getCurrentItemExplict(modValReg, result) print('item Explict : ', itemExplictList) if itemExplictList != []: matching = compareConditionExpress(itemExplictList, conditionList) if matching == True: print('Condition Matched') blockMouseOn = False conditionList = False orbPicked = 0 GUIQ.put_nowait('item/match') else: print('Condition Not Matched') GUIQ.put_nowait('item/missmatch') else: print('no item explict') moLState = valLeft elif valLeft != moLState and valLeft == False: orbPicked = 0 moLState = valLeft # -- right mouse if valRight != moRState and valRight == True: moRState = valRight pass elif valRight != moRState and valRight == False: result = it.getItemInfoFromClipboard((0, 0)) try: if result[invenF.K_ITEMBASENAME].find('오브') >= 0: msg = 'item/name/' + result[invenF.K_ITEMBASENAME] GUIQ.put_nowait(msg) orbPicked = 1 except: pass moRState = valRight # -- keys for idx, pressedKey in enumerate(keyboardKey): value = keys.is_pressed(pressedKey) if value == True: # F5 if idx == 0 and listKeyState[idx] != value: invenF.setActivePoe() result = it.getItemInfoForMods((0, 0), itemDB) getPossibleMod(modValReg, result, configFile) listKeyState[idx] = value # F6 elif idx == 1 and listKeyState[idx] != value: invenF.setActivePoe() result = it.getItemInfoForMods((0, 0), itemDB) getPossibleMod(modValReg, result, configFile, detail=True) listKeyState[idx] = value # F7 elif idx == 2 and listKeyState[idx] != value: invenF.setActivePoe() listKeyState[idx] = value # -- Condition Check 후 현제 아이템 상태가 조건을 만족하는지 확인 print('Mouse Blocker Start') if blockMouseOn == False: guiInitPosSize = gui.readConfigFile(configFile, gui.GUI_SECTION, gui.GUI_OPT_FRM_SIZE) if guiInitPosSize != None: guiInitPosSize = literal_eval(guiInitPosSize) #runGui(guiInitPosSize) config = configparser.ConfigParser(interpolation=None) config.read_file(codecs.open(configFile, 'r', 'UTF-8-sig')) condDict ={ 'prefix' : config[SECTION_COND]['prefix'], 'suffix' : config[SECTION_COND]['suffix'] } result = it.getItemInfoForMods((0, 0), itemDB) if result != None: modDict = getPossibleMod(modValReg, result, configFile) conditionList = checkConditionValidation(condDict, result[invenF.K_RARENESS], modDict) if conditionList != False: print('Condition you put: ', conditionList, 'is Valid') itemExplictList = getCurrentItemExplict(modValReg, result) print('item Explict : ', itemExplictList) if itemExplictList != []: runGui(guiInitPosSize, configFile) condMatch = compareConditionExpress(itemExplictList, conditionList) print('Condition Match : ', condMatch) if condMatch: GUIQ.put_nowait('item/match') else: print('this item have no explict') break else: print('Condition Expresion Error') break else: break blockMouseOn = True else: conditionList = False blockMouseOn = False else: if listKeyState[idx] != value: listKeyState[idx] = value if __name__=="__main__": # multiprocessing code 를 excutable로 만들경우 아래 코드가 필요 mp.freeze_support() SECTION_ORB = 'orb_helper' SECTION_POS = 'possible_item_explict' SECTION_CUR = 'current_item_explict' SECTION_COND = 'tobe_item_explict' configFile = os.path.dirname(os.path.realpath(__file__)) + '\\' + 'config.cfg' config = configparser.ConfigParser(interpolation=None) config.read_file(codecs.open(configFile, 'r', 'UTF-8-sig')) getPosModKey = config[SECTION_ORB]['key_possible_mod'] getPosModKeyDetail = config[SECTION_ORB]['key_possible_mod_detail'] blockMouse = config[SECTION_ORB]['key_orb_mouse_block'] print('orb helper started !') mainOrbHelper([getPosModKey, getPosModKeyDetail, blockMouse], configFile)
—gui.py—
import wx import win32api import win32con import win32gui import configparser import os import codecs import threading colVal = (0, 0, 0) stopColor = (247, 245, 231) GUI_SECTION = 'gui' GUI_OPT_FRM_SIZE = 'fram_size' def readConfigFile(configFile, section, opt): config = configparser.ConfigParser(interpolation=None) config.read_file(codecs.open(configFile, 'r', 'UTF-8-sig')) try: result = config[section][opt] except: result = None return result def writeConfigFile(configFile, section, writeStr, opt = 'val'): config = configparser.ConfigParser(interpolation=None) config.read_file(codecs.open(configFile, 'r', 'UTF-8-sig')) result = section in config.sections() if result == False: config.add_section(section) config[section][opt] = writeStr with open(configFile, 'w', encoding='UTF-8-sig') as config_file: config.write(config_file) def setFrmPosition(win, initPos): win.SetPosition(initPos) class InvenPanel(wx.Panel): stisfiedTxt = "조건 만족 !!" def __init__(self, parent): wx.Panel.__init__(self, parent, -1, style=wx.SIMPLE_BORDER) self.SetBackgroundColour(wx.BLACK) self.mFrame = parent self.Bind(wx.EVT_LEFT_DCLICK, self.OnClick) #-------------------------------------------------------- def OnClick(self, e): if e.LeftDClick(): print('Click') def changeBackGround(self): self.SetBackgroundColour(stopColor) self.Refresh() print(self.GetSize()) resultLabel = wx.StaticText(self, -1, self.stisfiedTxt, style=wx.ALIGN_CENTER) font = wx.Font(32, family=wx.DECORATIVE, style = wx.ITALIC, weight=wx.BOLD, underline=False) resultLabel.SetFont(font) resultLabel.SetForegroundColour(wx.Colour(255, 0, 0)) resultLabel.Center() def setInitBackgroundColor(self): self.SetBackgroundColour(wx.BLACK) class AppFrame(wx.Frame): satisfied = False resized = False moving = False pressedKey = '' def __init__(self, gui_q, initSize, configFile): if initSize != None: s = wx.Size(initSize[0], initSize[1]) else: s = wx.DefaultSize self.configFile = configFile wx.Frame.__init__(self, None, title="Inventory", size=s, style=wx.DEFAULT_FRAME_STYLE | wx.STAY_ON_TOP ) self.statusbar = self.CreateStatusBar(1) self.gui_q = gui_q self.makeTransparent() self.pnl = InvenPanel(self) hbox = wx.BoxSizer(wx.HORIZONTAL) hbox.Add(self.pnl, 1, wx.EXPAND | wx.ALL, 5) self.SetSizer(hbox) self.Centre() self.Bind(wx.EVT_CLOSE, self.OnCloseWindow) self.Bind(wx.EVT_MOVE, self.OnMove) self.Bind(wx.EVT_SIZE, self.OnSize) self.Bind(wx.EVT_IDLE, self.OnIdle) self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown) self.Bind(wx.EVT_KEY_UP, self.OnKeyUp) self.qThreadKill = False self.qThread = threading.Thread(target=self.qReceive, args=(self.gui_q, )) self.qThread.daemon = True self.qThread.start() #end AppFrame class #-------------------------------------------------------- def makeTransparent(self): hwnd = self.GetHandle() win32gui.SetWindowLong(hwnd, win32con.GWL_EXSTYLE, win32gui.GetWindowLong(hwnd, win32con.GWL_EXSTYLE) | win32con.WS_EX_LAYERED) # colVal 에 해당하는 색을 가진 영역을 투명하게 변경 후 마우스 입력을 받도록 설정 win32gui.SetLayeredWindowAttributes(hwnd, win32api.RGB(*colVal), 0, win32con.LWA_COLORKEY) def makeTransparentWhole(self): # 전체 프레임을 투명하게 함 hwnd = self.GetHandle() win32gui.SetWindowLong(hwnd, win32con.GWL_EXSTYLE, win32gui.GetWindowLong(hwnd, win32con.GWL_EXSTYLE) | win32con.WS_EX_LAYERED) win32gui.SetLayeredWindowAttributes(hwnd, 0, 60, win32con.LWA_ALPHA) def changePnlColor(self): self.pnl.changeBackGround() def setStatusBarText(self, strs): self.statusbar.SetStatusText(strs) # -------------------------------------------------------- def qReceive(self, msgQ): while not self.qThreadKill: if not msgQ.empty(): msg = msgQ.get() if msg == 'item/match': print('gui item match ') #self.makeTransparent() wx.CallAfter(self.changePnlColor) elif msg == 'item/wait': wx.CallAfter(self.setStatusBarText, 'Wait ....') elif msg == 'item/missmatch': wx.CallAfter(self.setStatusBarText, 'Miss Match') elif msg.find('item/name') >= 0: pickedItem = os.path.basename(msg) stText = pickedItem + 'Picked' wx.CallAfter(self.setStatusBarText, stText) print('gui thread end') #-------------------------------------------------------- def OnMove(self, e): self.moving = True def OnSize(self, e): e.Skip() self.resized = True def OnIdle(self, e): if self.resized: frmSize = tuple(self.GetPosition()) + tuple(self.GetSize()) writeConfigFile(self.configFile, GUI_SECTION, str(frmSize), GUI_OPT_FRM_SIZE) self.resized = False elif self.moving: frmSize = tuple(self.GetPosition()) + tuple(self.GetSize()) writeConfigFile(self.configFile, GUI_SECTION, str(frmSize), GUI_OPT_FRM_SIZE) #print(self.pnl.GetSize()) self.moving = False #-------------------------------------------------------- # 테스트 def OnKeyDown(self, e): self.pressedKey = e.GetKeyCode() print(self.pressedKey) def OnKeyUp(self, e): if self.pressedKey == e.GetKeyCode(): if self.pressedKey == wx.WXK_F1: print('Pressed') self.makeTransparent() self.pnl.changeBackGround() #self.pnl.SetBackgroundColour(wx.BLUE) self.pressedKey = '' # -------------------------------------------------------- def OnCloseWindow(self, evt) : self.pnl.setInitBackgroundColor() self.qThreadKill = True self.Destroy() # -------------------------------------------------------- #----------------------------------------------------- # # class invenProcessor(wx.App): # def OnInit(self): # frame = AppFrame(None) # frame.Show(True) # return True #end AppFrame class #======================================================= if __name__ == '__main__' : # test from ast import literal_eval import wx.lib.inspection import queue testQ = queue.Queue() configFile = os.path.dirname(os.path.realpath(__file__)) + '\\' + 'config.cfg' config = configparser.ConfigParser(interpolation=None) config.read_file(codecs.open(configFile, 'r', 'UTF-8-sig')) try: initPosSize = literal_eval(config[GUI_SECTION][GUI_OPT_FRM_SIZE]) initPos = initPosSize[:2] initSize = initPosSize[2:] except: initSize = None initPos = None app = wx.App(False) frm = AppFrame(testQ, initSize) if initPos != None: setFrmPosition(frm, initPos) frm.Show() #wx.lib.inspection.InspectionTool().Show() app.MainLoop()
드럽게 길군요 ㅜ_ㅜ
여튼 실행은 python orbHeler.py 로 하시면 됩니다.
나중에 정리 되면 git 에 올리던지 해야 겠어요 ㅜㅜ 여튼 코드는 위와 같습니다. 주석은 최대한 달았는데 .. 혹시 이해 안되시는 부분 있으시면 댓글 달아 주세요 ㅎㅎ
네 오늘은 여기 까지입니다. !!
저번주에 개도 안걸린다는 여름 감기 걸려서 죽다 살아 났어요 ㅋㅋ 콧물이 거의 물처럼 흐르더라구요 감기 조심하시구 굿 라이프 온 룸 되세요 ~ ㅎ
블로그 구독하기 !!
파이썬? 파이참 이런걸 잘 모르는 사람이라 설명이 좀 어렵네요 ㅎㅎ;
파이참 깔고 config에서 prefix에 옵션 복사해서 넣고 ㅣ 또 다른거 적고 저장하니까 실행안되고 꺼지네요 ㅜㅜ
그냥 코드로 실행시키시나요 ?? ㅎㅎ혹시 EXE 파일을 원하시면 본문 상단에 파일이 있습니다. ㅎㅎ
키를 누를실때 POE 게임창을 한번 클릭하시고 해보세요 !
1~3티어의 옵션을 전부 다 하나씩만 떠도 알람뜨게하고싶은데 어떻게해야하나요?
티어별 옵션은 지원하지 않습니다. ㅜㅜ 수치들을 전부 #처리 해서 비교하거든요 ㅜ
F5를 눌러서 원하는 옵션이 있을 경우 그 옵션을 그대로 prefix 항목에 넣으시면 됩니다.
아 글쿤요 ㅎㅎ 티어별로 인식할수있다면 최고일테지만 지금도 훌륭하네요 ㅎㅎ 좋은자료감사합니다
ㅎㅎ 감사합니다. 티어 별로는 좀 어려운 점이 아이템에 대고 ctrl+c를 누르면 티어에 상관없이 옵션만나오는데 문제는 서로 다른 티어지만 같은 옵션의 경우 수치가 합쳐져서 나오더라구요 ㅜㅜ 이럴 경우 판단이 불가 하더라구요 ㅜ ㅎㅎ
1. exe파일로 실행하는것과 파이썬으로 실행하는게 다른가요? 어제 exe로 해보니 잘 돌아가서 사실 굳이 사용법만 알면 exe로 해도 같은것 같아서요…
2. 한 접두어에 여러 속성이 붙는건 어떻게 설정할수 있을까요? 예를들어 보석이 박힌 검 같은 경우, ‘황제의’나 ‘ ‘독재자의’같은 옵션은 접두어 하나에 물리피해와 정확도가 둘다 붙는데 .. 정확도는 접미어에 있다고 뜨거든요. 그러면 조건식을 접두어 물리피해 & 접미어 정확도
이렇게 붙여야할까요?
3. 혹시 해당 조건에 맞게 자동으로 돌아가게 하는건 없을까요? 어제 해보니 클릭을 너무 빨리하면 인식전에 넘어가버리는 경우가 생기는것같더라구요. 프로그램에서 알아서 클릭하고 확인하고 넘어가고 원하는 옵션 나오면 멈추게끔 ㅎㅎ
댓글 남겼는데 지워졌네요 ㅠㅠ..길게 썼는데 그냥 간략하게 다시..
1. exe파일로 실행하는것과 파이썬으로 실행하는것 차이가 있나요?
2. 접두어 하나에 여러 옵이 붙는건 어떻게 조건을 줘야할까요? 만약 보석이 박힌 검 같은경우에 ‘독재자의’ 라는 접두어에 물리피해와 정확도가 같이 붙게 되는데 정확도 하나만 보면 접미어에 붙거든요.
그냥 보이는대로 검색하니깐 이런 경우는 접두어 물리피해 & 접미어 정확도로 검색해야하나요?
3. 혹시 자동으로 돌아가게하는건 없나요? 손으로 변화오브 바르다보면 너무 빠르게 클릭할때는 지나쳐버리는 경우들이 생기네요. 적당한 속도로 인식하게 해주는건 없을까요
1. 기능상의 차이는 없지만 성능상 코드를 실행 시키는게 조금 빠른거 같습니다. ㅎㅎ
2. 이 경우는 아래 처럼 하는게 안전할 것 같습니다. 아이템에 ctrl+c 를 했을 때 접두어 접미어가 구별되어 나오지 않습니다. 즉 말씀하신 접두어 접미어가 모두 나올경우 프로그램은 물리피해 and 정확도 밬에 받지 못합니다.
prefix = 물리피해
suffix = 정확도
3. 코드를 수정하면 자동으로 돌아가게 할 수 있겠지만 .. 지나치게 정책 위반이라서요 ..ㅎㅎ 일단 현제는 팝업창 밑에 검증이 완료되기 전에 waiting 이라고 뜨는데 이걸 보면서 바르는 수밬에 없습니다. ㅜ
혹시 쉐이퍼랑 엘더 옵션 추가할 계획없으신가요?
계획중에 있습니다. ㅋㅋㅋ 조만간 올릴게요 ㅎㅎ
혹시 아이템에 커서대놓고 F5
눌럿을시 도스창에 템 정보까지 나오고 No item in DB 라고 뜨는데
왜이런지 아시나요? DB파일은 위에꺼 그대로 가져온거고…혹시 신규템 DB라 안들어갓나 해서
다른템 해봐도 모든템이 노 아이템 디비로 표기되네여
안녕하세요 ㅜㅜ 업데이트 되면서 ctrl+c 누를시 희귀도 Key가 아이템 희귀도로 변경되었어요 ㅜㅜ 다시 올려놨으니 다시 해보시기 바랄게요 ㅜ
댓글이 삭제되었네요
노 아이템 db라고 뜨면서 db를 못불러오는데 뭐가문제일까요?
파일은 다있는상태입니다.
안녕하세요 ㅜㅜ 업데이트 되면서 ctrl+c 누를시 희귀도 Key가 아이템 희귀도로 변경되었어요 ㅜㅜ 다시 올려놨으니 다시 해보시기 바랄게요 ㅜ
갑자기 오류가 생기네요. 아이템 f5로 누르면 cmd창에 NO item in DB라고 뜹니다
안녕하세요 ㅜㅜ 업데이트 되면서 ctrl+c 누를시 희귀도 Key가 아이템 희귀도로 변경되었어요 ㅜㅜ 다시 올려놨으니 다시 해보시기 바랄게요 ㅜ
쉐이퍼 아이템은 옵션에 상관없이 explict에 “쉐이퍼 아이템”이라고만 나오네요. ㅠㅠ 쉐이퍼도추가부탁드립니다
넵 앞으로 작업하려구요 ㅎㅎㅎ
막누르고 싶은데 인식속도 빠르게 해주실순 업나요? ㅠㅠ
ㅠㅠ 일ㄷㄴ현제로썬 불가능합니다 다른 방법을 강구해 보고 있어요 ㅠㅠ
어이쿠 아닙니다 정말 고생이 만으십니다 생기는것도 없는데 이런것 만들어주시니 너무 감사할따름입니다.
아참 옷기 쉐이퍼 반지에 12레벨 암살징표 라던가 활에 포악한 투사체 같은 옵도 넣어주실수 있는지요..
염치없지만 크래프팅이 낙이라;
넵 지금 쉐이퍼 엘더 옵션도 지원하도록 코드 만들고 있습니다. ㅎㅎ 조만간 올리도록 할게요
F6누르면 디테일하게 수치까지 나오는데 이걸 적용시켜서 1등급 옵션을 띄우고 싶은데 어떻게 해야 하나요…??
지금은 1등급 옵션으로 조건 식을 만드는건 안됩니다. f6은 그냥 디테일한 옵션을 보고싶을 때 용도로만 사용해 주세요 ㅜㅜ
여러가지 옵션을 넣는 방법은 없을까요 예를들면 프리픽스에서 옵션 둘중 하나만 떠도 만족하게
여러가지 옵션을 넣는 방법은 없을까요 프리픽스에서 옵션 두가지를 넣어서 둘중 하나만 만족해도 뜰수있게
유튜브에 답변 드렸습니다. ㅎㅎ
안녕하세요 너무너무 사용하고싶은 기능인데
올려주신 exe 로 실행해서 누르면 not found / no item in db 가로 뜨는건 왜그럴까요? ㅠㅠ
혹시 어떤 아이템으로 하셨나요 ?? 지원 안되는 아이템 종류도 있습니다. ㅜㅜ
안녕하세요
혹시 이번 3.9 버전도 지원 가능하신지 알 수 있을까요?
전쟁군주 베이스에 원하는 옵션이 있어서요..
또, 매직 아이템을 크래프팅할때 접두나 접미 둘중 하나만 일치해도 성공 메세지가 나오게 할 수 있는 방법은 없을까요?
좀 보기쉽게 말씀드리자면 지금의 유효성 검사는
접두1 | 접두2 | 접두3 & 접미1 | 접미2 | 접미3 이런 방식을
접두1 | 접두2 | 접두3 | 접미1 | 접미2 | 접미3 이렇게 될 수 있는지요?
아마 구글 드라이브에 올라가 있는 거는 되는 걸로 알고 있습니다. ㅎㅎ
네 말씀 감사합니다.
사실 실행파일을 다운받아 사용해 봤는데 작동이 되질 않아서요 ㅜㅜ
환경은 윈도우10이며 카스퍼스키 백신을 사용하고 있습니다.
해상도는 2560×1440 이구요.
프로그램창에서 f5나 f7을 눌렀을경우 반응이 없다고 여러차례 시도하면
어떤 메세지가 순간적으로 보이다가 창이 닫혀버리네요.
지금 그게 어떤 내용인지 확인하고 싶어서 파이썬 공부를 시작했습니다 ㅎㅎ
f5눌러서 나온 옵션을 복사하여 prefix에 붙여넣기 하여 OK 버튼 누른후
나오는 GUI 창을 유튜브처럼 인벤토리에 맞추고
변화의 오브로 돌렸습니다 혹시 몰라서 가장 잘뜨는 생명력 최대치로 갑옷에 테스트 해봤는데
생명력 최대치가 나왔음에도 불구하고 멈추는 GUI 창이뜨질않고.. Miss match 라고 나오네요..
혹시 뭐를 확인해바야 하나요 ㅠ
뭐지 댓글이 안달리는건가..
테스트 해보고 있는데 원하는 옵션이 나왔는데도 GUI 창에서 마우스 클릭을 막는 창이 뜨질 않습니다..
GUI창 밑에 메시지 에는 Miss Match 라고 나오고..
orbHelper 에서는 Condition Not Matched 라고 나오네요….
현재는 사용이 안되는 건가요??
위에 테스트 해본 아이템은 십자군 베이스 판금 갑옷 입니다.