# Desc: 将用到的关卡文件压缩成程序使用的protobuf格式 # Date: 2017-04-18 import os import sys import json import levelData_pb2 def read_json(file_path): with open(file_path, 'r') as file: return json.load(file) def write_json(data, file_path): with open(file_path, 'w+') as file: json.dump(data, file, indent=4) # 将 ../conf/levelInstInfo.csv 里面的每一个实例的模板名加上“tf_”前缀 def add_prefix_to_inst_name(): with open('../conf/levelInstInfo.csv', 'r') as f: hdr_done = False with open('../conf/levelInstInfo_new.csv', 'w+') as f_new: for line in f.readlines(): if line.startswith('#'): f_new.write(line) continue if not hdr_done: f_new.write(line) hdr_done = True continue line = line.split(',') if line[1].startswith('tf_'): f_new.write(line) continue line[1] = 'tf_' + line[1] f_new.write(','.join(line)) # 解析 ../conf/levelInstInfo.csv 文件,得到所有被用到的模板列表 def parse_level_inst_info(groups): temp_list = set() for grp in groups: fn = '../conf/levelInstInfo.csv' if len(grp) != 0: fn = '../conf/levelInstInfo-' + grp + '.csv' with open(fn, 'r') as f: # 跳过第一行 f.readline() for line in f.readlines(): if line.startswith('#'): continue temp_list.add(line.split(',')[1]) return temp_list # 将某个tiled的json文件转换成protobuf格式 def conver_json_to_proto(tDir, tFileName, outDir): 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['width'] levelData.tileHeight = jsonData['height'] cntUndefined = 0 tilesByPos = {} maxZ = 0 maxX = 0 maxY = 0 # 首先得到stacked的坐标数据 stacked_map = {} for item in jsonData['layers']: name = item['name'] if name == 'Marks': data = item['data'] for i in range(0, len(data)): if data[i] == 0: continue x = (int)(i % levelData.tileWidth) y = (int)(i / (int)(levelData.tileWidth)) stacked_map[(x, y)] = 0 # 处理各层的tile数据 cntTiles = 0 for item in jsonData['layers']: name = item['name'] if not name.startswith('Tile_'): # stacked continue z = int(name[5:]) data = item['data'] for i in range(0, len(data)): if data[i] == 0: continue tile = levelData.tiles.add() tile.x = (int)(i % levelData.tileWidth) tile.y = (int)(i / (int)(levelData.tileWidth)) tile.z = z if (tile.x, tile.y) in stacked_map: stacked_map[(x, y)] += 1 tiledata = tile.tileData tiledata.zv = 0 tiledata.weight = 0 tiledata.id = data[i] 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 cntTiles += 1 if cntTiles%3 != 0: print('Error: tile count is not a multiple of 3: %d' % (cntTiles,)) exit(1) # 根据上面的布局信息,计算每个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 stacked_map: stack_data = levelData.stacks.add() stack_data.x = int(item[0]) stack_data.y = int(item[1]) stack_data.direction = 0 # 先默认都是0 # 数据序列化 level_protobuf_data = levelData.SerializeToString() protoFile = os.path.join(outDir, tFileName + '.bin') with open(protoFile, 'w+b') as f: f.write(level_protobuf_data) def gen_index_and_data(binDir, group = None): lvelsIndex = levelData_pb2.LevelsIndex() if group is not None: protoDataTrunk = os.path.join(binDir, 'levels-' + str(group) + '.bin') protoDataIndex = os.path.join(binDir, 'levelsIndex-' + str(group) + '.bin') else: protoDataTrunk = os.path.join(binDir, 'levels' + '.bin') protoDataIndex = os.path.join(binDir, 'levelsIndex' + '.bin') offset = 0 for filename in os.listdir(binDir): my_message = levelData_pb2.LevelData() binFile = os.path.splitext(filename)[0] + '.bin' binFile = os.path.join(binDir, binFile) if os.path.exists(binFile) : # print(binFile) with open(binFile, "rb") as f: binary_data = f.read() my_message.ParseFromString(binary_data) os.remove(binFile) 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) # 将所有的模板文件转换成protobuf格式 def convert_all_templates(): # 清空loadable目录 for root, dirs, files in os.walk('../loadable'): for name in files: os.remove(os.path.join(root, name)) temp_list = parse_level_inst_info(['','3']) dirs = ['../tf_templates', '../miniGame', '../templates'] for temp_name in temp_list: print(f"Converting template {temp_name}") # 判断正确的目录 for d in dirs: if os.path.exists(os.path.join(d, temp_name + '.json')): conver_json_to_proto(d, temp_name, '../loadable') break gen_index_and_data('../loadable') # 修改levelInstInfo.csv文件: 对于每一行,如果其try1一直到try10的随机数都是100,则一次将其加1 def correct_random_seed(): for f in ['../conf/levelInstInfo.csv', '../conf/levelInstInfo-2.csv']: lines = None with open(f, 'r') as file: lines = file.readlines() with open(f, 'w') as file: for line in lines: if line.startswith('#'): file.write(line) continue fs = line.split(',') if len(fs) < 12: file.write(line) continue for i in range(2, 12): if fs[i].split('|')[1] == '100': fs[i] = fs[i].replace('100', '%d' % (100+i)) file.write(','.join(fs)) # 将关卡实例每个关卡单独保存一个,避免改动的时候相互影响 def split_level_inst_info(): # 首先读取levelInfo.csv文件,对于每一个关卡,解析其所使用的实例名字信息 # 然后读取levelInstInfo.csv文件,得到每一个实例的详细信息 # 新建一个levelInstInfo-new.csv文件,对于每一个关卡,将其所使用的实例的信息改名成inst-关卡名,并存入上一步的详细信息 # 最后将levelInstInfo-new.csv文件保存 lvs_inst = {} with open('../conf/levelInfo-2.csv', 'r') as file: for line in file.readlines(): if line.startswith('#'): continue fs = line.split(',') lvs_inst[fs[0]] = (fs[1], fs) inst_info = {} with open('../conf/levelInstInfo-2.csv', 'r') as file: for line in file.readlines(): if line.startswith('#'): continue fs = line.split(',') inst_info[fs[0]] = ",".join(fs[1:]) # 更新info with open('../conf/levelInfo-2-new.csv', 'w') as file: for lv, (inst,li) in lvs_inst.items(): li[1] = 'inst-%s' % (lv,) file.write(",".join(li)) # 写入实例信息 with open('../conf/levelInstInfo-2-new.csv', 'w') as file: for lv, (inst,li) in lvs_inst.items(): if inst in inst_info: file.write("inst-%s,%s" % (lv, inst_info[inst])) else: print(f"Error: {inst} not found in levelInstInfo.csv") pass # 更新关卡实例的随机值 def updateRandomSeed(lvInfoFN, instInfoFN, seedsInfo): # seedsInfo每一行的格式:[Tile] resovler id: 31,37710|539110|14697|465925|662106|151463|667120|234313|60714|840794| # 解析seedsInfo,得到关卡和实例的时机值 seeds = {} for line in seedsInfo.split('\n'): fs = line.split(' ') if len(fs) < 4: continue fs = fs[3].split(',') seeds[fs[0]] = fs[1].split('|') # 先通过lvInfoFN文件,得到所有的关卡对应的实例名称 seeds4Inst = {} with open(lvInfoFN, 'r') as file: for line in file.readlines(): if line.startswith('#'): continue fs = line.split(',') if fs[0] in seeds: seeds4Inst[fs[1]] = seeds[fs[0]] # 对于instInfoFN文件,对于每一行,如果lvid在seeds里面,则用上面的随机值来更新一下;否则原封不动写入新文件 with open(instInfoFN, 'r') as infile: lines = infile.readlines() with open(instInfoFN + '-new', 'w') as outfile: for line in lines: fs = line.split(',') if len(fs) < 2: outfile.write(line) continue if fs[0] in seeds4Inst: for i in range(2, 12): ps = fs[i].split('|') ps[1] = seeds4Inst[fs[0]][i-2] fs[i] = '|'.join(ps) outfile.write(','.join(fs)) # 将instInfoFN-new文件重命名为instInfoFN os.rename(instInfoFN + '-new', instInfoFN) pass if __name__ == '__main__': # 获取当前工作目录 current_dir = os.getcwd() script_dir = os.path.dirname(os.path.abspath(__file__)) os.chdir(script_dir) # 得到可以发布的protobuf文件 #convert_all_templates() # correct_random_seed() # split_level_inst_info() # 更新随机信息 seedsInfo = """ [Tile] resovler id: 1,741994|782049|245401|586494|585515|844935|473832|560738|37700|334867| [Tile] resovler id: 2,673339|884121|575932|529672|871572|33514|756797|556553|383379|845974| [Tile] resovler id: 3,398075|153640|619852|832136|393503|20714|358987|110175|411774|542299| [Tile] resovler id: 4,712341|324580|941555|467896|494440|62707|399955|283406|639585|175493| [Tile] resovler id: 5,280717|189716|80411|853095|705220|546715|147854|523319|77937|314125| [Tile] resovler id: 6,564790|841391|971530|892729|188546|644199|696504|189275|48872|116772| [Tile] resovler id: 7,777849|331638|327732|602887|464807|895902|902564|68139|718117|67306| [Tile] resovler id: 8,987217|760748|96643|112824|570189|213610|462739|286855|562912|629566| [Tile] resovler id: 9,455598|453585|48738|28577|902432|675019|7499|885812|333875|598762| [Tile] resovler id: 10,530104|137449|879593|710229|245086|713169|262496|927742|112079|803424| [Tile] resovler id: 11,665940|847758|689087|49328|524860|728633|378918|855388|947680|598144| [Tile] resovler id: 12,533285|573613|226767|444329|137922|287942|854151|91001|209449|632312| [Tile] resovler id: 13,361958|2210|647076|680115|146546|129620|521030|303552|420691|158391| [Tile] resovler id: 14,77623|200485|352885|240955|190714|505685|436547|704161|832960|792325| [Tile] resovler id: 15,940941|130963|299015|526270|372169|353500|603281|507977|924894|335899| [Tile] resovler id: 16,559609|527858|24507|186088|353521|716308|834310|525708|458887|331968| [Tile] resovler id: 17,676321|843974|928908|264904|289351|857467|546935|328708|298631|374807| [Tile] resovler id: 18,31296|376484|257141|323041|542832|205748|45980|698444|431287|674208| [Tile] resovler id: 19,930135|522800|253430|831862|868088|783176|335919|374418|419249|181204| [Tile] resovler id: 20,455247|769048|409068|208122|185371|510388|503733|971820|846701|354383| [Tile] resovler id: 21,813311|783923|694902|753811|501860|472671|60359|786797|139673|345116| [Tile] resovler id: 22,694891|232972|455781|545755|795491|634323|756211|402966|509917|676323| [Tile] resovler id: 23,520833|681401|763437|851187|605886|786625|702376|516798|540748|90732| [Tile] resovler id: 24,111958|141292|938262|917249|422143|490826|257325|172344|42726|192422| [Tile] resovler id: 25,279272|877884|426040|838509|635941|222308|674208|75601|682109|793416| [Tile] resovler id: 26,655715|808350|41341|497273|297963|451667|617768|825016|222230|954515| [Tile] resovler id: 27,862461|274898|302007|669673|788609|715031|400599|454925|604808|891875| [Tile] resovler id: 28,953105|193904|867021|585873|571105|456798|76809|726947|975077|226670| [Tile] resovler id: 29,943954|495078|521909|778146|642957|513735|753960|51905|194416|232135| [Tile] resovler id: 30,134069|327536|931813|243629|195985|601044|977382|503294|47141|804275| [Tile] resovler id: 31,37710|539110|14697|465925|662106|151463|667120|234313|60714|840794| [Tile] resovler id: 32,593205|634340|958793|573271|383911|340306|242516|634033|898667|543886| [Tile] resovler id: 33,367610|560109|408542|183872|22081|141640|657756|727536|136080|687632| [Tile] resovler id: 34,477682|959591|121778|99899|399009|422919|900992|777701|991801|419563| [Tile] resovler id: 35,622027|417733|853517|554771|327225|935566|147369|680205|421683|612060| [Tile] resovler id: 36,729062|407063|538743|952108|606011|732614|690807|445646|572511|438099| [Tile] resovler id: 37,632522|385290|905640|371743|290561|498417|391781|940609|571385|90211| [Tile] resovler id: 38,170333|571685|978381|332382|305518|598214|16112|474203|690524|205857| [Tile] resovler id: 39,618601|699482|575303|25315|334114|501904|436188|947954|955591|894621| [Tile] resovler id: 40,522993|397185|399491|566062|684047|401688|651392|78906|913301|27998| [Tile] resovler id: 41,967717|701069|289445|724583|199114|723827|491676|237734|505409|286152| [Tile] resovler id: 42,516555|863942|694354|558588|535895|551956|299334|185801|342688|995820| [Tile] resovler id: 43,13042|509566|158747|308094|411446|379696|202985|613109|469669|97125| [Tile] resovler id: 44,473254|794981|962880|331739|253120|791682|876221|358862|295101|319777| [Tile] resovler id: 45,873172|562727|718642|338860|462551|796609|628745|136923|464748|329762| [Tile] resovler id: 46,758477|506785|82081|249014|6453|901498|675435|717693|882069|627399| [Tile] resovler id: 47,832961|254122|353220|126552|188723|965968|145410|742039|327848|62456| [Tile] resovler id: 48,787090|458228|720595|591408|765640|484171|573153|986987|673973|165149| [Tile] resovler id: 49,619003|375498|950166|611038|449557|852723|168237|956036|682230|363220| [Tile] resovler id: 50,978466|675863|444711|133748|598425|852067|799426|993761|249127|514111| """ updateRandomSeed('../conf/levelInfo-3.csv', '../conf/levelInstInfo-3.csv', seedsInfo) # 恢复当前工作目录 os.chdir(current_dir)