import os import csv from io import StringIO # function: read_text_file def read_text_file(file_path): """尝试用不同编码读取文本文件""" encodings = ['utf-8', 'gbk', 'gb2312', 'latin-1'] for encoding in encodings: try: with open(file_path, 'r', encoding=encoding) as file: return file.read() except UnicodeDecodeError: continue except Exception as e: print(f"用编码 {encoding} 读取时出错: {e}") return None # function: parse_csv_to_dict def parse_csv_to_dict(csv_str): """将CSV字符串解析为字典列表""" string_io = StringIO(csv_str) csv_reader = csv.DictReader(string_io) data = [] for row in csv_reader: data.append(dict(row)) # 转换为普通字典 return data def has_content(s): return s is not None and len(s) > 0 # function: calc_story_end_idx def calc_story_range_list(csv_data): story_ranges = [] for i, item_data in enumerate(csv_data): if has_content(item_data['关卡']): story_ranges.append(i) story_ranges.append(len(csv_data)) story_range_list = [] for i in range(len(story_ranges)-1): story_range_list.append({'start': story_ranges[i], 'end': story_ranges[i+1]}) return story_range_list # functino: make_story_data def make_story_data(csv_data, start_idx, end_idx): start_csv_line = csv_data[start_idx] story_data = { 'id': start_csv_line['ID'], 'level': start_csv_line['关卡'], 'task_title': start_csv_line['聊天任务标题'], 'story_type': start_csv_line['剧情分类'], 'B1_add_heart': start_csv_line['分支1好感度'], 'B1_reward': start_csv_line['分支1奖励'], 'B1_scripts': [], 'B2_add_heart': start_csv_line['分支2好感度'], 'B2_reward': start_csv_line['分支2奖励'], 'B2_scripts': [], 'B3_add_heart': start_csv_line['分支3好感度'], 'B3_reward': start_csv_line['分支3奖励'], 'B3_scripts': [], } idx = 0 for i in range(start_idx, end_idx): csv_line = csv_data[i] # 分支1 story_data['B1_scripts'].append({'idx': idx, 'name': csv_line['分支1角色'], 'script': csv_line['分支1台词']}) # if csv_line['分支1角色'] != '' and csv_line['分支1台词'] != '': # story_data['B1_scripts'].append({'idx': idx, 'name': csv_line['分支1角色'], 'script': csv_line['分支1台词']}) # 分支2 story_data['B2_scripts'].append({'idx': idx, 'name': csv_line['分支2角色'], 'script': csv_line['分支2台词']}) # if csv_line['分支2角色'] != '' and csv_line['分支2台词'] != '': # story_data['B2_scripts'].append({'idx': idx, 'name': csv_line['分支2角色'], 'script': csv_line['分支2台词']}) # 分支3 story_data['B3_scripts'].append({'idx': idx, 'name': csv_line['分支3角色'], 'script': csv_line['分支3台词']}) # if csv_line['分支3角色'] != '' and csv_line['分支3台词'] != '': # story_data['B3_scripts'].append({'idx': idx, 'name': csv_line['分支3角色'], 'script': csv_line['分支3台词']}) idx = idx + 1 return story_data # functino: make_language_key g_language_strs = {} def make_language_key(idx, str): key = "ST%s"%(idx) g_language_strs[key] = str return key # functino: get_language_name_key g_actor_info_dict = [ {'NameKey': 'player', 'Img': 'player', 'Name': '玩家'}, {'NameKey': 'actor_nvshen', 'Img': 'HeroDraw/Npc_01', 'Name': '女神'}, {'NameKey': 'actor_weier', 'Img': 'HeroDraw/Npc_02', 'Name': '薇儿'}, {'NameKey': 'actor_xifu', 'Img': 'HeroDraw/Npc_03', 'Name': '希芙'}, {'NameKey': 'actor_furuiya', 'Img': 'HeroDraw/Npc_04', 'Name': '芙蕾雅'}, {'NameKey': 'actor_yideng', 'Img': 'HeroDraw/Npc_05', 'Name': '伊登'}, {'NameKey': 'actor_haila', 'Img': 'HeroDraw/Npc_06', 'Name': '海拉'}, {'NameKey': 'actor_waerjili', 'Img': 'HeroDraw/Npc_07', 'Name': '瓦尔基里'} ] def get_actor_info(name): for value in g_actor_info_dict: if value['Name'] == name: return value print("[get_actor_info] no find actor info name: " + name) return None # functino: create_story_cfg def create_story_cfg(story_id, story_type, story_selection_texts, story_selection_next, story_force_guide_group, story_condition): return { 'Id': int(story_id), 'StoryId': int(story_id), 'PreStoryId': 0, 'Type': int(story_type), 'SelectionTexts': story_selection_texts, 'SelectionNext': story_selection_next, 'NeedBg': False, 'HideUi': False, 'Transition': False, 'TransitionType': 3, 'Skip': False, 'PlayInterval': 5, 'ProgramControl': True, 'NeedSave': len(story_selection_texts) > 0, 'ForceGuideGroup': story_force_guide_group, 'Condition': story_condition, 'Group': '', 'DataCheck': '' } # function: create_stroyPerformCfg, act_type: 'RoleMovein', 'RoleSpeaking', 'RoleMoveout' def create_stroyPerformCfg(script_info_list, story_id, story_type, start_idx, end_idx): cfgs = [] idx = 0 if start_idx <= end_idx: for script_info in script_info_list[start_idx:end_idx+1]: if has_content(script_info['script']): idx += 1 story_perform_id = "%s%d" % (story_id, idx) SpeakSite = 1 SpeakId = make_language_key(story_perform_id, script_info['script']) LeftName = '' LeftImg = '' LeftAct = '' RightName = '' RightImg = '' RightAct = '' actor_info = get_actor_info(script_info['name']) if actor_info == None: print("ERR: not find actor name, story_id=" + story_id) if actor_info['NameKey'] == 'player': SpeakSite = 2 LeftName = '' LeftImg = '' LeftAct = 'RoleMoveout' RightName = actor_info['NameKey'] RightImg = actor_info['Img'] RightAct = 'RoleMovein' else: SpeakSite = 1 LeftName = actor_info['NameKey'] LeftImg = actor_info['Img'] LeftAct = 'RoleMovein' RightName = '' RightImg = '' RightAct = 'RoleMoveout' cfgs.append({ 'Id': story_perform_id, 'StoryId': story_id, 'Type': 0, 'LeftAct': LeftAct, 'LeftActBgTime': 0, 'RightAct': RightAct, 'RightActBgTime': 0, 'LeftName': LeftName, 'LeftNameColour': 'title02', 'LeftImg': LeftImg, 'RightName': RightName, 'RightNameColour': 'title01', 'RightImg': RightImg, 'SpeakSite': SpeakSite, 'SpeakId': SpeakId, 'SpeakTime': 0, 'WordTime': 10, 'Camp': '', 'CfgId': '', 'Dir': '', 'DirStartTime': '', 'AniName': '', 'AniStartTime': '', 'Eff': '', 'EffStartTime': '', 'OverTime': 0 }) # 如果是对话框对话,就要在首尾添加入场配置和退场配置 if story_type == 0: story_perform_id = "%s%d" % (story_id, 0) LeftName = cfgs[0]['LeftName'] LeftImg = cfgs[0]['LeftImg'] RightName = cfgs[0]['RightName'] RightImg = cfgs[0]['RightImg'] cfgs.insert(0, { 'Id': story_perform_id, 'StoryId': story_id, 'Type': 0, 'LeftAct': 'RoleMovein', 'LeftActBgTime': 0, 'RightAct': 'RoleMovein', 'RightActBgTime': 0, 'LeftName': LeftName, 'LeftNameColour': 'title02', 'LeftImg': LeftImg, 'RightName': RightName, 'RightNameColour': 'title01', 'RightImg': RightImg, 'SpeakSite': 0, 'SpeakId': '', 'SpeakTime': 0, 'WordTime': 10, 'Camp': '', 'CfgId': '', 'Dir': '', 'DirStartTime': '', 'AniName': '', 'AniStartTime': '', 'Eff': '', 'EffStartTime': '', 'OverTime': 0 }) story_perform_id = "%s%d" % (story_id, len(cfgs)) LeftName = cfgs[-1]['LeftName'] LeftImg = cfgs[-1]['LeftImg'] RightName = cfgs[-1]['RightName'] RightImg = cfgs[-1]['RightImg'] cfgs.append({ 'Id': story_perform_id, 'StoryId': story_id, 'Type': 0, 'LeftAct': 'RoleMoveout', 'LeftActBgTime': 0, 'RightAct': 'RoleMoveout', 'RightActBgTime': 0, 'LeftName': LeftName, 'LeftNameColour': 'title02', 'LeftImg': LeftImg, 'RightName': RightName, 'RightNameColour': 'title01', 'RightImg': RightImg, 'SpeakSite': 0, 'SpeakId': '', 'SpeakTime': 0, 'WordTime': 10, 'Camp': '', 'CfgId': '', 'Dir': '', 'DirStartTime': '', 'AniName': '', 'AniStartTime': '', 'Eff': '', 'EffStartTime': '', 'OverTime': 0 }) for i, cfg_item in enumerate(cfgs): if cfg_item == None: print("----cfg_item is None-----") print(cfg_item) return cfgs # functino: make_story_cfg def make_story_cfg(story_cfgs, stroyPerformCfgV2_cfgs, story_data): ID = story_data['id'] level = story_data['level'] story_type = 0 if story_data['story_type'] == '对话': story_type = 0 elif story_data['story_type'] == '聊天': story_type = 1 story_force_guide_group = 0 story_condition = '' has_multi_options = False for i in range(len(story_data['B1_scripts'])): if has_content(story_data['B1_scripts'][i]['script']) and has_content(story_data['B2_scripts'][i]['script']) and has_content(story_data['B3_scripts'][i]['script']): story_id = "%s" % (ID) story1_id = "%s01" % (ID) story2_id = "%s02" % (ID) story3_id = "%s03" % (ID) story1_opt_1 = make_language_key(story1_id, story_data['B1_scripts'][i]['script']) story1_opt_2 = make_language_key(story2_id, story_data['B2_scripts'][i]['script']) story1_opt_3 = make_language_key(story3_id, story_data['B3_scripts'][i]['script']) story1_selection_texts = "%s;%s;%s" % (story1_opt_1, story1_opt_2, story1_opt_3) story1_selection_next = "%s;%s;%s" % (story1_id, story2_id, story3_id) story_cfgs.append(create_story_cfg(story_id, story_type, story1_selection_texts, story1_selection_next, story_force_guide_group, story_condition)) story_cfgs.append(create_story_cfg(story1_id, story_type, '', '', 0, '')) story_cfgs.append(create_story_cfg(story2_id, story_type, '', '', 0, '')) story_cfgs.append(create_story_cfg(story3_id, story_type, '', '', 0, '')) stroyPerformCfgV2_cfgs.extend(create_stroyPerformCfg(story_data['B1_scripts'], story_id, story_type, 0, i-1)) stroyPerformCfgV2_cfgs.extend(create_stroyPerformCfg(story_data['B1_scripts'], story1_id, story_type, i+1, len(story_data['B1_scripts'])-1)) stroyPerformCfgV2_cfgs.extend(create_stroyPerformCfg(story_data['B2_scripts'], story2_id, story_type, i+1, len(story_data['B2_scripts'])-1)) stroyPerformCfgV2_cfgs.extend(create_stroyPerformCfg(story_data['B3_scripts'], story3_id, story_type, i+1, len(story_data['B3_scripts'])-1)) has_multi_options = True break if has_content(story_data['B1_scripts'][i]['script']) and has_content(story_data['B2_scripts'][i]['script']) and has_content(story_data['B3_scripts'][i]['script']) == False: story_id = "%s" % (ID) story1_id = "%s01" % (ID) story2_id = "%s02" % (ID) story1_opt_1 = make_language_key(story1_id, story_data['B1_scripts'][i]['script']) story1_opt_2 = make_language_key(story2_id, story_data['B2_scripts'][i]['script']) story1_selection_texts = "%s;%s" % (story1_opt_1, story1_opt_2) story1_selection_next = "%s;%s" % (story1_id, story2_id) story_cfgs.append(create_story_cfg(story_id, story_type, story1_selection_texts, story1_selection_next, story_force_guide_group, story_condition)) story_cfgs.append(create_story_cfg(story1_id, story_type, '', '', 0, '')) story_cfgs.append(create_story_cfg(story2_id, story_type, '', '', 0, '')) stroyPerformCfgV2_cfgs.extend(create_stroyPerformCfg(story_data['B1_scripts'], story_id, story_type, 0, i-1)) stroyPerformCfgV2_cfgs.extend(create_stroyPerformCfg(story_data['B1_scripts'], story1_id, story_type, i+1, len(story_data['B1_scripts'])-1)) stroyPerformCfgV2_cfgs.extend(create_stroyPerformCfg(story_data['B2_scripts'], story2_id, story_type, i+1, len(story_data['B2_scripts'])-1)) has_multi_options = True break if has_multi_options == False: story_id = "%s" % (ID) story_cfgs.append(create_story_cfg(story_id, story_type, '', '', story_force_guide_group, story_condition)) stroyPerformCfgV2_cfgs.extend(create_stroyPerformCfg(story_data['B1_scripts'], story_id, story_type, 0, len(story_data['B1_scripts'])-1)) # function: # generate_StoryCfgV2 def generate_StoryCfgV2(out_path, data): with open(out_path+'/StoryCfgV2.csv', 'w', newline='', encoding='utf-8') as file: fieldnames = ['序列ID', '触发剧情ID', '前置剧情', '剧情类型(0是对话,其他ChatNpcId)', '选项显示', '继续下一个剧情', '是否底图', '是否隐藏UI', '是否转场', '转场类型', '是否可跳过', '剧情播放间隔时间(s)', '是否需要程序驱动', '是否存盘', '强制引导', '触发条件', '同组', '数据判断'] row1 = ['Id', 'StoryId', 'PreStoryId', 'Type', 'SelectionTexts', 'SelectionNext', 'NeedBg', 'HideUi', 'Transition', 'TransitionType', 'Skip', 'PlayInterval', 'ProgramControl', 'NeedSave', 'ForceGuideGroup', 'Condition', 'Group', 'DataCheck'] row2 = ['int', 'int', 'int', 'int', 'list', 'list', 'bool', 'bool', 'bool', 'int', 'bool', 'int', 'bool', 'bool', 'int', 'list', 'list', 'list'] csv_data = [] csv_data.append(fieldnames) csv_data.append(row1) csv_data.append(row2) for i, data_item in enumerate(data): csv_data.append(data_item.values()) writer = csv.writer(file) writer.writerows(csv_data) #print("\n--- StoryCfgV2.csv ---") #print(csv_data) # generate_StroyPerformCfgV2 def generate_StroyPerformCfgV2(out_path, data): with open(out_path+'/StroyPerformCfgV2.csv', 'w', newline='', encoding='utf-8') as file: fieldnames = ['序列ID', '剧情ID', '类型', '左侧行为', '左侧行为开始时间', '右侧行为', '右侧行为时间', '左侧姓名', '左侧姓名颜色', '左侧半身像', '右侧姓名', '右侧姓名颜色', '阵营', '配置ID', '面向', '面向开始时间', '动作名字', '动作开始时间', '特效ID', '特效开始时间', '结束时间'] row1 = ['Id', 'StoryId', 'Type', 'LeftAct', 'LeftActBgTime', 'RightAct', 'RightActBgTime', 'LeftName', 'LeftNameColour', 'LeftImg', 'RightName', 'RightNameColour', 'RightImg', 'SpeakSite', 'SpeakId', 'SpeakTime', 'WordTime', 'Camp', 'CfgId', 'Dir', 'DirStartTime', 'AniName', 'AniStartTime', 'Eff', 'EffStartTime', 'OverTime'] row2 = ['int', 'int', 'int', 'string', 'int', 'string', 'int', 'string', 'string', 'string', 'string', 'string', 'string', 'int', 'string', 'int', 'int', 'list', 'list', 'list', 'list', 'list', 'list', 'list', 'list', 'int'] csv_data = [] csv_data.append(fieldnames) csv_data.append(row1) csv_data.append(row2) print("\n--- StroyPerformCfgV2.csv ---") for i, data_item in enumerate(data): if data_item == None: print("data_item: None " + str(i)) csv_data.append(data_item.values()) writer = csv.writer(file) writer.writerows(csv_data) # generate_LanguagePackageV2_cn def generate_LanguagePackageV2_cn(out_path, data): with open(out_path+'/../../../Assets/Content/Config/LanguagePackageV2_cn.csv', 'w', newline='', encoding='utf-8') as file: fieldnames = ['key', '内容'] row1 = ['key', 'Language'] row2 = ['string', 'string'] csv_data = [] csv_data.append(fieldnames) csv_data.append(row1) csv_data.append(row2) for actor_info in g_actor_info_dict: csv_data.append([actor_info['NameKey'], actor_info['Name']]) for key, value in data.items(): csv_data.append([key, value]) writer = csv.writer(file) writer.writerows(csv_data) print("\n--- LanguagePackageV2_cn.csv ---") #print(csv_data) # functino: generate_config def generate_config(story_data_list, out_path): StoryCfgV2_lines = [] StroyPerformCfgV2_lines = [] LanguagePackage_cn_lines = [] for i, story_data in enumerate(story_data_list): make_story_cfg(StoryCfgV2_lines, StroyPerformCfgV2_lines, story_data) generate_StoryCfgV2(out_path, StoryCfgV2_lines) generate_StroyPerformCfgV2(out_path, StroyPerformCfgV2_lines) generate_LanguagePackageV2_cn(out_path, g_language_strs) # main入口 if __name__ == "__main__": g_script_dir = os.path.dirname(os.path.abspath(__file__)) g_input_dir = os.path.join(g_script_dir, 'input') g_out_dir = os.path.join(g_script_dir, 'output') g_input_dir = g_input_dir.replace('\\', '/') g_out_dir = g_out_dir.replace('\\', '/') print('g_input_dir: ' + g_input_dir) print('g_out_dir: ' + g_out_dir) g_story_data_list = [] for filename in os.listdir(g_input_dir): if filename.endswith('.csv'): file_path = os.path.join(g_input_dir, filename) file_content = read_text_file(file_path) if file_content is not None: print(f"=== {filename} ===") # 解析csv数据为Dict csv_data = parse_csv_to_dict(file_content) # 把csv数据分隔为一段段剧情,并生成剧情数据列表:story_data_list story_range_list = calc_story_range_list(csv_data) for i, story_range in enumerate(story_range_list): story_data = make_story_data(csv_data, story_range['start'], story_range['end']) g_story_data_list.append(story_data) print("--- story_data ---") print(story_data) print() # 根据story_data_list生成配置文件 generate_config(g_story_data_list, g_out_dir) else: print(f"无法读取文件: {filename}")