123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404 |
- 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()
|