# -*- coding: utf-8 -*- """ 生成《不朽封仙》游戏文字脚本文档 """ import csv import os import glob from docx import Document from docx.shared import Pt, Cm, Inches from docx.enum.text import WD_ALIGN_PARAGRAPH from docx.enum.table import WD_TABLE_ALIGNMENT from docx.oxml.ns import qn # ============ 路径配置 ============ CSV_DIR = r"d:\p-L12\work\cvs" GAME_DIR = r"d:\p-L12\P-L12_game\unity\Assets\Resources\Resources_moved\gui\atlas_ui" ICON_DIRS = { "product": os.path.join(GAME_DIR, "atlas_product_icons"), "skill": os.path.join(GAME_DIR, "atlas_skill"), "rune": os.path.join(GAME_DIR, "atlas_rune_icons"), "talent": os.path.join(GAME_DIR, "atlas_talent_icons"), "equip": os.path.join(GAME_DIR, "atlas_equip_icons"), } OUTPUT_PATH = r"d:\p-L12\work\《不朽封仙》游戏文字脚本.docx" # ============ 辅助函数 ============ def read_csv(filename): """读取CSV文件,跳过前3行(中文头、类型头、英文头),返回数据行""" path = os.path.join(CSV_DIR, filename) # 尝试多种编码 for encoding in ["utf-8-sig", "gbk", "gb2312", "utf-8"]: try: with open(path, "r", encoding=encoding) as f: reader = csv.reader(f) rows = list(reader) return rows[3:] # skip header rows except (UnicodeDecodeError, UnicodeError): continue raise ValueError(f"无法读取文件 {filename},尝试了所有编码") def find_image(icon_name, icon_dir_key): """根据icon名称查找图片文件路径,返回完整路径或None""" icon_dir = ICON_DIRS.get(icon_dir_key) if not icon_dir or not os.path.isdir(icon_dir): return None # 尝试直接匹配 for ext in [".png", ".jpg"]: p = os.path.join(icon_dir, icon_name + ext) if os.path.isfile(p): return p # 尝试补零匹配(如 icon_product_1 -> icon_product_01) import re m = re.match(r"^(.+?)(\d+)$", icon_name) if m: prefix, num = m.group(1), m.group(2) for pad in [2, 3]: padded = prefix + num.zfill(pad) for ext in [".png", ".jpg"]: p = os.path.join(icon_dir, padded + ext) if os.path.isfile(p): return p return None def add_image_to_cell(cell, image_path, height_cm=3): """在表格单元格中插入图片,锁定纵横比,设置高度""" paragraph = cell.paragraphs[0] paragraph.alignment = WD_ALIGN_PARAGRAPH.CENTER run = paragraph.add_run() run.add_picture(image_path, height=Cm(height_cm)) def set_cell_text(cell, text): """设置单元格文本""" cell.text = "" paragraph = cell.paragraphs[0] paragraph.alignment = WD_ALIGN_PARAGRAPH.CENTER run = paragraph.add_run(str(text)) run.font.size = Pt(10) def add_table_with_icons(doc, headers, rows_data, icon_dir_key): """添加带图标的表格""" table = doc.add_table(rows=1, cols=len(headers), style="Table Grid") table.alignment = WD_TABLE_ALIGNMENT.CENTER # 设置表头 for i, header in enumerate(headers): cell = table.rows[0].cells[i] cell.text = header for paragraph in cell.paragraphs: paragraph.alignment = WD_ALIGN_PARAGRAPH.CENTER for run in paragraph.runs: run.font.bold = True run.font.size = Pt(10) # 添加数据行 for row_data in rows_data: row = table.add_row() name, icon_name, desc = row_data[0], row_data[1], row_data[2] if len(row_data) > 2 else "" set_cell_text(row.cells[0], name) # 图标列 img_path = find_image(icon_name, icon_dir_key) if icon_name else None if img_path: add_image_to_cell(row.cells[1], img_path) else: set_cell_text(row.cells[1], "") # 描述列 if desc: set_cell_text(row.cells[2], desc) def add_table_equip(doc, headers, rows_data, icon_dir_key): """添加装备表格(只有名称和图标两列)""" table = doc.add_table(rows=1, cols=len(headers), style="Table Grid") table.alignment = WD_TABLE_ALIGNMENT.CENTER for i, header in enumerate(headers): cell = table.rows[0].cells[i] cell.text = header for paragraph in cell.paragraphs: paragraph.alignment = WD_ALIGN_PARAGRAPH.CENTER for run in paragraph.runs: run.font.bold = True run.font.size = Pt(10) for row_data in rows_data: row = table.add_row() name, icon_name = row_data[0], row_data[1] set_cell_text(row.cells[0], name) img_path = find_image(icon_name, icon_dir_key) if icon_name else None if img_path: add_image_to_cell(row.cells[1], img_path) else: set_cell_text(row.cells[1], "") # ============ 数据准备 ============ def get_system_prompts(): """获取被引用的GameTextConst系统提示""" # 所有GameTextConst常量及其文本值 all_consts = { "ErrorTaskFailed": "领取失败。", "ErrorBuyFailed": "购买失败。", "ErrorUseFailed": "使用失败。", "ErrorTakesuccess": "领取成功。", "ErrorStrengthLimit": "强化已达上限。", "ErrorChallengeTimesNotEnough": "今日挑战次数不足。", "ErrorRecruitTimesNotEnough": "今日可招募次数不足。", "ErrorRecruitTicketNotEnough": "召唤道具与月灵玉璧不足。", "ErrorRecruitDiamondNotEnough": "星棱幻晶不足,无法进行招募。", "ErrorUpStageFirst": "请先升阶。", "ErrorUpStage1First": "请先升品。", "ErrorUpLevelFirst": "请先升级。", "ErrorUpStarFirst": "请先升星。", "ErrorUpStrengthFirst": "请先强化。", "ErrorLevelNotExist": "关卡不存在。", "ErrorInvalidGuideId": "无效的引导。", "ErrorItemNotEnough": "道具不足。", "ErrorRewardAlreadyClaimed": "您已领奖。", "ErrorNotSettlementTime": "未到结算时间。", "ErrorNoReward": "暂无奖励。", "ErrorHeroStarUpSuccess": "升星成功。", "ErrorTujianProgressNotEnough": "图鉴进度未达到领取条件。", "ErrorUpLevelSuccess": "升级成功。", "ErrorUpStageSuccess": "升阶成功。", "ErrorGuildSetSuccess": "设置公会成功。", "ErrorNoPackagePurchased": "未购买礼包。", "ErrorNotMeetClaimCondition": "暂未达到领取条件。", "ErrorNameNotAvailable": "含有敏感字符,无法使用该名字。", "ErrorNameOnlyChinese": "昵称只能包含中文。", "ErrorPleaseInputName": "请输入昵称。", "ErrorSetSuccess": "修改成功。", "ErrorCurrencyNotEnough": "货币不足。", "ErrorHeroMaxCountReached": "伙伴达到最大人数。", "ErrorNoHeroToCombine": "没有可合成的伙伴。", "ErrorJoinSuccess": "加入成功。", "ErrorApplySuccess": "申请成功。", "ErrorEnergyNotEnough": "体力不足。", "ErrorBuySuccess": "购买成功。", "ErrorQuitGuildSuccess": "退出公会成功。", "ErrorGuildNameEmpty": "公会名称不能为空。", "ErrorGuildNameTooLong": "公会名称不能超过6个字符。", "ErrorCreateGuildSuccess": "创建公会成功。", "ErrorMaterialNotEnough": "材料不足。", "ErrorEquipStrengthSuccess": "装备强化成功。", "ErrorEquipBreakthroughSuccess": "装备升品成功。", "ErrorStrengthSuccess": "强化成功。", "ErrorDiamondNotEnough": "星棱幻晶不足。", "ErrorQuickSweepTimesNotEnough": "快速游历次数不足。", "ErrorCopySuccess": "复制成功。", "ErrorReceiveConditionNotMet": "未达成领取条件。", "ErrorAlreadyClaimed": "已领取。", "ErrorItemDailyUseTimesReached": "该道具今日使用次数已达上限。", "ErrorConditionNotMet": "条件不足。", "ErrorVipLevelNotEnough": "权益等级不足。", "ErrorLevelNotEnough": "等级不足。", "ErrorScoreNotEnough": "积分不足。", "ErrorPassNotOpen": "关卡未开放。", "ErrorPreyFailed": "祈愿失败。", "ErrorInvalidId": "无效的ID。", "ErrorSaveSuccess": "保存成功。", "ErrorSaveFailed": "保存失败。", "ErrorNoHeroCarry": "无上阵伙伴。", "ErrorNoHeroInTeam": "至少上阵一名修士。", "ErrorNoFreePos": "人数已达上限。", "ErrorRefreshTimesNotEnough": "刷新次数不足。", "ErrorTimesNotEnough": "次数不足。", "ErrorHeroLevelUnlock": "修士{0}级解锁。", "TipChallengeTimesConfirm": "\u3000是否消耗{0}进行挑战。\n\u3000今日剩余({1}/{2})次。", "ErrorFusionRuneCountNotEnough": "合成铭文所需数量不足。", "TipFusionSuccess": "合成成功。", "ErrorFusionRuneRarityNotSame": "合成铭文必须是相同稀有度。", "ErrorFusionRuneCountMax": "合成铭文所需数量已达上限。", "ErrorFusionRuneNotFusion": "该铭文不能合成。", "ErrorRunePutOnSuccess": "镶嵌成功。", "ErrorTitleNotUnlock": "称号未解锁。", "ErrorHeroNotUnlock": "该伙伴尚未获得,需前往旅人驿站招募哦。", "ErrorRuneSlotFull": "铭文槽已满,请先卸下。", "ErrorPushBoxLock": "通过{0}后解锁。", "ErrorFriendApplySuccess": "已同意成为好友。", "ErrorFriendApplyRefuse": "已拒绝成为好友。", "ErrorFriendAddOrNotExist": "玩家已添加或不存在。", "ErrorBagFull": "背包已满。", "ErrorTargetNotEnough": "{0}不足。", "ErrorPassNotUnlock": "通关后解锁。", "ErrorSaveHostage": "解救{0}个人质后解锁。", "ErrorSomethingNotEnough": "{0}不足。", "ErrorRetreatNotNow": "现在还不能撤离。", "ErrorNotUnlock": "未解锁。", "ErrorSendGiftSuccess": "赠送成功", "ErrorCollectionUnlock": "请在关卡中拾取藏品解锁。", "ErrorTaskNotFinish": "任务未完成。", "ErrorTaskRewardNotClaimed": "任务奖励未领取。", "ErrorNotNewApply": "暂无新的好友申请。", "ErrorNotPassBigBox": "请通关当前所有关卡后领取。", "ErrorNotPassBigBoxStar": "满星通过前置关卡后解锁。", "ErrorBreakthroughSuccess": "突破成功。", "ErrorLearnSuccess": "学习成功。", } # 被引用的常量(排除只在注释中引用的) referenced = { "ErrorAlreadyClaimed", "ErrorApplySuccess", "ErrorBagFull", "ErrorBreakthroughSuccess", "ErrorBuyFailed", "ErrorBuySuccess", "ErrorChallengeTimesNotEnough", "ErrorCollectionUnlock", "ErrorConditionNotMet", "ErrorCopySuccess", "ErrorCreateGuildSuccess", "ErrorCurrencyNotEnough", "ErrorDiamondNotEnough", "ErrorEnergyNotEnough", "ErrorEquipBreakthroughSuccess", "ErrorEquipStrengthSuccess", "ErrorFriendAddOrNotExist", "ErrorFriendApplyRefuse", "ErrorFriendApplySuccess", "ErrorFusionRuneCountMax", "ErrorFusionRuneCountNotEnough", "ErrorFusionRuneNotFusion", "ErrorFusionRuneRarityNotSame", "ErrorGuildNameEmpty", "ErrorGuildNameTooLong", "ErrorGuildSetSuccess", "ErrorHeroLevelUnlock", "ErrorHeroNotUnlock", "ErrorHeroStarUpSuccess", "ErrorInvalidGuideId", "ErrorItemDailyUseTimesReached", "ErrorItemNotEnough", "ErrorJoinSuccess", "ErrorLearnSuccess", "ErrorLevelNotEnough", "ErrorLevelNotExist", "ErrorMaterialNotEnough", "ErrorNameNotAvailable", "ErrorNameOnlyChinese", "ErrorNoFreePos", "ErrorNoHeroCarry", "ErrorNoHeroInTeam", "ErrorNoPackagePurchased", "ErrorNoReward", "ErrorNotMeetClaimCondition", "ErrorNotNewApply", "ErrorNotPassBigBox", "ErrorNotPassBigBoxStar", "ErrorNotSettlementTime", "ErrorNotUnlock", "ErrorPassNotOpen", "ErrorPassNotUnlock", "ErrorPleaseInputName", "ErrorPreyFailed", "ErrorPushBoxLock", "ErrorQuickSweepTimesNotEnough", "ErrorQuitGuildSuccess", "ErrorRefreshTimesNotEnough", "ErrorRetreatNotNow", "ErrorRewardAlreadyClaimed", "ErrorRunePutOnSuccess", "ErrorRuneSlotFull", "ErrorSaveHostage", "ErrorSaveSuccess", "ErrorScoreNotEnough", "ErrorSendGiftSuccess", "ErrorSetSuccess", "ErrorSomethingNotEnough", "ErrorTakesuccess", "ErrorTargetNotEnough", "ErrorTaskFailed", "ErrorTaskNotFinish", "ErrorTaskRewardNotClaimed", "ErrorTimesNotEnough", "ErrorTitleNotUnlock", "ErrorUpLevelFirst", "ErrorUpLevelSuccess", "ErrorUpStage1First", "ErrorUpStageFirst", "ErrorUpStageSuccess", "ErrorUpStrengthFirst", "ErrorVipLevelNotEnough", "TipChallengeTimesConfirm", "TipFusionSuccess", } # 只保留被引用的,去重文本值 seen_texts = set() result = [] for name, text in all_consts.items(): if name in referenced and text not in seen_texts: seen_texts.add(text) result.append(text) return result def get_quest_texts(): """获取任务文本(category 1和2的desc字段,去重)""" rows = read_csv("quest.csv") seen = set() result = [] for row in rows: if len(row) > 3: category = row[1].strip() desc = row[3].strip() if category in ("1", "2") and desc and desc not in seen: seen.add(desc) result.append(desc) return result def get_achievement_texts(): """获取成就文本(category 9的desc字段,去重)""" rows = read_csv("quest.csv") seen = set() result = [] for row in rows: if len(row) > 3: category = row[1].strip() desc = row[3].strip() if category == "9" and desc and desc not in seen: seen.add(desc) result.append(desc) return result def get_guide_texts(): """获取新手指引文本(guide.csv的desc字段,去重)""" rows = read_csv("guide.csv") seen = set() result = [] for row in rows: if len(row) > 7: desc = row[7].strip() if desc and desc not in seen: seen.add(desc) result.append(desc) return result def get_prop_data(): """获取道具数据 [(name, icon, tips), ...]""" rows = read_csv("prop.csv") result = [] for row in rows: if len(row) > 7: name = row[2].strip() tips = row[6].strip() icon = row[7].strip() if name: result.append((name, icon, tips)) return result def get_skill_data(): """获取技能数据 [(name, icon, desc), ...]""" rows = read_csv("skill.csv") result = [] for row in rows: if len(row) > 21: name = row[1].strip() icon = row[6].strip() desc = row[21].strip() if name: result.append((name, icon, desc)) return result def get_rune_data(): """获取灵石数据 [(name, icon, desc), ...]""" rows = read_csv("rune.csv") result = [] for row in rows: if len(row) > 8: name = row[1].strip() icon = row[2].strip() desc = row[8].strip() if name: result.append((name, icon, desc)) return result def get_talent_data(): """获取天赋数据 [(name, icon, desc), ...] 去重""" rows = read_csv("playerlevel.csv") seen = set() result = [] for row in rows: if len(row) > 15: name = row[13].strip() desc = row[14].strip() icon = row[15].strip() if name and name not in seen: seen.add(name) result.append((name, icon, desc)) return result def get_equip_data(): """获取装备数据 [(name, icon), ...] 去重""" rows = read_csv("equip.csv") seen = set() result = [] for row in rows: if len(row) > 5: name = row[1].strip() icon = row[5].strip() if name and name not in seen: seen.add(name) result.append((name, icon)) return result def get_level_names(): """获取关卡名称(fight_sample.csv的name字段,去重)""" rows = read_csv("fight_sample.csv") seen = set() result = [] for row in rows: if len(row) > 2: name = row[2].strip() if name and name not in seen: seen.add(name) result.append(name) return result # 玩家随机名称文本(保持不变) PLAYER_NAMES = [ ("李", "子轩"), ("王", "梓涵"), ("张", "浩然"), ("刘", "欣怡"), ("陈", "宇轩"), ("杨", "晨曦"), ("赵", "俊杰"), ("黄", "雅静"), ("周", "天佑"), ("吴", "梦琪"), ("徐", "博文"), ("孙", "思彤"), ("胡", "睿智"), ("朱", "静怡"), ("高", "昊天"), ("林", "雨桐"), ("何", "泽宇"), ("郭", "婉清"), ("马", "俊熙"), ("罗", "雪柔"), ("梁", "子豪"), ("宋", "依琳"), ("郑", "宇航"), ("谢", "诗涵"), ("韩", "志强"), ("唐", "梦瑶"), ("冯", "文博"), ("于", "雅楠"), ("董", "天宇"), ("萧", "欣妍"), ("程", "子睿"), ("曹", "静雯"), ("袁", "俊豪"), ("邓", "雨欣"), ("许", "浩宇"), ("傅", "语嫣"), ("沈", "子涵"), ("曾", "思雨"), ("彭", "博涛"), ("吕", "雅婷"), ("苏", "天翔"), ("卢", "梦洁"), ("蒋", "文轩"), ("蔡", "雪梅"), ("贾", "志远"), ("丁", "依娜"), ("魏", "子安"), ("薛", "静雅"), ("叶", "俊峰"), ("阎", "雨婷"), ("余", "浩轩"), ("潘", "诗琪"), ("杜", "子恒"), ("戴", "思雅"), ("夏", "博超"), ("钟", "雅琪"), ("汪", "天瑞"), ("田", "梦菲"), ("任", "文昊"), ("姜", "雪莲"), ("范", "志鹏"), ("方", "依婷"), ("石", "子健"), ("姚", "静淑"), ("谭", "俊凯"), ("廖", "雨菲"), ("邹", "浩南"), ("熊", "诗瑶"), ("金", "子宁"), ("陆", "思琪"), ("郝", "博远"), ("孔", "雅静"), ("白", "天浩"), ("崔", "梦婷"), ("康", "文杰"), ("毛", "雪琪"), ("邱", "志伟"), ("秦", "依静"), ("江", "子文"), ("史", "静怡"), ("顾", "俊杰"), ("侯", "雨萱"), ("邵", "浩天"), ("孟", "诗涵"), ("龙", "子辰"), ("万", "思妍"), ("段", "博文"), ("雷", "雅欣"), ("钱", "天佑"), ("汤", "梦瑶"), ("尹", "文豪"), ("黎", "雪莹"), ("易", "志强"), ("常", "依琳"), ("武", "子轩"), ("乔", "静雯"), ("贺", "俊熙"), ("赖", "雨桐"), ("龚", "浩宇"), ("文", "诗琪"), ] # ============ 文档生成 ============ # 中文编号 CN_NUMBERS = ["一", "二", "三", "四", "五", "六", "七", "八", "九", "十", "十一"] def add_title(doc, text): """添加文档标题:宋体、二号(22pt)、加粗、居中、无下划线""" para = doc.add_paragraph() para.alignment = WD_ALIGN_PARAGRAPH.CENTER run = para.add_run(text) run.font.name = "宋体" run._element.rPr.rFonts.set(qn("w:eastAsia"), "宋体") run.font.size = Pt(22) run.font.bold = True run.font.underline = False run.font.color.rgb = None # 确保黑色,无蓝色 def add_section_heading(doc, cn_num, title_text): """添加小标题:仿宋、四号(14pt)、加粗、中文编号""" para = doc.add_paragraph() run = para.add_run(f"{cn_num}、{title_text}") run.font.name = "仿宋" run._element.rPr.rFonts.set(qn("w:eastAsia"), "仿宋") run.font.size = Pt(14) run.font.bold = True def add_numbered_paragraph(doc, index, text): """添加编号段落:仿宋、四号(14pt)、不加粗、阿拉伯数字编号""" para = doc.add_paragraph() run = para.add_run(f"{index}.{text}") run.font.name = "仿宋" run._element.rPr.rFonts.set(qn("w:eastAsia"), "仿宋") run.font.size = Pt(14) run.font.bold = False def add_numbered_items(doc, items): """批量添加编号段落""" for i, text in enumerate(items, 1): add_numbered_paragraph(doc, i, text) def add_name_table(doc, names): """添加玩家随机名称表格:姓/名两列""" table = doc.add_table(rows=1, cols=2, style="Table Grid") table.alignment = WD_TABLE_ALIGNMENT.CENTER # 表头 for i, header in enumerate(["姓", "名"]): cell = table.rows[0].cells[i] cell.text = "" para = cell.paragraphs[0] para.alignment = WD_ALIGN_PARAGRAPH.CENTER run = para.add_run(header) run.font.name = "仿宋" run._element.rPr.rFonts.set(qn("w:eastAsia"), "仿宋") run.font.size = Pt(14) run.font.bold = True # 数据行 for surname, given_name in names: row = table.add_row() for j, text in enumerate([surname, given_name]): cell = row.cells[j] cell.text = "" para = cell.paragraphs[0] para.alignment = WD_ALIGN_PARAGRAPH.CENTER run = para.add_run(text) run.font.name = "仿宋" run._element.rPr.rFonts.set(qn("w:eastAsia"), "仿宋") run.font.size = Pt(14) run.font.bold = False def build_document(): doc = Document() # 设置默认字体 style = doc.styles["Normal"] style.font.name = "仿宋" style.font.size = Pt(14) style._element.rPr.rFonts.set(qn("w:eastAsia"), "仿宋") # 标题:宋体、二号、加粗、无蓝色下划线 add_title(doc, "《不朽封仙》文字脚本") # ======= 一、系统提示 ======= add_section_heading(doc, CN_NUMBERS[0], "系统提示") add_numbered_items(doc, get_system_prompts()) # ======= 二、任务文本 ======= add_section_heading(doc, CN_NUMBERS[1], "任务文本") add_numbered_items(doc, get_quest_texts()) # ======= 三、成就文本 ======= add_section_heading(doc, CN_NUMBERS[2], "成就文本") add_numbered_items(doc, get_achievement_texts()) # ======= 四、玩家随机名称文本 ======= add_section_heading(doc, CN_NUMBERS[3], "玩家随机名称文本") add_name_table(doc, PLAYER_NAMES) # ======= 五、新手指引文本 ======= add_section_heading(doc, CN_NUMBERS[4], "新手指引文本") add_numbered_items(doc, get_guide_texts()) # ======= 六、道具说明 ======= add_section_heading(doc, CN_NUMBERS[5], "道具说明") prop_data = get_prop_data() add_table_with_icons(doc, ["道具名称", "图标", "道具描述"], prop_data, "product") # ======= 七、技能 ======= add_section_heading(doc, CN_NUMBERS[6], "技能") skill_data = get_skill_data() add_table_with_icons(doc, ["技能名称", "技能图标", "技能描述"], skill_data, "skill") # ======= 八、灵石 ======= add_section_heading(doc, CN_NUMBERS[7], "灵石") rune_data = get_rune_data() add_table_with_icons(doc, ["灵石名称", "灵石图标", "灵石介绍"], rune_data, "rune") # ======= 九、天赋 ======= add_section_heading(doc, CN_NUMBERS[8], "天赋") talent_data = get_talent_data() add_table_with_icons(doc, ["天赋名称", "天赋图标", "天赋介绍"], talent_data, "talent") # ======= 十、装备 ======= add_section_heading(doc, CN_NUMBERS[9], "装备") equip_data = get_equip_data() add_table_equip(doc, ["装备名称", "装备图标"], equip_data, "equip") # ======= 十一、关卡名称 ======= add_section_heading(doc, CN_NUMBERS[10], "关卡名称") add_numbered_items(doc, get_level_names()) # 保存文档 doc.save(OUTPUT_PATH) print(f"文档已生成: {OUTPUT_PATH}") if __name__ == "__main__": build_document()