import json import os import re import xml.etree.ElementTree as ET import random import copy #资源根目录 resRootDir = "/Users/red/Documents/codings/ScrewGame1/Resources" # 关卡配置文件目录 configFileDirPath = resRootDir+"/res_ScrewGame/关卡/" # 关卡模板文件目录 templateFileDirPath = resRootDir+"/res_ScrewGame/关卡/关卡模板/" # 盘子文件目录 plateConfigFileDirPath = resRootDir+"/res_ScrewGame/ccb/盘子上位置情况/" # 关卡JSON文件夹目录 levelConfigJSONDirPath = resRootDir+"/res_ScrewGame/config/" # 关卡配置文件最终目录 finalLevelConfigPath = resRootDir+"/res_ScrewGame/config/最终关卡配置/" # 模拟接入C++关卡生成的钉子种类数 wf?? nScrewTypes = 3 """ 显示一下关卡配置文件路径 要修改的话改一下 """ def askAndSetNewConfigFileDirPath(): global configFileDirPath tipStr = "当前默认的关卡配置文件夹路径为: {}, 设置新的关卡配置文件夹路径? [y/n]".format(configFileDirPath) needNewConfigDirPath = input(tipStr) # 重新设置关卡配置文件夹路径 if needNewConfigDirPath == 'y' or not configFileDirPath: configFileDirPath = input("输入新的关卡配置文件夹路径(路径结尾要带斜杠/):") setNewConfigDirPath() tipStr = "当前默认的关卡模板文件夹路径为: {}, 设置新的关卡模板配置文件夹路径? [y/n]".format(templateFileDirPath) # TODO: 设置新的关卡模板配置文件路径 # TODO: 询问是否设置新的盘子文件目录 # TODO: 设置新的盘子文件目录 # TODO: 询问是否修改关卡JSON文件目录 # TODO: 修改关卡JSON文件目录 """修改本文件中的configFileDirPath, 下一次使用的时候默认使用这个新路径""" def setNewConfigDirPath() -> None: with open(__file__, "r") as curFile: content = curFile.read() pattern = r"configFileDirPath = \"\s\"" replacement = r"configFileDirPath = \"{}\"".format(configFileDirPath) newContent = re.sub(pattern, replacement, content, count=1) with open(__file__, "w") as curFile: curFile.write(newContent) """ 测试 将一个关卡配置文件转化为 JSON """ def testToConvert1(): print("nothing to do") # by wf def testToConvert11(): levelRedName="关卡11.red" configFilePath = configFileDirPath + levelRedName with open(configFilePath) as configFile: configFileTree = ET.parse(configFile) fileRoot = configFileTree.getroot() # print(fileRoot.keys()) dataPyDict = xmlToDict(fileRoot[0]) # print(dataPyDict) #print(dataPyDict) templateIDList = getTemplateIDFromDict(dataPyDict) #print(templateIDList) templateDatas = getTemplatesPyDictForm(templateIDList) #print(templateDatas) # ok plateIDs = getPlateIDsFromTemplateFilePyDictForm(templateDatas) print(plateIDs) # ok plateConfigsPyDictForm = getPlateConfigPyDictForms(plateIDs) #print(plateConfigsPyDictForm) levelConfigs = generateLevelConfigWithPlateConfig(plateConfigsPyDictForm) # print(levelConfigs) saveLevelConfigToJsonFile(levelConfigs, levelRedName) """ 从转化为dict的关卡配置中获取使用的关卡模板号 """ def getTemplateIDFromDict(dataPyDict: dict) -> list: templateList = dataPyDict['nodeGraph']['children'][0]['children'] # print(templateList) templateIDList = [] for template in templateList: print("debug " + template['displayName']) templateIDList.append( template['displayName'] ) return templateIDList """ 从传入的关卡模板号对应的关卡模板配置文件中获得py-dict格式的模板数据 """ def getTemplatesPyDictForm(templateIDList) -> list: ret = [] # 这里的ID是关卡模板ID for ID in templateIDList: templateFileName =templateFileDirPath+ID+".red" # "{}模板{}.red".format(templateFileDirPath, ID) # print(templateFileName) with open(templateFileName, "r") as templateFile: templateFileRoot = ET.parse(templateFile).getroot() templateFilePyDictForm = xmlToDict(templateFileRoot[0]) # print(templateFilePyDictForm) ret.append(templateFilePyDictForm) return ret """ 从转化为py-dict的templateFile中获取使用到的plate的ID的list """ def getPlateIDsFromTemplateFilePyDictForm(templateFilePyDictForms: list) -> list: ret = [] for templateFilePyDictForm in templateFilePyDictForms: for plates in templateFilePyDictForm["nodeGraph"]["children"][0]["children"]: ret.append(plates["displayName"]) return ret """ 根据盘子ID获得py-dict格式的盘子配置 """ def getPlateConfigPyDictForms(plateIDs: list) -> list: ret = [] for plateID in plateIDs: plateConfigFileName = plateConfigFileDirPath + plateID+".red" # print(plateConfigFileName) with open(plateConfigFileName, "r") as plateConfigFile: plateConfigPyDictForm = xmlToDict(ET.parse(plateConfigFile).getroot()[0]) plateConfigPyDictForm["plateID"] = plateID # added # print(plateConfigPyDictForm) ret.append(plateConfigPyDictForm) return ret """ 需要获得每个 plate 的 position, scale, rotate, plateId, zorder """ def generateLevelConfigWithPlateConfig(plateConfigPyDictForms: list) -> list: plateDatas = [] for plate in plateConfigPyDictForms: properties = plate["nodeGraph"]["children"][0]["properties"] # print(propertiesDict) plateConfig = {} for property in properties: propertyName = property["name"] if propertyName == "position": position = property["value"][:2] position = [round(float(n), 1) for n in position] plateConfig["position"] = position elif propertyName == "scale": scale = round(float(property["value"][0]), 1) plateConfig["scale"] = scale elif propertyName == "rotation": rotate = round(float(property["value"]), 1) plateConfig["rotate"] = rotate if "position" not in plateConfig: # 这个默认的位置可能不对 随便设置的 plateConfig["position"] = [500.0, 500.0] if "scale" not in plateConfig: plateConfig["scale"] = 1.000 if "rotate" not in plateConfig: plateConfig["rotate"] = 0.000 plateName = plate["nodeGraph"]["children"][0]["displayName"] pattern = r"\d*$" plateID = re.findall(pattern, plateName)[0] plateConfig["plateId"] = 0 # deprecated int(plateID) print("debug plate name " + plate["plateID"]) plateConfig["plateIds"] = plate["plateID"] # 新加字符串ID plateConfig["zorder"] = 1 plateConfig["holes"] = getHolePositionsFromPlateConfig(plate) # print(plateConfig) plateDatas.append(plateConfig) return plateDatas # 获取钉子的位置 def getHolePositionsFromPlateConfig(plateConfigPyDictForms: list) -> list: ret = [] plateConditions = plateConfigPyDictForms["nodeGraph"]["children"][0]["children"] # 对应plate的每种情况 比如 "一个钉子" "五个钉子" for condition in plateConditions: holeConfigs = {} holes = condition["children"] holePositions = [] for hole in holes: holeProperties = hole["properties"] for property in holeProperties: if property["name"] == "position": holePositions.append([round(float(property["value"][0]), 1), round(float(property["value"][1]), 1)]) break holeConfigs["position"] = holePositions ret.append(holeConfigs) return ret """ 将关卡配置保存到JSON中 """ def saveLevelConfigToJsonFile(levelConfigs: list, fileName: str) -> None: # print("saving json files: ", fileName) fileName = fileName.replace(".red", ".json") with open(levelConfigJSONDirPath + fileName, "w") as levelConfigFile: finalData = {"plateData": levelConfigs} json.dump(finalData, levelConfigFile, ensure_ascii=False) """ 将.red文件的内容转化为 python-dict 格式 """ def xmlToDict(fileRoot: ET.Element) -> dict | list | str: rootTag = fileRoot.tag # print(rootTag) resList = [] resDict = {} if rootTag == 'dict': idx = 0 while idx < len(fileRoot): if fileRoot[idx].tag == 'key': nextTag = fileRoot[idx + 1].tag if nextTag == 'true': resDict[fileRoot[idx].text] = True elif nextTag == 'false': resDict[fileRoot[idx].text] = False else: resDict[fileRoot[idx].text] = xmlToDict(fileRoot[idx + 1]) idx += 1 idx += 1 return resDict elif rootTag == 'array': for child in fileRoot: resList.append(xmlToDict(child)) return resList else: return fileRoot.text ''' 测试 将关卡的redream转化成json ''' def testToConvertAll() -> None: # configFilePath = configFileDirPath + "关卡1test.red" allLevelConfigs = [] for file in os.listdir(configFileDirPath): # print(file) if file != ".DS_Store": absolutePath = configFileDirPath + file if not os.path.isdir(absolutePath): # print(absolutePath) with open(configFileDirPath + file) as configFile: configFileTree = ET.parse(configFile) fileRoot = configFileTree.getroot() # print(fileRoot.keys()) dataPyDict = xmlToDict(fileRoot[0]) # print(dataPyDict) templateIDList = getTemplateIDFromDict(dataPyDict) # print(templateIDList) templateDatas = getTemplatesPyDictForm(templateIDList) plateIDs = getPlateIDsFromTemplateFilePyDictForm(templateDatas) # print(plateIDs) plateConfigsPyDictForm = getPlateConfigPyDictForms(plateIDs) # print(plateConfigsPyDictForm) levelConfigs = generateLevelConfigWithPlateConfig(plateConfigsPyDictForm) # print(levelConfigs) # TODO 在模拟生成最终的关卡数据 所以先不save了 allLevelConfigs.append(levelConfigs) # saveLevelConfigToJsonFile(levelConfigs, file) finalLevelConfigs = simulateToGenerateRealLevelConfig(allLevelConfigs) saveFinalLevelConfigToJsonFile(finalLevelConfigs, file) deleteAllHolesInConfigFile() ''' 使用之前生成的关卡配置 模拟接入C++关卡生成代码后生成最终的关卡配置文件 ''' ''' 返回最终的 levelConfig ''' def simulateToGenerateRealLevelConfig(levelConfigs: list) -> list: for levelConfig in levelConfigs: # print(levelConfig) # 随机挑选完情况后 关卡的钉子总数 nScrewsOfLevel = 0 conditionsSelected = [] for plate in levelConfig: # 随机挑选一种情况 holes = plate['holes'] conditionID = random.randint(0, len(holes) - 1) # print(conditionID) conditionsSelected.append(conditionID) nScrewsOfLevel += len(holes[conditionID]['position']) # print(nScrewsOfLevel) # 在钉子种类范围内 随机选取钉子编号 screwIDList = [random.randint(1, nScrewTypes) for i in range(nScrewsOfLevel)] random.shuffle(screwIDList) # print("随机挑选的钉子的ID: ", screwIDList) # print("选取的盘子情况的ID: ", conditionsSelected) screwPositions = [] i = 0 for plate in levelConfig: # print(plate['holes'][conditionsSelected[i]]['position']) for position in plate['holes'][conditionsSelected[i]]['position']: screwPositions.append(position) i += 1 # print(screwPositions) # screwConfigs = [{ # "screwId": screwIDList[i], # "scale": 1.0, # "rotate": 0.0, # "position": screwPositions[i] # } for i in range(len(screwIDList))] # print(screwConfigs) # 每个盘子的添加screwConfig 其中钉子的数量是被选择的情况的空位数 nScrewAdded = 0 i = 0 for plate in levelConfig: nScrewsToAdd = len(plate['holes'][conditionsSelected[i]]['position']) screwConfigs = [{ "screwId": screwIDList[nScrewAdded + j], "scale": 1.0, "rotate": 0.0, "position": screwPositions[nScrewAdded + j] } for j in range(nScrewsToAdd)] nScrewAdded += nScrewsToAdd i += 1 plate['screws'] = screwConfigs # print(plate) # 修改plateConfig后的levelConfig # print(levelConfig) # 修改后的levelConfigs # print(levelConfigs) return levelConfigs ''' 获得盘子最少和最多能安放的钉子数 ''' def getMinMaxScrewsOfPlate(holes: list) -> tuple: for condition in holes: maxSizeOfCondition = 0 minSizeOfCondition = 1 positions = condition['position'] if len(positions) > maxSizeOfCondition: maxSizeOfCondition = len(positions) if len(positions) < minSizeOfCondition: minSizeOfCondition = len(positions) return minSizeOfCondition, maxSizeOfCondition def saveFinalLevelConfigToJsonFile(finalLevelConfig: list, file: str) -> None: fileName = finalLevelConfigPath + file.replace("red", "json") copyOfFinalConfig = copy.copy(finalLevelConfig) with open(fileName, "w") as finalLevelConfigFile: # print(finalLevelConfigFile) finalData = {"plateData": copyOfFinalConfig} json.dump(finalData, finalLevelConfigFile) def deleteAllHolesInConfigFile() -> None: for filePath in os.listdir(finalLevelConfigPath): # .DS_Store 必须忽略 不然会出现编码错误 if filePath != ".DS_Store": with open(finalLevelConfigPath + filePath, 'r') as file: # rawData = file.read() # rawData = rawData.encode("utf-8", errors="ignore") # rawData = json.loads(rawData) rawData = json.load(file) for levelConfig in rawData['plateData']: for plate in levelConfig: if 'holes' in plate: del plate['holes'] # TODO: 不知道为什么有的是dict有的是list if type(rawData['plateData']) == list: if type(rawData['plateData'][0]) == list: rawData['plateData'] = rawData['plateData'][0] else: rawData['plateData'] = [rawData['plateData']] # print(type(rawData['plateData'])) # print(rawData['plateData']) with open(finalLevelConfigPath + filePath, 'w') as file: json.dump(rawData, file) if __name__ == "__main__": # print("Hello") # askAndSetNewConfigFileDirPath() # testToConvert1() # TODO: 注意一下 里面没有save为中间态json #testToConvertAll() testToConvert11()