123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354 |
- # -*- 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)
- 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)
- 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()
- # 生成一些关卡的体验参数模板,在关卡实例中对应这个里面来
- with open("./loadable/paramTemplates.csv", 'w+') as f:
- fieldnames = ["name","tilevar","topN","matchRInTopN","maxUmZ","umMatchRs"]
- writer = csv.DictWriter(f, fieldnames=fieldnames)
- writer.writeheader()
- templates = {}
- # 容易:首层即有明显和高的匹配率,每一层的为匹配率也比较高
- writer.writerow({"name":"easy",
- "tilevar":"3",
- "topN":"1",
- "matchRInTopN":"50",
- "maxUmZ":"2",
- "umMatchRs":"40"})
- # 普通:首层的匹配率一般,到第二层的匹配率较高,后面的匹配率一般
- writer.writerow({"name":"normal",
- "tilevar":"3",
- "topN":"1",
- "matchRInTopN":"30",
- "maxUmZ":"3",
- "umMatchRs":"60|20|20|20|20"})
- # 普通.前难后易:首部2层的匹配率一般,但是后面的匹配率高
- writer.writerow({"name":"normal.hard_easy",
- "tilevar":"3",
- "topN":"2",
- "matchRInTopN":"60",
- "maxUmZ":"3",
- "umMatchRs":"40|40|40|40|40"})
- # 难:前两层的匹配率较高,后面的匹配率一般,同时tile的分布极端一点
- writer.writerow({"name":"hard",
- "tilevar":"4",
- "topN":"2",
- "matchRInTopN":"30",
- "maxUmZ":"4",
- "umMatchRs":"20|20|20|20|20"})
-
- # 将关卡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("./loadable/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)
- # 实例模版的信息
- hdr4try = "(tileCnt|seed|param_template)"
- with open("./loadable/levelInstInfo.csv", 'w+') as f:
- all_insts = set()
- fieldnames = ["lvinst","template"]
- for tried in range(1,11):
- k = "try" + str(tried)+hdr4try
- fieldnames.append(k)
- 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":""}
- for tried in range(1,11):
- k = "try" + str(tried)+hdr4try
- d[k] = ""
- 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
- # 默认都是normal的体验模式
- ps=[tileCnt,]
- if tried <= len(seeds):
- ps.append(seeds[tried-1])
- else:
- ps.append("100")
- ps.append("normal")
- d["try" + str(tried)+hdr4try] = "|".join(ps)
- else:
- d["try" + str(tried)+hdr4try] = ""
- writer.writerow(d)
- # 导出关卡id和模板的对应关系,并输出模板的信息
- else:
- print('ohoh')
- # 恢复原来的工作目录
- os.chdir(current_dir)
|