jsonToProto.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. # -*- coding: utf-8 -*-
  2. import os
  3. import os.path
  4. import sys
  5. import json
  6. import levelData_pb2
  7. import csv
  8. # 获取当前工作目录
  9. current_dir = os.getcwd()
  10. # 获取脚本所在的目录
  11. script_dir = os.path.dirname(os.path.abspath(__file__))
  12. # 改变当前工作目录
  13. os.chdir(script_dir)
  14. jsDir = "./loadable/"
  15. # 所有关卡文件的信息,key为关卡文件名,value是一个dict,包含关卡文件的信息
  16. lvFileInfo = {}
  17. templateInfo = {} # 模版信息
  18. # 所有关卡的信息,key为关卡id,value是一个dict,包含关卡的信息
  19. lvInfo = {}
  20. def parseLvid():
  21. global lvFileInfo, lvInfo
  22. with open("../config_level.csv", 'r') as lcf:
  23. # csv 文件,格式如下:
  24. # LevelId,Des,TemplateId,RandomType,DupPara1,DupPara2,DupPara3,DupPara4,DupPara5,DupPara6,DupPara7,DupPara8,DupPara9,DupPara10
  25. # string,string,string,int,array,array,array,array,array,array,array,array,array,array
  26. # 关卡 id,备注,模板 id,0=固定按第n次玩来取副本,1=随机取,第1次玩(ATile种类数 | 随机种子 | 塔副本幸运值 | BTile种类数),第2次玩(ATile种类数 | 随机种子 | 塔副本幸运值 | BTile种类数),第3次玩(ATile种类数 | 随机种子 | 塔副本幸运值 | BTile种类数),第4次玩(ATile种类数 | 随机种子 | 塔副本幸运值 | BTile种类数),第5次玩(ATile种类数 | 随机种子 | 塔副本幸运值 | BTile种类数),第6次玩(ATile种类数 | 随机种子 | 塔副本幸运值 | BTile种类数),第7次玩(ATile种类数 | 随机种子 | 塔副本幸运值 | BTile种类数),第8次玩(ATile种类数 | 随机种子 | 塔副本幸运值 | BTile种类数),第9次玩(ATile种类数 | 随机种子 | 塔副本幸运值 | BTile种类数),第10次及之后玩(ATile种类数 | 随机种子 | 塔副本幸运值 | BTile种类数)
  27. # toolguide01,,undo01,0,1|0|100|1,1|0|100|1,1|0|100|1,1|0|100|1,1|0|100|1,1|0|100|1,1|0|100|1,1|0|100|1,1|0|100|1,1|0|100|1
  28. # 将 csv 文件转为 dict,存放到 lvFileInfo 中;忽略前两行,并将第3行作为 dict 的 key,后面的行作为 value
  29. lcf.readline()
  30. lcf.readline()
  31. keys = lcf.readline().strip().split(',')
  32. for line in lcf:
  33. line = line.strip()
  34. if len(line) == 0:
  35. continue
  36. if line[0] == '#':
  37. continue
  38. line = line.split(',')
  39. lvFileInfo[line[0]] = dict(zip(keys, line))
  40. with open("../config_level_list.csv") as cllf:
  41. # csv 文件,格式如下:
  42. # LevelIndex,LevelType,LevelCoin,RewardCoinMultiple,LevelId,DDALevelId1,DDALevelId2,DDALevelId3,DDAType,DDAMatrix,LevelDuration,LevelCoinPercentage
  43. # int,int,int,int,array,array,array,array,int,array,int,int
  44. # 第几关,关卡类型,0=普通,1=Hard,2=SuperHard,3=金币,基础金币奖励值。结算金币数=0时不显示。,用户看完广告后获得的金币倍数,关卡 id,关卡 id,关卡 id,关卡 id,DDA 类型,0=不走DDA(取 LevelId 列),1=走DDA,2=考试关(取 LevelId 列),DDA 参数,金币关的关卡时长(单位秒),最高100分钟,即6000s,金币tile的比例(单位为%,取值0-100)
  45. # 1,0,0,0,tempnm0017,tempnm0017,tempnm0017,tempnm0017,0,0|100,,
  46. # 将 csv 文件转为 dict,存放到 lvInfo 中;忽略前两行,并将第3行作为 dict 的 key,后面的行作为 value
  47. cllf.readline()
  48. cllf.readline()
  49. keys = cllf.readline().strip().split(',')
  50. for line in cllf:
  51. line = line.strip()
  52. if len(line) == 0:
  53. continue
  54. if line[0] == '#':
  55. continue
  56. line = line.split(',')
  57. lvInfo[line[0]] = dict(zip(keys, line))
  58. pass
  59. # 解析关卡的种子信息
  60. lvSeedInfo = {}
  61. def parseSeed():
  62. global lvSeedInfo
  63. with open("../lv_seed.txt") as cllf:
  64. # TileManor输出的文件
  65. # 每一行的格式:[Tile] resovler id: 30,735680|69576|719549|907289|550748|399652|246018|611094|979453|303995|269635|
  66. # 去掉头部:"[Tile] resovler id: " 之后的数据分成两部分,第一部分是lvid,后面是一系列种子的列表
  67. # 将这些数据存放到 lvSeedInfo 中,key为lvid,value为种子列表
  68. for line in cllf:
  69. line = line.strip()
  70. if len(line) == 0:
  71. continue
  72. if line[0] == '#':
  73. continue
  74. fs = line.split(':')[1].split(',')
  75. lvid = fs[0].strip()
  76. seeds = fs[1].split('|')
  77. lvSeedInfo[lvid] = seeds
  78. pass
  79. def converJosnToProto(tDir, tFileName, leveId):
  80. global lvFileInfo, lvInfo
  81. jsonFile = os.path.join(tDir, tFileName + '.json')
  82. with open(jsonFile, 'r') as f:#, encoding='utf-8'
  83. jsonData = json.load(f)
  84. levelData = levelData_pb2.LevelData()
  85. levelData.tileWidth = jsonData['LevelWidth']
  86. levelData.tileHeight = jsonData['LevelHeight']
  87. cntUndefined = 0
  88. tilesByPos = {}
  89. maxZ = 0
  90. maxX = 0
  91. maxY = 0
  92. for item in jsonData['LevelData']:
  93. tile = levelData.tiles.add()
  94. tile.x = int(item['X'])
  95. tile.y = int(item['Y'])
  96. tile.z = int(item['Z'])
  97. tiledata = tile.tileData
  98. tiledata.zv = 0
  99. tiledata.weight = 0
  100. tiledata.id = int(item['TileId'])
  101. if tiledata.id == -10:
  102. cntUndefined += 1
  103. tiledata.type = 1
  104. tiledata.subtype = 0
  105. # 一些统计信息
  106. tilesByPos[(tile.x, tile.y, tile.z)] = tile
  107. if tile.z > maxZ:
  108. maxZ = tile.z
  109. if tile.x > maxX:
  110. maxX = tile.x
  111. if tile.y > maxY:
  112. maxY = tile.y
  113. # 根据上面的布局信息,计算每个tile的几个信息:
  114. # 1. 每个tile的视觉层级(不同于上面z的信息,那是一个布局信息,相同的z可能出在不同的视觉层级)
  115. # 从maxZ开始,逐层向下计算;对于每一个位置,如果该位置有tile,则计算该tile的视觉层级
  116. # 对于每一个tile,如果其上方(从该tile的z+1层,一直到maxZ层)有tile,则其视觉层级为上方tile的视觉层级+1
  117. for z in range(0, maxZ+1):
  118. z = maxZ - z
  119. for x in range(0, maxX+1):
  120. for y in range(0, maxY+1):
  121. if (x,y,z) in tilesByPos:
  122. tile = tilesByPos[(x, y, z)]
  123. adjs = [(x,y),(x,y+1),(x,y-1),(x+1,y),(x+1,y+1),(x+1,y-1),(x-1,y),(x-1,y-1),(x-1,y+1)]
  124. # 确定视觉层级
  125. zvMax = -1
  126. for zup in range(z+1, maxZ+1):
  127. for adj in adjs:
  128. if (adj[0],adj[1],zup) in tilesByPos:
  129. zv = tilesByPos[adj[0],adj[1],zup].tileData.zv
  130. if zv > zvMax:
  131. zvMax = zv
  132. tile.tileData.zv = zvMax + 1
  133. # 计算权重
  134. # 从最底下开始,逐层向上计算
  135. # 每个tile的初始权重为4,然后加上其下方压住的tile的权重:如果压了全部,则该加上被压住的tile权重;
  136. # 如果压住了一半,则加上被压住的tile权重的一半;如果是压住1/4,则加上被压住的tile权重的1/4
  137. for z in range(0, maxZ+1):
  138. for x in range(0, maxX+1):
  139. for y in range(0, maxY+1):
  140. if (x,y,z) in tilesByPos:
  141. tile = tilesByPos[(x,y,z)]
  142. adjs = [(x,y,1.0),(x,y+1,0.5),(x,y-1,0.5),(x+1,y,0.5),(x+1,y+1,0.25),(x+1,y-1,0.25),(x-1,y,0.5),(x-1,y-1,0.25),(x-1,y+1,0.25)]
  143. weight = 4
  144. for adj in adjs:
  145. # 从临近层往下,在某个位置找到了tile,则该tile被压住了,就不再找了
  146. for zb in range(0, z):
  147. zb = z-1-zb
  148. if (adj[0], adj[1], zb) in tilesByPos:
  149. tileB = tilesByPos[(adj[0], adj[1], zb)]
  150. weight += tileB.tileData.weight * adj[2]
  151. break
  152. tile.tileData.weight = int(weight)
  153. pass
  154. for item in jsonData['StackData']:
  155. stack_data = levelData.stacks.add()
  156. stack_data.x = int(item['X'])
  157. stack_data.y = int(item['Y'])
  158. stack_data.direction = int(item['Direction'])
  159. templateInfo[tFileName] = {}
  160. if cntUndefined == 0:
  161. templateInfo[tFileName]['无未定义的tile'] = '1'
  162. elif cntUndefined == len(jsonData['LevelData']):
  163. templateInfo[tFileName]['所有tile都未被定义'] = '1'
  164. else:
  165. templateInfo[tFileName]['部分tile被定义'] = '1'
  166. if len(jsonData['StackData']) > 0:
  167. templateInfo[tFileName]['有stack数据'] = '1'
  168. # 数据序列化
  169. level_protobuf_data = levelData.SerializeToString()
  170. protoFile = os.path.join(jsDir, tFileName + '.bin')
  171. with open(protoFile, 'w+b') as f:
  172. f.write(level_protobuf_data)
  173. #关卡数据拼接map+trcuk
  174. def mergeLeveldata3(group = None):
  175. lvelsIndex = levelData_pb2.LevelsIndex()
  176. if group is not None:
  177. protoDataTrunk = os.path.join(jsDir, 'levels-' + str(group) + '.bin')
  178. protoDataIndex = os.path.join(jsDir, 'levelsIndex-' + str(group) + '.bin')
  179. else:
  180. protoDataTrunk = os.path.join(jsDir, 'levels' + '.bin')
  181. protoDataIndex = os.path.join(jsDir, 'levelsIndex' + '.bin')
  182. offset = 0
  183. for filename in os.listdir(jsDir):
  184. my_message = levelData_pb2.LevelData()
  185. binFile = os.path.splitext(filename)[0] + '.bin'
  186. binFile = os.path.join(jsDir, binFile)
  187. if os.path.exists(binFile) :
  188. # print(binFile)
  189. with open(binFile, "rb") as f:
  190. binary_data = f.read()
  191. my_message.ParseFromString(binary_data)
  192. with open(protoDataTrunk, 'ab') as f:
  193. f.write(binary_data)
  194. lvelsIndex.LevelsIndex[os.path.splitext(filename)[0]].len = len(binary_data)
  195. lvelsIndex.LevelsIndex[os.path.splitext(filename)[0]].offset = offset
  196. offset += len(binary_data)
  197. level_protobuf_index = lvelsIndex.SerializePartialToString()
  198. with open(protoDataIndex, 'w+b') as f:
  199. f.write(level_protobuf_index)
  200. if __name__ == '__main__':
  201. parseLvid()
  202. parseSeed()
  203. folder_path = '../level_data2'
  204. for filename in os.listdir(folder_path):
  205. # 获取文件的绝对路径
  206. file_path = os.path.join(folder_path, filename)
  207. # 检查是否是文件而不是文件夹
  208. if os.path.isfile(file_path):
  209. base_name = os.path.splitext(filename)
  210. # 在文件转为bin
  211. converJosnToProto(folder_path, base_name[0],1)
  212. mergeLeveldata3()
  213. # 将关卡id和模板的对应关系输出到文件
  214. lv_stat = {}
  215. got = False
  216. for k, v in lvInfo.items():
  217. levelId = v["关卡 id"]
  218. templateId = lvFileInfo[levelId]["模板 id"]
  219. if templateId not in templateInfo:
  220. templateId += '_m'
  221. if templateId in templateInfo:
  222. # 将所有信息综合到一块
  223. lv_stat[k] = lvFileInfo[levelId].copy()
  224. for key in templateInfo[templateId].keys():
  225. lv_stat[k][key] = templateInfo[templateId][key]
  226. lv_stat[k]["关卡 id"] = levelId
  227. lv_stat[k]["关卡类型"] = v["关卡类型,0=普通,1=Hard,2=SuperHard,3=金币"]
  228. lv_stat[k]["dda_type"] = v["DDA 类型,0=不走DDA(取 LevelId 列),1=走DDA,2=考试关(取 LevelId 列)"]
  229. lv_stat[k]["dda_para"] = v["DDA 参数"]
  230. lv_stat[k]["模板 id"] = templateId
  231. # 关卡与模版的对应关系,以及关卡本身的一些控制参数
  232. with open("levelInfo.csv", 'w+') as f:
  233. fieldnames = ["lvid","lvinst","type(normal=0,hard=1,superHard=2)","dda_type","dda_para"]
  234. writer = csv.DictWriter(f, fieldnames=fieldnames)
  235. writer.writeheader()
  236. for lvid in sorted(lv_stat.keys(), key=lambda x:int(x)):
  237. d = {"lvid":lvid, "lvinst":"","type(normal=0,hard=1,superHard=2)":"","dda_type":"","dda_para":""}
  238. d["lvinst"] = lv_stat[lvid]["关卡 id"]
  239. d["type(normal=0,hard=1,superHard=2)"] = lv_stat[lvid]["关卡类型"]
  240. d["dda_type"] = lv_stat[lvid]["dda_type"]
  241. d["dda_para"] = lv_stat[lvid]["dda_para"]
  242. writer.writerow(d)
  243. # 模版的信息
  244. with open("levelInstInfo.csv", 'w+') as f:
  245. all_insts = set()
  246. fieldnames = ["lvinst","template",
  247. "try1(tileA|tileB|tileVar|indexTop3|indexPlusByZ|seed)",
  248. "try2(tileA|tileB|tileVar|indexTop3|indexPlusByZ|seed)",
  249. "try3(tileA|tileB|tileVar|indexTop3|indexPlusByZ|seed)",
  250. "try4(tileA|tileB|tileVar|indexTop3|indexPlusByZ|seed)",
  251. "try5(tileA|tileB|tileVar|indexTop3|indexPlusByZ|seed)",
  252. "try6(tileA|tileB|tileVar|indexTop3|indexPlusByZ|seed)",
  253. "try7(tileA|tileB|tileVar|indexTop3|indexPlusByZ|seed)",
  254. "try8(tileA|tileB|tileVar|indexTop3|indexPlusByZ|seed)",
  255. "try9(tileA|tileB|tileVar|indexTop3|indexPlusByZ|seed)",
  256. "try10(tileA|tileB|tileVar|indexTop3|indexPlusByZ|seed)"]
  257. writer = csv.DictWriter(f, fieldnames=fieldnames)
  258. writer.writeheader()
  259. for lvid in sorted(lv_stat.keys(), key=lambda x:int(x)):
  260. inst_id = lv_stat[lvid]["关卡 id"]
  261. if inst_id not in all_insts:
  262. all_insts.add(inst_id)
  263. else:
  264. continue
  265. d = {"lvinst":"", "template":"",
  266. "try1(tileA|tileB|tileVar|indexTop3|indexPlusByZ|seed)":"",
  267. "try2(tileA|tileB|tileVar|indexTop3|indexPlusByZ|seed)":"",
  268. "try3(tileA|tileB|tileVar|indexTop3|indexPlusByZ|seed)":"",
  269. "try4(tileA|tileB|tileVar|indexTop3|indexPlusByZ|seed)":"",
  270. "try5(tileA|tileB|tileVar|indexTop3|indexPlusByZ|seed)":"",
  271. "try6(tileA|tileB|tileVar|indexTop3|indexPlusByZ|seed)":"",
  272. "try7(tileA|tileB|tileVar|indexTop3|indexPlusByZ|seed)":"",
  273. "try8(tileA|tileB|tileVar|indexTop3|indexPlusByZ|seed)":"",
  274. "try9(tileA|tileB|tileVar|indexTop3|indexPlusByZ|seed)":"",
  275. "try10(tileA|tileB|tileVar|indexTop3|indexPlusByZ|seed)":""}
  276. d["lvinst"] = lv_stat[lvid]["关卡 id"]
  277. d["template"] = lv_stat[lvid]["模板 id"]
  278. seeds = []
  279. if lvid in lvSeedInfo:
  280. seeds = lvSeedInfo[lvid]
  281. for tried in range(1,11):
  282. k1 = "第" + str(tried) + "次玩(ATile种类数 | 随机种子 | 塔副本幸运值 | BTile种类数)"
  283. if tried == 10:
  284. k1 = "第10次及之后玩(ATile种类数 | 随机种子 | 塔副本幸运值 | BTile种类数)"
  285. if k1 in lv_stat[lvid]:
  286. try1 = lv_stat[lvid][k1]
  287. tileA = try1.split('|')[0]
  288. tileB = try1.split('|')[3]
  289. tileCnt = tileA if int(tileA) > int(tileB) else tileB
  290. ps=[tileCnt,"0","0","100","100"]
  291. if tried <= len(seeds):
  292. ps.append(seeds[tried-1])
  293. else:
  294. ps.append("100")
  295. d["try" + str(tried)+"(tileA|tileB|tileVar|indexTop3|indexPlusByZ|seed)"] = "|".join(ps)
  296. else:
  297. d["try" + str(tried)+"(tileA|tileB|tileVar|indexTop3|indexPlusByZ|seed)"] = ""
  298. writer.writerow(d)
  299. # 导出关卡id和模板的对应关系,并输出模板的信息
  300. else:
  301. print('ohoh')
  302. # 恢复原来的工作目录
  303. os.chdir(current_dir)