# -*- coding: utf-8 -*- import os import os.path import sys import json import levelData_pb2 import csv # 获取当前工作目录 current_dir = os.getcwd() # 获取脚本所在的目录 script_dir = os.path.dirname(os.path.abspath(__file__)) # 改变当前工作目录 os.chdir(script_dir) jsDir = "./loadable/" # 所有关卡文件的信息,key为关卡文件名,value是一个dict,包含关卡文件的信息 lvFileInfo = {} templateInfo = {} # 模版信息 # 所有关卡的信息,key为关卡id,value是一个dict,包含关卡的信息 lvInfo = {} def parseLvid(): global lvFileInfo, lvInfo with open("../config_level.csv", 'r') as lcf: # csv 文件,格式如下: # LevelId,Des,TemplateId,RandomType,DupPara1,DupPara2,DupPara3,DupPara4,DupPara5,DupPara6,DupPara7,DupPara8,DupPara9,DupPara10 # string,string,string,int,array,array,array,array,array,array,array,array,array,array # 关卡 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种类数) # 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 # 将 csv 文件转为 dict,存放到 lvFileInfo 中;忽略前两行,并将第3行作为 dict 的 key,后面的行作为 value lcf.readline() lcf.readline() keys = lcf.readline().strip().split(',') for line in lcf: line = line.strip() if len(line) == 0: continue if line[0] == '#': continue line = line.split(',') lvFileInfo[line[0]] = dict(zip(keys, line)) with open("../config_level_list.csv") as cllf: # csv 文件,格式如下: # LevelIndex,LevelType,LevelCoin,RewardCoinMultiple,LevelId,DDALevelId1,DDALevelId2,DDALevelId3,DDAType,DDAMatrix,LevelDuration,LevelCoinPercentage # int,int,int,int,array,array,array,array,int,array,int,int # 第几关,关卡类型,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) # 1,0,0,0,tempnm0017,tempnm0017,tempnm0017,tempnm0017,0,0|100,, # 将 csv 文件转为 dict,存放到 lvInfo 中;忽略前两行,并将第3行作为 dict 的 key,后面的行作为 value cllf.readline() cllf.readline() keys = cllf.readline().strip().split(',') for line in cllf: line = line.strip() if len(line) == 0: continue if line[0] == '#': continue line = line.split(',') lvInfo[line[0]] = dict(zip(keys, line)) pass # 解析关卡的种子信息 lvSeedInfo = {} def parseSeed(): global lvSeedInfo with open("../lv_seed.txt") as cllf: # TileManor输出的文件 # 每一行的格式:[Tile] resovler id: 30,735680|69576|719549|907289|550748|399652|246018|611094|979453|303995|269635| # 去掉头部:"[Tile] resovler id: " 之后的数据分成两部分,第一部分是lvid,后面是一系列种子的列表 # 将这些数据存放到 lvSeedInfo 中,key为lvid,value为种子列表 for line in cllf: line = line.strip() if len(line) == 0: continue if line[0] == '#': continue fs = line.split(':')[1].split(',') lvid = fs[0].strip() seeds = fs[1].split('|') lvSeedInfo[lvid] = seeds pass def converJosnToProto(tDir, tFileName, leveId): global lvFileInfo, lvInfo jsonFile = os.path.join(tDir, tFileName + '.json') with open(jsonFile, 'r') as f:#, encoding='utf-8' jsonData = json.load(f) levelData = levelData_pb2.LevelData() levelData.tileWidth = jsonData['LevelWidth'] levelData.tileHeight = jsonData['LevelHeight'] cntUndefined = 0 tilesByPos = {} maxZ = 0 maxX = 0 maxY = 0 for item in jsonData['LevelData']: tile = levelData.tiles.add() tile.x = int(item['X']) tile.y = int(item['Y']) tile.z = int(item['Z']) tiledata = tile.tileData tiledata.zv = 0 tiledata.weight = 0 tiledata.id = int(item['TileId']) if tiledata.id == -10: cntUndefined += 1 tiledata.type = 1 tiledata.subtype = 0 # 一些统计信息 tilesByPos[(tile.x, tile.y, tile.z)] = tile if tile.z > maxZ: maxZ = tile.z if tile.x > maxX: maxX = tile.x if tile.y > maxY: maxY = tile.y # 根据上面的布局信息,计算每个tile的几个信息: # 1. 每个tile的视觉层级(不同于上面z的信息,那是一个布局信息,相同的z可能出在不同的视觉层级) # 从maxZ开始,逐层向下计算;对于每一个位置,如果该位置有tile,则计算该tile的视觉层级 # 对于每一个tile,如果其上方(从该tile的z+1层,一直到maxZ层)有tile,则其视觉层级为上方tile的视觉层级+1 for z in range(0, maxZ+1): z = maxZ - z for x in range(0, maxX+1): for y in range(0, maxY+1): if (x,y,z) in tilesByPos: tile = tilesByPos[(x, y, z)] 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)] # 确定视觉层级 zvMax = -1 for zup in range(z+1, maxZ+1): for adj in adjs: if (adj[0],adj[1],zup) in tilesByPos: zv = tilesByPos[adj[0],adj[1],zup].tileData.zv if zv > zvMax: zvMax = zv tile.tileData.zv = zvMax + 1 # 计算权重 # 从最底下开始,逐层向上计算 # 每个tile的初始权重为4,然后加上其下方压住的tile的权重:如果压了全部,则该加上被压住的tile权重; # 如果压住了一半,则加上被压住的tile权重的一半;如果是压住1/4,则加上被压住的tile权重的1/4 for z in range(0, maxZ+1): for x in range(0, maxX+1): for y in range(0, maxY+1): if (x,y,z) in tilesByPos: tile = tilesByPos[(x,y,z)] 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)] weight = 4 for adj in adjs: # 从临近层往下,在某个位置找到了tile,则该tile被压住了,就不再找了 for zb in range(0, z): zb = z-1-zb if (adj[0], adj[1], zb) in tilesByPos: tileB = tilesByPos[(adj[0], adj[1], zb)] weight += tileB.tileData.weight * adj[2] break tile.tileData.weight = int(weight) pass for item in jsonData['StackData']: stack_data = levelData.stacks.add() stack_data.x = int(item['X']) stack_data.y = int(item['Y']) stack_data.direction = int(item['Direction']) templateInfo[tFileName] = {} if cntUndefined == 0: templateInfo[tFileName]['无未定义的tile'] = '1' elif cntUndefined == len(jsonData['LevelData']): templateInfo[tFileName]['所有tile都未被定义'] = '1' else: templateInfo[tFileName]['部分tile被定义'] = '1' if len(jsonData['StackData']) > 0: templateInfo[tFileName]['有stack数据'] = '1' # 数据序列化 level_protobuf_data = levelData.SerializeToString() protoFile = os.path.join(jsDir, tFileName + '.bin') with open(protoFile, 'w+b') as f: f.write(level_protobuf_data) #关卡数据拼接map+trcuk def mergeLeveldata3(group = None): lvelsIndex = levelData_pb2.LevelsIndex() if group is not None: protoDataTrunk = os.path.join(jsDir, 'levels-' + str(group) + '.bin') protoDataIndex = os.path.join(jsDir, 'levelsIndex-' + str(group) + '.bin') else: protoDataTrunk = os.path.join(jsDir, 'levels' + '.bin') protoDataIndex = os.path.join(jsDir, 'levelsIndex' + '.bin') offset = 0 for filename in os.listdir(jsDir): my_message = levelData_pb2.LevelData() binFile = os.path.splitext(filename)[0] + '.bin' binFile = os.path.join(jsDir, binFile) if os.path.exists(binFile) : # print(binFile) with open(binFile, "rb") as f: binary_data = f.read() my_message.ParseFromString(binary_data) with open(protoDataTrunk, 'ab') as f: f.write(binary_data) lvelsIndex.LevelsIndex[os.path.splitext(filename)[0]].len = len(binary_data) lvelsIndex.LevelsIndex[os.path.splitext(filename)[0]].offset = offset offset += len(binary_data) level_protobuf_index = lvelsIndex.SerializePartialToString() with open(protoDataIndex, 'w+b') as f: f.write(level_protobuf_index) if __name__ == '__main__': parseLvid() parseSeed() folder_path = '../level_data2' for filename in os.listdir(folder_path): # 获取文件的绝对路径 file_path = os.path.join(folder_path, filename) # 检查是否是文件而不是文件夹 if os.path.isfile(file_path): base_name = os.path.splitext(filename) # 在文件转为bin converJosnToProto(folder_path, base_name[0],1) mergeLeveldata3() # 将关卡id和模板的对应关系输出到文件 lv_stat = {} got = False for k, v in lvInfo.items(): levelId = v["关卡 id"] templateId = lvFileInfo[levelId]["模板 id"] if templateId not in templateInfo: templateId += '_m' if templateId in templateInfo: # 将所有信息综合到一块 lv_stat[k] = lvFileInfo[levelId].copy() for key in templateInfo[templateId].keys(): lv_stat[k][key] = templateInfo[templateId][key] lv_stat[k]["关卡 id"] = levelId lv_stat[k]["关卡类型"] = v["关卡类型,0=普通,1=Hard,2=SuperHard,3=金币"] lv_stat[k]["dda_type"] = v["DDA 类型,0=不走DDA(取 LevelId 列),1=走DDA,2=考试关(取 LevelId 列)"] lv_stat[k]["dda_para"] = v["DDA 参数"] lv_stat[k]["模板 id"] = templateId # 关卡与模版的对应关系,以及关卡本身的一些控制参数 with open("levelInfo.csv", 'w+') as f: fieldnames = ["lvid","lvinst","type(normal=0,hard=1,superHard=2)","dda_type","dda_para"] writer = csv.DictWriter(f, fieldnames=fieldnames) writer.writeheader() for lvid in sorted(lv_stat.keys(), key=lambda x:int(x)): d = {"lvid":lvid, "lvinst":"","type(normal=0,hard=1,superHard=2)":"","dda_type":"","dda_para":""} d["lvinst"] = lv_stat[lvid]["关卡 id"] d["type(normal=0,hard=1,superHard=2)"] = lv_stat[lvid]["关卡类型"] d["dda_type"] = lv_stat[lvid]["dda_type"] d["dda_para"] = lv_stat[lvid]["dda_para"] writer.writerow(d) # 模版的信息 with open("levelInstInfo.csv", 'w+') as f: all_insts = set() fieldnames = ["lvinst","template", "try1(tileA|tileB|tileVar|indexTop3|indexPlusByZ|seed)", "try2(tileA|tileB|tileVar|indexTop3|indexPlusByZ|seed)", "try3(tileA|tileB|tileVar|indexTop3|indexPlusByZ|seed)", "try4(tileA|tileB|tileVar|indexTop3|indexPlusByZ|seed)", "try5(tileA|tileB|tileVar|indexTop3|indexPlusByZ|seed)", "try6(tileA|tileB|tileVar|indexTop3|indexPlusByZ|seed)", "try7(tileA|tileB|tileVar|indexTop3|indexPlusByZ|seed)", "try8(tileA|tileB|tileVar|indexTop3|indexPlusByZ|seed)", "try9(tileA|tileB|tileVar|indexTop3|indexPlusByZ|seed)", "try10(tileA|tileB|tileVar|indexTop3|indexPlusByZ|seed)"] writer = csv.DictWriter(f, fieldnames=fieldnames) writer.writeheader() for lvid in sorted(lv_stat.keys(), key=lambda x:int(x)): inst_id = lv_stat[lvid]["关卡 id"] if inst_id not in all_insts: all_insts.add(inst_id) else: continue d = {"lvinst":"", "template":"", "try1(tileA|tileB|tileVar|indexTop3|indexPlusByZ|seed)":"", "try2(tileA|tileB|tileVar|indexTop3|indexPlusByZ|seed)":"", "try3(tileA|tileB|tileVar|indexTop3|indexPlusByZ|seed)":"", "try4(tileA|tileB|tileVar|indexTop3|indexPlusByZ|seed)":"", "try5(tileA|tileB|tileVar|indexTop3|indexPlusByZ|seed)":"", "try6(tileA|tileB|tileVar|indexTop3|indexPlusByZ|seed)":"", "try7(tileA|tileB|tileVar|indexTop3|indexPlusByZ|seed)":"", "try8(tileA|tileB|tileVar|indexTop3|indexPlusByZ|seed)":"", "try9(tileA|tileB|tileVar|indexTop3|indexPlusByZ|seed)":"", "try10(tileA|tileB|tileVar|indexTop3|indexPlusByZ|seed)":""} d["lvinst"] = lv_stat[lvid]["关卡 id"] d["template"] = lv_stat[lvid]["模板 id"] seeds = [] if lvid in lvSeedInfo: seeds = lvSeedInfo[lvid] for tried in range(1,11): k1 = "第" + str(tried) + "次玩(ATile种类数 | 随机种子 | 塔副本幸运值 | BTile种类数)" if tried == 10: k1 = "第10次及之后玩(ATile种类数 | 随机种子 | 塔副本幸运值 | BTile种类数)" if k1 in lv_stat[lvid]: try1 = lv_stat[lvid][k1] tileA = try1.split('|')[0] tileB = try1.split('|')[3] tileCnt = tileA if int(tileA) > int(tileB) else tileB ps=[tileCnt,"0","0","100","100"] if tried <= len(seeds): ps.append(seeds[tried-1]) else: ps.append("100") d["try" + str(tried)+"(tileA|tileB|tileVar|indexTop3|indexPlusByZ|seed)"] = "|".join(ps) else: d["try" + str(tried)+"(tileA|tileB|tileVar|indexTop3|indexPlusByZ|seed)"] = "" writer.writerow(d) # 导出关卡id和模板的对应关系,并输出模板的信息 else: print('ohoh') # 恢复原来的工作目录 os.chdir(current_dir)