level11_red2json.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404
  1. import json
  2. import os
  3. import re
  4. import xml.etree.ElementTree as ET
  5. import random
  6. import copy
  7. #资源根目录
  8. resRootDir = "/Users/red/Documents/codings/ScrewGame1/Resources"
  9. # 关卡配置文件目录
  10. configFileDirPath = resRootDir+"/res_ScrewGame/关卡/"
  11. # 关卡模板文件目录
  12. templateFileDirPath = resRootDir+"/res_ScrewGame/关卡/关卡模板/"
  13. # 盘子文件目录
  14. plateConfigFileDirPath = resRootDir+"/res_ScrewGame/ccb/盘子上位置情况/"
  15. # 关卡JSON文件夹目录
  16. levelConfigJSONDirPath = resRootDir+"/res_ScrewGame/config/"
  17. # 关卡配置文件最终目录
  18. finalLevelConfigPath = resRootDir+"/res_ScrewGame/config/最终关卡配置/"
  19. # 模拟接入C++关卡生成的钉子种类数 wf??
  20. nScrewTypes = 3
  21. """ 显示一下关卡配置文件路径 要修改的话改一下 """
  22. def askAndSetNewConfigFileDirPath():
  23. global configFileDirPath
  24. tipStr = "当前默认的关卡配置文件夹路径为: {}, 设置新的关卡配置文件夹路径? [y/n]".format(configFileDirPath)
  25. needNewConfigDirPath = input(tipStr)
  26. # 重新设置关卡配置文件夹路径
  27. if needNewConfigDirPath == 'y' or not configFileDirPath:
  28. configFileDirPath = input("输入新的关卡配置文件夹路径(路径结尾要带斜杠/):")
  29. setNewConfigDirPath()
  30. tipStr = "当前默认的关卡模板文件夹路径为: {}, 设置新的关卡模板配置文件夹路径? [y/n]".format(templateFileDirPath)
  31. # TODO: 设置新的关卡模板配置文件路径
  32. # TODO: 询问是否设置新的盘子文件目录
  33. # TODO: 设置新的盘子文件目录
  34. # TODO: 询问是否修改关卡JSON文件目录
  35. # TODO: 修改关卡JSON文件目录
  36. """修改本文件中的configFileDirPath, 下一次使用的时候默认使用这个新路径"""
  37. def setNewConfigDirPath() -> None:
  38. with open(__file__, "r") as curFile:
  39. content = curFile.read()
  40. pattern = r"configFileDirPath = \"\s\""
  41. replacement = r"configFileDirPath = \"{}\"".format(configFileDirPath)
  42. newContent = re.sub(pattern, replacement, content, count=1)
  43. with open(__file__, "w") as curFile:
  44. curFile.write(newContent)
  45. """ 测试 将一个关卡配置文件转化为 JSON """
  46. def testToConvert1():
  47. print("nothing to do")
  48. # by wf
  49. def testToConvert11():
  50. levelRedName="关卡11.red"
  51. configFilePath = configFileDirPath + levelRedName
  52. with open(configFilePath) as configFile:
  53. configFileTree = ET.parse(configFile)
  54. fileRoot = configFileTree.getroot()
  55. # print(fileRoot.keys())
  56. dataPyDict = xmlToDict(fileRoot[0])
  57. # print(dataPyDict)
  58. #print(dataPyDict)
  59. templateIDList = getTemplateIDFromDict(dataPyDict)
  60. #print(templateIDList)
  61. templateDatas = getTemplatesPyDictForm(templateIDList)
  62. #print(templateDatas) # ok
  63. plateIDs = getPlateIDsFromTemplateFilePyDictForm(templateDatas)
  64. print(plateIDs) # ok
  65. plateConfigsPyDictForm = getPlateConfigPyDictForms(plateIDs)
  66. #print(plateConfigsPyDictForm)
  67. levelConfigs = generateLevelConfigWithPlateConfig(plateConfigsPyDictForm)
  68. # print(levelConfigs)
  69. saveLevelConfigToJsonFile(levelConfigs, levelRedName)
  70. """ 从转化为dict的关卡配置中获取使用的关卡模板号 """
  71. def getTemplateIDFromDict(dataPyDict: dict) -> list:
  72. templateList = dataPyDict['nodeGraph']['children'][0]['children']
  73. # print(templateList)
  74. templateIDList = []
  75. for template in templateList:
  76. print("debug " + template['displayName'])
  77. templateIDList.append( template['displayName'] )
  78. return templateIDList
  79. """ 从传入的关卡模板号对应的关卡模板配置文件中获得py-dict格式的模板数据 """
  80. def getTemplatesPyDictForm(templateIDList) -> list:
  81. ret = []
  82. # 这里的ID是关卡模板ID
  83. for ID in templateIDList:
  84. templateFileName =templateFileDirPath+ID+".red" # "{}模板{}.red".format(templateFileDirPath, ID)
  85. # print(templateFileName)
  86. with open(templateFileName, "r") as templateFile:
  87. templateFileRoot = ET.parse(templateFile).getroot()
  88. templateFilePyDictForm = xmlToDict(templateFileRoot[0])
  89. # print(templateFilePyDictForm)
  90. ret.append(templateFilePyDictForm)
  91. return ret
  92. """ 从转化为py-dict的templateFile中获取使用到的plate的ID的list """
  93. def getPlateIDsFromTemplateFilePyDictForm(templateFilePyDictForms: list) -> list:
  94. ret = []
  95. for templateFilePyDictForm in templateFilePyDictForms:
  96. for plates in templateFilePyDictForm["nodeGraph"]["children"][0]["children"]:
  97. ret.append(plates["displayName"])
  98. return ret
  99. """ 根据盘子ID获得py-dict格式的盘子配置 """
  100. def getPlateConfigPyDictForms(plateIDs: list) -> list:
  101. ret = []
  102. for plateID in plateIDs:
  103. plateConfigFileName = plateConfigFileDirPath + plateID+".red"
  104. # print(plateConfigFileName)
  105. with open(plateConfigFileName, "r") as plateConfigFile:
  106. plateConfigPyDictForm = xmlToDict(ET.parse(plateConfigFile).getroot()[0])
  107. plateConfigPyDictForm["plateID"] = plateID # added
  108. # print(plateConfigPyDictForm)
  109. ret.append(plateConfigPyDictForm)
  110. return ret
  111. """ 需要获得每个 plate 的 position, scale, rotate, plateId, zorder """
  112. def generateLevelConfigWithPlateConfig(plateConfigPyDictForms: list) -> list:
  113. plateDatas = []
  114. for plate in plateConfigPyDictForms:
  115. properties = plate["nodeGraph"]["children"][0]["properties"]
  116. # print(propertiesDict)
  117. plateConfig = {}
  118. for property in properties:
  119. propertyName = property["name"]
  120. if propertyName == "position":
  121. position = property["value"][:2]
  122. position = [round(float(n), 1) for n in position]
  123. plateConfig["position"] = position
  124. elif propertyName == "scale":
  125. scale = round(float(property["value"][0]), 1)
  126. plateConfig["scale"] = scale
  127. elif propertyName == "rotation":
  128. rotate = round(float(property["value"]), 1)
  129. plateConfig["rotate"] = rotate
  130. if "position" not in plateConfig:
  131. # 这个默认的位置可能不对 随便设置的
  132. plateConfig["position"] = [500.0, 500.0]
  133. if "scale" not in plateConfig:
  134. plateConfig["scale"] = 1.000
  135. if "rotate" not in plateConfig:
  136. plateConfig["rotate"] = 0.000
  137. plateName = plate["nodeGraph"]["children"][0]["displayName"]
  138. pattern = r"\d*$"
  139. plateID = re.findall(pattern, plateName)[0]
  140. plateConfig["plateId"] = 0 # deprecated int(plateID)
  141. print("debug plate name " + plate["plateID"])
  142. plateConfig["plateIds"] = plate["plateID"] # 新加字符串ID
  143. plateConfig["zorder"] = 1
  144. plateConfig["holes"] = getHolePositionsFromPlateConfig(plate)
  145. # print(plateConfig)
  146. plateDatas.append(plateConfig)
  147. return plateDatas
  148. # 获取钉子的位置
  149. def getHolePositionsFromPlateConfig(plateConfigPyDictForms: list) -> list:
  150. ret = []
  151. plateConditions = plateConfigPyDictForms["nodeGraph"]["children"][0]["children"]
  152. # 对应plate的每种情况 比如 "一个钉子" "五个钉子"
  153. for condition in plateConditions:
  154. holeConfigs = {}
  155. holes = condition["children"]
  156. holePositions = []
  157. for hole in holes:
  158. holeProperties = hole["properties"]
  159. for property in holeProperties:
  160. if property["name"] == "position":
  161. holePositions.append([round(float(property["value"][0]), 1), round(float(property["value"][1]), 1)])
  162. break
  163. holeConfigs["position"] = holePositions
  164. ret.append(holeConfigs)
  165. return ret
  166. """ 将关卡配置保存到JSON中 """
  167. def saveLevelConfigToJsonFile(levelConfigs: list, fileName: str) -> None:
  168. # print("saving json files: ", fileName)
  169. fileName = fileName.replace(".red", ".json")
  170. with open(levelConfigJSONDirPath + fileName, "w") as levelConfigFile:
  171. finalData = {"plateData": levelConfigs}
  172. json.dump(finalData, levelConfigFile, ensure_ascii=False)
  173. """ 将.red文件的内容转化为 python-dict 格式 """
  174. def xmlToDict(fileRoot: ET.Element) -> dict | list | str:
  175. rootTag = fileRoot.tag
  176. # print(rootTag)
  177. resList = []
  178. resDict = {}
  179. if rootTag == 'dict':
  180. idx = 0
  181. while idx < len(fileRoot):
  182. if fileRoot[idx].tag == 'key':
  183. nextTag = fileRoot[idx + 1].tag
  184. if nextTag == 'true':
  185. resDict[fileRoot[idx].text] = True
  186. elif nextTag == 'false':
  187. resDict[fileRoot[idx].text] = False
  188. else:
  189. resDict[fileRoot[idx].text] = xmlToDict(fileRoot[idx + 1])
  190. idx += 1
  191. idx += 1
  192. return resDict
  193. elif rootTag == 'array':
  194. for child in fileRoot:
  195. resList.append(xmlToDict(child))
  196. return resList
  197. else:
  198. return fileRoot.text
  199. ''' 测试 将关卡的redream转化成json '''
  200. def testToConvertAll() -> None:
  201. # configFilePath = configFileDirPath + "关卡1test.red"
  202. allLevelConfigs = []
  203. for file in os.listdir(configFileDirPath):
  204. # print(file)
  205. if file != ".DS_Store":
  206. absolutePath = configFileDirPath + file
  207. if not os.path.isdir(absolutePath):
  208. # print(absolutePath)
  209. with open(configFileDirPath + file) as configFile:
  210. configFileTree = ET.parse(configFile)
  211. fileRoot = configFileTree.getroot()
  212. # print(fileRoot.keys())
  213. dataPyDict = xmlToDict(fileRoot[0])
  214. # print(dataPyDict)
  215. templateIDList = getTemplateIDFromDict(dataPyDict)
  216. # print(templateIDList)
  217. templateDatas = getTemplatesPyDictForm(templateIDList)
  218. plateIDs = getPlateIDsFromTemplateFilePyDictForm(templateDatas)
  219. # print(plateIDs)
  220. plateConfigsPyDictForm = getPlateConfigPyDictForms(plateIDs)
  221. # print(plateConfigsPyDictForm)
  222. levelConfigs = generateLevelConfigWithPlateConfig(plateConfigsPyDictForm)
  223. # print(levelConfigs)
  224. # TODO 在模拟生成最终的关卡数据 所以先不save了
  225. allLevelConfigs.append(levelConfigs)
  226. # saveLevelConfigToJsonFile(levelConfigs, file)
  227. finalLevelConfigs = simulateToGenerateRealLevelConfig(allLevelConfigs)
  228. saveFinalLevelConfigToJsonFile(finalLevelConfigs, file)
  229. deleteAllHolesInConfigFile()
  230. ''' 使用之前生成的关卡配置 模拟接入C++关卡生成代码后生成最终的关卡配置文件 '''
  231. ''' 返回最终的 levelConfig '''
  232. def simulateToGenerateRealLevelConfig(levelConfigs: list) -> list:
  233. for levelConfig in levelConfigs:
  234. # print(levelConfig)
  235. # 随机挑选完情况后 关卡的钉子总数
  236. nScrewsOfLevel = 0
  237. conditionsSelected = []
  238. for plate in levelConfig:
  239. # 随机挑选一种情况
  240. holes = plate['holes']
  241. conditionID = random.randint(0, len(holes) - 1)
  242. # print(conditionID)
  243. conditionsSelected.append(conditionID)
  244. nScrewsOfLevel += len(holes[conditionID]['position'])
  245. # print(nScrewsOfLevel)
  246. # 在钉子种类范围内 随机选取钉子编号
  247. screwIDList = [random.randint(1, nScrewTypes) for i in range(nScrewsOfLevel)]
  248. random.shuffle(screwIDList)
  249. # print("随机挑选的钉子的ID: ", screwIDList)
  250. # print("选取的盘子情况的ID: ", conditionsSelected)
  251. screwPositions = []
  252. i = 0
  253. for plate in levelConfig:
  254. # print(plate['holes'][conditionsSelected[i]]['position'])
  255. for position in plate['holes'][conditionsSelected[i]]['position']:
  256. screwPositions.append(position)
  257. i += 1
  258. # print(screwPositions)
  259. # screwConfigs = [{
  260. # "screwId": screwIDList[i],
  261. # "scale": 1.0,
  262. # "rotate": 0.0,
  263. # "position": screwPositions[i]
  264. # } for i in range(len(screwIDList))]
  265. # print(screwConfigs)
  266. # 每个盘子的添加screwConfig 其中钉子的数量是被选择的情况的空位数
  267. nScrewAdded = 0
  268. i = 0
  269. for plate in levelConfig:
  270. nScrewsToAdd = len(plate['holes'][conditionsSelected[i]]['position'])
  271. screwConfigs = [{
  272. "screwId": screwIDList[nScrewAdded + j],
  273. "scale": 1.0,
  274. "rotate": 0.0,
  275. "position": screwPositions[nScrewAdded + j]
  276. } for j in range(nScrewsToAdd)]
  277. nScrewAdded += nScrewsToAdd
  278. i += 1
  279. plate['screws'] = screwConfigs
  280. # print(plate)
  281. # 修改plateConfig后的levelConfig
  282. # print(levelConfig)
  283. # 修改后的levelConfigs
  284. # print(levelConfigs)
  285. return levelConfigs
  286. ''' 获得盘子最少和最多能安放的钉子数 '''
  287. def getMinMaxScrewsOfPlate(holes: list) -> tuple:
  288. for condition in holes:
  289. maxSizeOfCondition = 0
  290. minSizeOfCondition = 1
  291. positions = condition['position']
  292. if len(positions) > maxSizeOfCondition:
  293. maxSizeOfCondition = len(positions)
  294. if len(positions) < minSizeOfCondition:
  295. minSizeOfCondition = len(positions)
  296. return minSizeOfCondition, maxSizeOfCondition
  297. def saveFinalLevelConfigToJsonFile(finalLevelConfig: list, file: str) -> None:
  298. fileName = finalLevelConfigPath + file.replace("red", "json")
  299. copyOfFinalConfig = copy.copy(finalLevelConfig)
  300. with open(fileName, "w") as finalLevelConfigFile:
  301. # print(finalLevelConfigFile)
  302. finalData = {"plateData": copyOfFinalConfig}
  303. json.dump(finalData, finalLevelConfigFile)
  304. def deleteAllHolesInConfigFile() -> None:
  305. for filePath in os.listdir(finalLevelConfigPath):
  306. # .DS_Store 必须忽略 不然会出现编码错误
  307. if filePath != ".DS_Store":
  308. with open(finalLevelConfigPath + filePath, 'r') as file:
  309. # rawData = file.read()
  310. # rawData = rawData.encode("utf-8", errors="ignore")
  311. # rawData = json.loads(rawData)
  312. rawData = json.load(file)
  313. for levelConfig in rawData['plateData']:
  314. for plate in levelConfig:
  315. if 'holes' in plate:
  316. del plate['holes']
  317. # TODO: 不知道为什么有的是dict有的是list
  318. if type(rawData['plateData']) == list:
  319. if type(rawData['plateData'][0]) == list:
  320. rawData['plateData'] = rawData['plateData'][0]
  321. else:
  322. rawData['plateData'] = [rawData['plateData']]
  323. # print(type(rawData['plateData']))
  324. # print(rawData['plateData'])
  325. with open(finalLevelConfigPath + filePath, 'w') as file:
  326. json.dump(rawData, file)
  327. if __name__ == "__main__":
  328. # print("Hello")
  329. # askAndSetNewConfigFileDirPath()
  330. # testToConvert1()
  331. # TODO: 注意一下 里面没有save为中间态json
  332. #testToConvertAll()
  333. testToConvert11()