def newTemplate( self, name, imgPath, default_threshold=0.98, # 默认识别度 onload=False, # 是否直接加载 showlog=False): # 是否显示成功添加 if name[0] not in string.ascii_letters: logger.error(f'名字首位必须为字母字符:{name}') return None if not os.path.isfile(imgPath): logger.error("图片不存在:" + imgPath) return None if name not in self.__namelist__: temp = None if onload: temp = cv2.imread(imgPath) if isinstance(temp, type(None)) and onload: logger.error("未找到%s的图片[位于\'%s\']" % (name, imgPath)) return None setattr(self, name, [temp, imgPath, default_threshold, onload]) self.addName(name) if showlog: logger.debug("成功添加定位标志%s" % name) else: try: setattr( self, name, [cv2.imread(imgPath), imgPath, default_threshold, True]) logger.warning("已覆盖名为%s的定位标志" % name) except: logger.warning('在覆盖标志时出现错误') return None
def _writeStageDrop_Ver2(self, stageId, dropList, cost_time: float = 0, raw_finish_time: int = 0): """ :param stageId: type str for read file {stageId}.drp :param dropList: recognizeEndItemsWithTag() return's :param cost_time: cost time of attack :param raw_finish_time:原始完成时间 :return: None :writeStructure: ([version \x02][len(droplist)<1>][[itemid<2>][quantity<2>][type<1>]]..[atk_time<3>][time<4>]) """ sv = [] for i in dropList: _q = i['quantity'] if _q > 0: sv.append([self.get(i['name']), _q, i['type']]) if len(sv) == 0: logger.warning(f"EMPTY DropList as Stage<{stageId}>") return with open(f"{DROPFOLDER_PATH}{stageId}.drp", 'ab') as fd: fd.write(b'\x02') fd.write(len(sv).to_bytes(1, 'little')) for i, q, t in sv: fd.write(i.to_bytes(2, 'little')) fd.write(q.to_bytes(2, 'little')) fd.write((t + 1).to_bytes(1, 'little')) try: fd.write(int(cost_time * 100).to_bytes(3, 'little')) except OverflowError: logger.warning(f"cannot convert cost_time({cost_time}) into byte!") fd.write(b'\xff\xff\xff') fd.write(int(raw_finish_time or time.time()).to_bytes(4, 'little'))
def recognizeOperation(image, map_dict=None) -> Tuple[str, str]: if not map_dict: map_dict = getMapCost() opidtext = fix_known_data(load_data()[1].recognize2( image, subset='0123456789abcdefghijklmnopqrstuvwxyz-')[0]) where_ = map_dict.where(opidtext) if where_: return opidtext, where_ # 无法识别,尝试使用tesseract logger.warning("无法识别地图信息" + opidtext) debug_ret = [] import pytesseract for index in range(10): ret = pytesseract.image_to_string(image, lang="eng") \ .replace("\n", "").replace("\x0c", "") where_ = map_dict.where(ret) debug_ret.append(ret) if where_: logger.debug("识别信息:%s" % str(debug_ret)) return ret, where_ logger.error("在10次识别后依旧无法识别出当前关卡信息。") logger.debug("识别信息:%s" % str(debug_ret)) return "", ""
def createMap(self, name): if self.config.get(name) is None: self.config.update({name: []}) return True else: logger.warning("已存在名为%s的数据" % name) return False
def readFile(self, filename): if not os.path.exists(filename): logger.warning("%s不存在,自动创建!" % filename) fn = '.\\' + filename f = open(fn, 'w') f.write('') f.close() self._filename = filename self.data = open(filename, 'rb') self._fileopen = True
def recognizeEndItems(ark, stageCode, quantity=True): drop_list = getDropList(stageCode) for i in getActivityOpen()[1]: drop_list.add((i, getItemTag()['items'][i].get('name', "未知物品:" + i))) # drop_list = list(drop_list) # return drop_list screen = ark.getScreenShot(470, 520, 800, 160) drop_reco = [] for item, name in drop_list: try: rep, where = ark.gameTemp.matchDifference( screen, cv2.resize( ark.gameTemp.getTemplate("lootitem\\" + item + ".png")[0], itemReco.STAGE_DROP)[29:29 + 46, 34:34 + 68], method=1) except cv2.error as cverr: logger.warning( f"处理掉落物'{name}'失败:(code={cverr.code}){cverr.err} in function '{cverr.func}'" ) continue y, x, num = -1, -1, -1 if rep >= 0.9: y, x = int(where[0]), int(where[1]) x += 26 y += 63 if quantity: imgraw = Image.fromarray(screen[y:y + 27, x:x + 53]) numimg = imgops.crop_blackedge2(imgraw, 120) if numimg is None or numimg.size < (5, 5): numimg = imgops.clear_background(imgraw, threshold=180) # cv2.imshow("d",np.asarray(numimg)); cv2.waitKey(0) img = imgops.clear_background(numimg, threshold=120) # cv2.imshow("i", np.asarray(img)); cv2.waitKey(0) reco = ark.reconizerN.recognize2(img, subset="0123456789万") try: num = int(reco[0]) if reco != '' else 0 except: num = 0 else: num = -1 err = rep > 0.9 and num <= 0 if err: logger.warning(f"识别到物品\"{name}\"数量出现异常,已置零,该物品将不计入统计中") # drop_reco.append([name, rep > 0.9, rep, [x, y], num]) drop_reco.append({ "name": name, "have": rep > 0.9, "_acq": rep, "_pos": (x, y), "quantity": num, "error": err }) return drop_reco
def update_droprate_processing(self, stage, item, droprate, mode='add'): if stage not in self.stage_name_rv['zh']: logger.warning(f'stage {stage} not found') return if item not in self.item_name_rv: logger.warning(f'item {item} not found') return stageid = self.stage_name_rv['zh'][stage] itemid = self.item_name_rv[item] if mode == 'add': self.probs_matrix[stageid][itemid] += droprate elif mode == 'update': self.probs_matrix[stageid][itemid] = droprate
def addStage(self, stageName, times, require=None, append=False): if stageName in self['list']: if append is self: logger.warning( f"已存在名为{stageName}的关卡在攻击列表中,若需要添加请使用append=True更新攻击次数") return self else: self['stages'][self['list'].index( stageName)]["max_atk"] += times return self self["list"].append(stageName) self['stages'].append(Stage(stageName, times, require)) return self
def toMain(self): if self.__ark.getLocation(self.__ark.getScreenShot(), 'main'): return True if self.__ark.gameTemp.dingwei( "main\\main_ref.png", self.__ark.getScreenShot(199, 11, 141, 53)): logger.warning('未知识别位置!强制返回主界面并从主界面进行操作,可能会导致异常产生!') self.__ark.clicker.mouse_click(269, 39, t=0.7) self.__ark.clicker.mouse_click(93, 172, t=2) # TODO:检查事件 self.__ark.check_to_main_notice() return True return False
def fromPlanner(planned_data, require_request=False): if not isinstance(planned_data, dict): logger.error( f"规划数据并非dict形式,请检查(获得了{planned_data.__class__.__name__})") return StageSet() stages = planned_data.get("stages", []) if not stages: logger.warning("规划数据关卡列表为空!") return StageSet() ret = StageSet() for stage in stages: ret.addStage(stage['stage'], int(float(stage['count']) + 0.9), stage['items'], require_request) return ret
def wrapper(*args, **kwargs): if isinstance(arg, str): if update or not RootData.has(arg): ret = func(*args, **kwargs) cache_time = 0 if max_cache_time < 0 else time.time( ) + max_cache_time RootData.set(arg, ret, cache_time) return ret else: return RootData.get(arg) else: logger.warning("cache can't store name for type of %s" % arg.__class__.__name__) return func(*args, **kwargs)
def addMap(self, name, condition=None): if '.' in name: name_bef, name_after = name.split('.', 1) if not self.mapping.get('map').has(name_bef): logger.warning(name + " doesn't in the MapList") return False self.mapping.get('map').get(name_bef).get('addon').addAddon(name_after) else: if self.mapping.get("map").has(name): logger.warning(name + " has already in the MapList") return False self.mapping.get("map").update(self._default_map_template(name, condition=condition)) self.mapping.get("reflector").push(name, name, create=True) return True
def addItemFromRawDict(self, dumpdict: dict): if dumpdict.get('type') in ['FURN', 'CHAR', 'NONE', 'ET_STAGE']: logger.warning( f"cannot add type:{dumpdict.get('type')} into ItemStack") return self try: return self.addItem( getItemTag()['items'].get(dumpdict.get('id'))['name'], int(dumpdict.get('count'))) except: logger.warning( f"cannot add [T:{dumpdict.get('type')} I:{dumpdict.get('id')}] into ItemStack" ) return self
def moveStageInMap(self, mapName, stage1, stage2, error=True): # 承接changestageinmap,在识别到两个stage基础上进行stage内移动 rect = self.getMapStageInfo(mapName) rect_stage = [os.path.basename(i)[:-4] for i, v in rect] assert rect != [] if error: stage1 = rect_stage[0] else: assert stage1 in rect_stage x, y = rect[rect_stage.index(stage1)][1] # print(rect, stage1, stage2, rect_stage) dx, dy = self.getDistanceBetweenStage(mapName, stage1, stage2) if 0 < dx + x < 1240 and 50 < dy + y < 618: # print(x, dx, y, dy) self.__ark.clicker.mouse_click(x + dx + 35, y + dy + 13, t=1) return True while abs(dx) > 669: self.__ark.clicker.dragRel(1172, 35, ((-1 if dx > 0 else 1) * 400), 0) self.__ark.clicker.mouse_click(510, 35, t=0.2) dx += (-1 if dx > 0 else 1) * 660 while abs(dx) > 172: self.__ark.clicker.dragRel(1172, 35, ((-1 if dx > 0 else 1) * 100), 0) self.__ark.clicker.mouse_click(510, 35, t=0.2) dx += (-1 if dx > 0 else 1) * 172 while abs(dx) > 70: self.__ark.clicker.dragRel(1172, 35, ((-1 if dx > 0 else 1) * 50), 0) self.__ark.clicker.mouse_click(510, 35, t=0.2) dx += (-1 if dx > 0 else 1) * 70 # print(x + dx, y + dy) reco = self.getMapStageInfo(mapName) reco_li = [os.path.basename(i)[:-4] for i, v in reco] if stage2 in reco_li: cur_x, cur_y = reco[reco_li.index(stage2)][1] self.__ark.clicker.mouse_click(cur_x + 35, cur_y + 13, t=1) return True else: logger.warning(f"{stage2} not found in current map") self.moveStageInMap(mapName, stage1, stage2)
def _writeStageDrop_Ver1(self, stageId: str, dropList: dict, cost_time: float = 0): """ :param stageId: type str for read file {stageId}.drp :param dropList: recognizeEndItemsWithTag() return's :return: None :writeStructure: ([version \x01][len(droplist)<1>][[itemid<2>][quantity<2>]]..[time<4>]) """ sv = [] for i in dropList: if i['have']: _q = i['quantity'] if _q > 0: sv.append([self.get(i['name']), _q]) if len(sv) == 0: logger.warning(f"EMPTY DropList as Stage<{stageId}>") return with open(f"{DROPFOLDER_PATH}{stageId}.drp", 'ab') as fd: fd.write(b'\x01') fd.write(len(sv).to_bytes(1, 'little')) for i, q in sv: fd.write(i.to_bytes(2, 'little')) fd.write(q.to_bytes(2, 'little')) fd.write(int(time.time()).to_bytes(4, 'little'))
def UpgradeDropFromVer1_ToVer2(ark, stage, force=False): drp = getStageTag().getStage(stage).getDropInfo().getDropList() li = list(i[1] for i in drp) se = set(i[1] for i in drp) if len(li) != len(se): logger.warning('Unable to upgrade the level with different drop overlaps from Ver.1 to Ver.2!') if not force: return False drop_info = ark.dropLinker.getStageDrop(stage) drp_wname = {} for drop_type, game_id in drp: drop_name = getItemTag()['items'].get(game_id)['name'] drop_id = ark.dropLinker.get(drop_name) drp_wname.update({drop_name: [drop_name, drop_id, drop_type]}) for index in range(len(drop_info)): cur_drop: DropInfo = drop_info[index] if cur_drop.getVersion() != 1: continue if cur_drop.isEmpty(): logger.warning(f"Find Empty Attack at Index:{index} in Stage:{stage},Set to None.") drop_info[index] = None continue new_drop = DropItemStack() ERR_G = [None, None, None] for type_, drp_li in cur_drop.dropList.items(): for drp_name, drop_q in drp_li: new_drop.addItem(drp_name, drop_q, drp_wname.get(drp_name, ERR_G)[2] or 253) drop_info[index] = DropInfo(2, stage, cur_drop.attackTime, new_drop) # Write file Structure:([version \x02][len(droplist)<1>][[itemid<2>][quantity<2>][type<1>]]..[atk_time<3>][time<4>]) # save raw file to backup os.rename(f"{DROPFOLDER_PATH}{stage}.drp", f"{DROPFOLDER_PATH}{stage}.drp.U12.{randomstr()}") ttl_upg = 0 with open(f"{DROPFOLDER_PATH}{stage}.drp", 'wb') as fd: for dropstack in drop_info: if dropstack is None: continue fd.write(b'\x02') all_items = dropstack.dropList.getAllItems() fd.write(len(all_items).to_bytes(1, 'little')) for name, quantity, type_ in all_items: fd.write(ark.dropLinker.get(name).to_bytes(2, 'little')) fd.write(quantity.to_bytes(2, 'little')) fd.write((type_ + 1).to_bytes(1, 'little')) cost_time = dropstack.atk_in_t or 0 try: fd.write(int(cost_time * 100).to_bytes(3, 'little')) except OverflowError: logger.warning(f"cannot convert cost_time({cost_time}) into byte!") fd.write(b'\xff\xff\xff') fd.write(dropstack.attackTime.to_bytes(4, 'little')) ttl_upg += 1 logger.notice(f"Upgrade Finished![STAGE {stage},{ttl_upg}TUI]") return drop_info
def updateItemData(): import requests from bs4 import BeautifulSoup as soup k = requests.get("http://prts.wiki/w/%E9%81%93%E5%85%B7%E4%B8%80%E8%A7%88") r = soup(k.content, "html.parser") item_dump = [] for data in list(r.find_all(class_="smwdata")): item_dump.append([data.get("data-name"), data.get("data-rarity"), data.get("data-file"), data.get("data-id")]) with open(".\\ArknightsGameData\\zh_CN\\gamedata\\excel\\item_table.json", "r", encoding="utf-8") as f: item = json.load(f) item_from_json = item["items"] item_names = {item_from_json.get(i)["name"]: item_from_json.get(i)["iconId"] for i in item_from_json.keys()} # for name, _, __, ___ in item_dump: # try: # item_names.remove(name) # except ValueError: # print(name) # print(item_names) headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36 QIHU 360SE' } patch_item_name_data = { "合约赏金(旧)": "CRISIS_SHOP_COIN", "晶体电子单元": "MTL_SL_OEU", "寻访数据契约(遗愿焰火)": "LMTGS_COIN_601", "寻访数据契约(勿忘我)": "LMTGS_COIN_903", "寻访数据契约(地生五金)": "LMTGS_COIN_1401", "寻访数据契约(月隐晦明)": "LMTGS_COIN_1601", "α类新年寻访凭证(2020)": "2020recruitment10_1", "β类新年寻访凭证(2020)": "2020recruitment10_2", "γ类新年寻访凭证(2020)": "2020recruitment10_3", "α类新年寻访凭证(2021)": "2021recruitment10_1", "β类新年寻访凭证(2021)": "2021recruitment10_2", "γ类新年寻访凭证(2021)": "2021recruitment10_3", } _ = os.path _PARENT = _.realpath(_.join(_.dirname(__file__), '..')) unknown_item = [] err_k = [] for name, rarity, url, sortid in item_dump: # http://prts.wiki/images/thumb/2/23/%E9%81%93%E5%85%B7_%E5%B8%A6%E6%A1%86_%E6%97%A0%E5%90%8D%E7%9A%84%E8%AF%86%E5%88%AB%E7%89%8C.png/100px-%E9%81%93%E5%85%B7_%E5%B8%A6%E6%A1%86_%E6%97%A0%E5%90%8D%E7%9A%84%E8%AF%86%E5%88%AB%E7%89%8C.png # http://prts.wiki/images/2/23/%E9%81%93%E5%85%B7_%E5%B8%A6%E6%A1%86_%E6%97%A0%E5%90%8D%E7%9A%84%E8%AF%86%E5%88%AB%E7%89%8C.png # if image in current path,break try: id_ = item_names.get(name) if id_ is None: id_ = patch_item_name_data.get(name) if id_ is None: id_ = name unknown_item.append(id_) logger.warning("[ItemIconUpdater]无效的材料转换名称:" + name) if os.path.exists(f"{_PARENT}\\imgReco\\img\\lootitem\\{id_}.png"): continue url_new = url.replace("/thumb", "")[::-1].split("/", 1)[1][::-1] rep = requests.get(url_new, headers=headers) if rep.status_code == 200: logger.notice(f"[ItemIconUpdater]完成图标更新: {id_}") with open(f"{_PARENT}\\imgReco\\img\\lootitem\\{id_}.png", "wb") as f: f.write(rep.content) except: logger.error("出现错误:" + str([name, rarity, url, sortid])) err_k.append([name, rarity, url, sortid]) print(unknown_item) print(err_k)
def recognizeEndItemsWithTag(ark, stageCode_or_dropList, quantity=True): def removeClosePos(gps: list): if len(gps) <= 1: return gps gps.sort() final = [gps[0]] prev_x = gps[0][0] for _x, _y in gps: if -7 < prev_x - _x < 7: continue prev_x = _x final.append(( _x, _y, )) return final if isinstance(stageCode_or_dropList, str): drop_list = getDropList(stageCode_or_dropList) else: drop_list = stageCode_or_dropList for i in getActivityOpen()[1]: drop_list.add((i, getItemTag()['items'][i].get('name', "未知物品:" + i))) screen = ark.getScreenShot(470, 520, 800, 160) drop_reco = [] for item, name in drop_list: dumped = False try: gps = ark.gameTemp.matchDifference( screen, cv2.resize( ark.gameTemp.getTemplate("lootitem\\" + item + ".png")[0], itemReco.STAGE_DROP)[29:29 + 46, 34:34 + 68], threshold=0.9) except cv2.error as cverr: try: gps = ark.gameTemp.matchDifference( screen, ark.gameTemp.getTemplate("lootitem\\dumped\\" + item + ".png")[0][29:29 + 46, 34:34 + 68], threshold=0.9) dumped = True except (cv2.error, TypeError): logger.warning( f"处理掉落物'{name}<{item}>'失败:(code={cverr.code}){cverr.err} in function '{cverr.func}'" ) continue gps = removeClosePos(gps) for x, y in gps: if dumped: y = 28 # 检测所有位置的掉落信息,分别根据不同位置判断不同掉落属性dropType num = -1 x += 26 y += 63 if quantity: imgraw = Image.fromarray(screen[y:y + 27, x:x + 53]) # cv2.imshow('r', np.array(imgraw)) # cv2.waitKey(0) numimg = imgops.crop_blackedge2(imgraw, 150) # cv2.imshow('n', np.array(numimg)) # cv2.waitKey(0) if numimg is None or numimg.size < (5, 5): numimg = imgops.clear_background(imgraw, threshold=180) img = imgops.clear_background(numimg, threshold=120) # cv2.imshow('g', np.array(img)) # cv2.waitKey(0) reco = ark.reconizerN.recognize2(img, subset="0123456789万") # print(reco) try: num = int(reco[0]) if reco != '' else 0 except: num = 0 else: num = -1 err = num <= 0 if err: logger.warning(f"识别到物品\"{name}\"数量出现异常({num}),已置零,该物品将不计入统计中") else: drop_reco.append({ "name": name, "have": True, "_acq": 0.9, "type": recognizeEndItemDropType(screen, x), "_pos": (x, y), "quantity": num, "error": err }) return drop_reco
def checkMapExist(self, name): if not self.hasMap(name.split('.', 1)[0]): logger.warning("不存在名为" + name.split('.', 1)[0] + "的Map") return False return True
def readConfig(self, config=None, keepseek=False, inner=False, debug=False): # logger.system('<+>读取配置文件中...',flush=True) # TODO:处理空文件时异常:mrfzdata.mr ->list型数据 # F = Config(cfgdata=json.load(open('.\MRFZdata\ArknightsGameData\excel\mission_table.json','rb')),Magic='mrfzd',file='mrfzdata2.mr') # D = Config(Magic='mrfzd',file='mrfzdata.mr') # R = Config(Magic='test',file='testcfg.t') # seek在mission->guide_1->unlockParam 166 if config == None: config = self.cfg if self._fileopen: hand = self.data.tell() else: if hasattr(self, '_filename'): self.readFile(self._filename) hand = 0 else: logger.error('<!>无可读取内容!配置不存在') return False if self._cfgread: self.cfg = configTree() config = self.cfg if self.checkHeader(): self.version = struct.unpack('>f', self.readData(4))[0] if round(self.version, 1) < RootData.get("Config_version") and not inner: logger.error("<!>配置文件版本(%.1f)高于程序版本!无法读取" % self.version) return False elif round(self.version, 1) > RootData.get("Config_version") and not inner: logger.warning("<!>配置文件版本(%.1f)低于程序版本!保存时将使用当前版本存储配置!" % self.version) if keepseek: self.data.seek(hand) _ts = time.time() if not inner: if self.readfromType(bool): logger.error("<!>配置是空的!") return False if debug is True: logger.debug('<+>-----------ReadConfig-S-----------') while True: try: name_len = struct.unpack('>h', self.readData(2))[0] # 命名长度 name = self.readData(name_len).decode() type_ = self.type(self.readData(1)) if not type_ == configTree: # list? ---->item value = self.readfromType(type_) config.set(name, value) if debug is True: logger.debug('<+>[%s]%s.%s(%s): %s' % (self.data.tell(), config._root, name, name_len, str(type_)[8:-2:])) if self.readfromType(bool): if debug is True: logger.debug('<+>[%s][t1]end,exit' % (self.data.tell())) break else: # folder config.addChild(name) # -> # empty folder ? if self.readfromType(bool): if debug is True: logger.debug('<+>[%s][t2]end,exit' % (self.data.tell())) break # <- self.readConfig(getattr(config, name), keepseek=True, inner=True, debug=debug) if self.readfromType(bool): if debug is True: logger.debug('<+>[%s][t3]end,exit' % (self.data.tell())) break else: self.data.seek(self.data.tell() - 1) except: logger.error('<!>读取时出现错误 [seek:%s]' % self.data.tell()) # logger.system('<r>读取配置文件完成![用时%f秒]' % _ts) return False _ts = time.time() - _ts if not inner: self._cfgread = True if debug is True: logger.debug('<+>[%s]-----------ReadConfig-E-----------' % self.data.tell()) logger.system('<+>读取配置文件完成![用时%.2f毫秒]' % (_ts * 1000)) return True else: logger.error('<!>Magic Code不匹配!') return False