From a1ae69aa7a3c055a9185f61684153efb1fab3cd8 Mon Sep 17 00:00:00 2001 From: shenlei Date: Mon, 28 Sep 2026 18:45:58 +0900 Subject: [PATCH] zh locale: pack locale_zh and add reviewed Chinese names eterpack_pack writes locale packs in the original format (COMPRESS entries, LZO+TEA index), and build_zh_locale.py packs locale/zh into locale_zh.eix/.epk registered in pack/Index instead of leaving loose files. The reviewed translations for names and keys still in English are kept apart from the server snapshot in data/zh_40250/*_zh_overlay.txt (1,607 items, 231 mobs/NPCs, 45 locale_game keys) and override it at build time. Only YMIR, the coordinate-named warp markers and the empty TOOLTIP_UNSEAL_LEFT_TIME remain English. Co-Authored-By: Claude Opus 5.5 --- data/zh_40250/README.md | 3 + data/zh_40250/item_names_zh_overlay.txt | 1608 ++++++++++++++++++++++ data/zh_40250/locale_game_zh_overlay.txt | 46 + data/zh_40250/mob_names_zh_overlay.txt | 232 ++++ docs/ZH-LOCALE.md | 65 +- extension/src/port/CMakeLists.txt | 3 + extension/tests/eterpack_pack.cpp | 105 ++ script/build_zh_locale.py | 118 +- script/test_build_zh_locale.py | 45 +- 9 files changed, 2180 insertions(+), 45 deletions(-) create mode 100644 data/zh_40250/item_names_zh_overlay.txt create mode 100644 data/zh_40250/locale_game_zh_overlay.txt create mode 100644 data/zh_40250/mob_names_zh_overlay.txt create mode 100644 extension/tests/eterpack_pack.cpp diff --git a/data/zh_40250/README.md b/data/zh_40250/README.md index 2d5dfd19..2b9988b9 100644 --- a/data/zh_40250/README.md +++ b/data/zh_40250/README.md @@ -11,5 +11,8 @@ | `mob_names_zh.txt` | 1,334 | `fdbfe4b3f3367d5842c0e9bbe0b5e5ea2dbe99fb6e566ef5789f65d3794eab5c` | 线上字段里的 1,608 个物品名及 346 个怪物/NPC 名不含汉字,本快照原样保留。 +它们的审核译文(2026-09-28 确认)单独放在 `item_names_zh_overlay.txt`、 +`mob_names_zh_overlay.txt`,界面回退键译文在 `locale_game_zh_overlay.txt`; +`script/build_zh_locale.py` 构建时以 overlay 覆盖快照同 VNUM / 同键的值。 旧的龙驹中文表与线上表有 21 个相同 VNUM 不同名;不把旧表覆盖到服务端。 服务端名称若再更新,需重新只读导出并核对差异后更新本快照。 diff --git a/data/zh_40250/item_names_zh_overlay.txt b/data/zh_40250/item_names_zh_overlay.txt new file mode 100644 index 00000000..2705acf4 --- /dev/null +++ b/data/zh_40250/item_names_zh_overlay.txt @@ -0,0 +1,1608 @@ +VNUM LOCALE_NAME +1 金币 +280 圣剑 +281 圣剑 +282 圣剑 +283 圣剑 +284 圣剑 +285 圣剑 +286 圣剑 +287 圣剑 +288 圣剑 +289 圣剑 +470 龙牙刀 +471 龙牙刀 +472 龙牙刀 +473 龙牙刀 +474 龙牙刀 +475 龙牙刀 +476 龙牙刀 +477 龙牙刀 +478 龙牙刀 +479 龙牙刀 +1340 五行刀 +1341 五行刀 +1342 五行刀 +1343 五行刀 +1344 五行刀 +1345 五行刀 +1346 五行刀 +1347 五行刀 +1348 五行刀 +1349 五行刀 +2370 凤凰弓 +2371 凤凰弓 +2372 凤凰弓 +2373 凤凰弓 +2374 凤凰弓 +2375 凤凰弓 +2376 凤凰弓 +2377 凤凰弓 +2378 凤凰弓 +2379 凤凰弓 +4030 日月双刀 +4031 日月双刀 +4032 日月双刀 +4033 日月双刀 +4034 日月双刀 +4035 日月双刀 +4036 日月双刀 +4037 日月双刀 +4038 日月双刀 +4039 日月双刀 +5130 死神铃 +5131 死神铃 +5132 死神铃 +5133 死神铃 +5134 死神铃 +5135 死神铃 +5136 死神铃 +5137 死神铃 +5138 死神铃 +5139 死神铃 +5140 龙牙铃 +5141 龙牙铃 +5142 龙牙铃 +5143 龙牙铃 +5144 龙牙铃 +5145 龙牙铃 +5146 龙牙铃 +5147 龙牙铃 +5148 龙牙铃 +5149 龙牙铃 +5330 龙颚铃 +5331 龙颚铃 +5332 龙颚铃 +5333 龙颚铃 +5334 龙颚铃 +5335 龙颚铃 +5336 龙颚铃 +5337 龙颚铃 +5338 龙颚铃 +5339 龙颚铃 +5340 龙神铃 +5341 龙神铃 +5342 龙神铃 +5343 龙神铃 +5344 龙神铃 +5345 龙神铃 +5346 龙神铃 +5347 龙神铃 +5348 龙神铃 +5349 龙神铃 +7170 八卦扇 +7171 八卦扇 +7172 八卦扇 +7173 八卦扇 +7174 八卦扇 +7175 八卦扇 +7176 八卦扇 +7177 八卦扇 +7178 八卦扇 +7179 八卦扇 +7200 马上长枪 +7201 马上长枪 +7202 马上长枪 +7203 马上长枪 +7204 马上长枪 +7205 马上长枪 +7206 马上长枪 +7207 马上长枪 +7208 马上长枪 +7209 马上长枪 +7370 魔魂无双法杖 +7371 魔魂无双法杖 +7372 魔魂无双法杖 +7373 魔魂无双法杖 +7374 魔魂无双法杖 +7375 魔魂无双法杖 +7376 魔魂无双法杖 +7377 魔魂无双法杖 +7378 魔魂无双法杖 +7379 魔魂无双法杖 +9001 罗汉甲 +9002 弹魔甲 +9003 镇魂甲 +9004 青囊衣 +9501 武士刀 +9502 短刀 +9503 短弓 +9504 长剑 +9505 白扇 +11309 赤铁龙甲 +11509 护具 +11709 魔灵神甲 +12009 真枫铜衫 +12260 恐惧战盔 +12261 恐惧战盔 +12262 恐惧战盔 +12263 恐惧战盔 +12264 恐惧战盔 +12265 恐惧战盔 +12266 恐惧战盔 +12267 恐惧战盔 +12268 恐惧战盔 +12269 恐惧战盔 +12280 战神盔 +12281 战神盔 +12282 战神盔 +12283 战神盔 +12284 战神盔 +12285 战神盔 +12286 战神盔 +12287 战神盔 +12288 战神盔 +12289 战神盔 +12390 鬼族头巾 +12391 鬼族头巾 +12392 鬼族头巾 +12393 鬼族头巾 +12394 鬼族头巾 +12395 鬼族头巾 +12396 鬼族头巾 +12397 鬼族头巾 +12398 鬼族头巾 +12399 鬼族头巾 +12400 蜘蛛头巾 +12401 蜘蛛头巾 +12402 蜘蛛头巾 +12403 蜘蛛头巾 +12404 蜘蛛头巾 +12405 蜘蛛头巾 +12406 蜘蛛头巾 +12407 蜘蛛头巾 +12408 蜘蛛头巾 +12409 蜘蛛头巾 +12530 魔角盔 +12531 魔角盔 +12532 魔角盔 +12533 魔角盔 +12534 魔角盔 +12535 魔角盔 +12536 魔角盔 +12537 魔角盔 +12538 魔角盔 +12539 魔角盔 +12540 魔法盔 +12541 魔法盔 +12542 魔法盔 +12543 魔法盔 +12544 魔法盔 +12545 魔法盔 +12546 魔法盔 +12547 魔法盔 +12548 魔法盔 +12549 魔法盔 +14500 红玉手镯 +14501 红玉手镯 +14502 红玉手镯 +14503 红玉手镯 +14504 红玉手镯 +14505 红玉手镯 +14506 红玉手镯 +14507 红玉手镯 +14508 红玉手镯 +14509 红玉手镯 +14540 翡翠手镯 +14541 翡翠手镯 +14542 翡翠手镯 +14543 翡翠手镯 +14544 翡翠手镯 +14545 翡翠手镯 +14546 翡翠手镯 +14547 翡翠手镯 +14548 翡翠手镯 +14549 翡翠手镯 +14560 青玉手镯 +14561 青玉手镯 +14562 青玉手镯 +14563 青玉手镯 +14564 青玉手镯 +14565 青玉手镯 +14566 青玉手镯 +14567 青玉手镯 +14568 青玉手镯 +14569 青玉手镯 +15370 真三昧靴 +15371 真三昧靴 +15372 真三昧靴 +15373 真三昧靴 +15374 真三昧靴 +15375 真三昧靴 +15376 真三昧靴 +15377 真三昧靴 +15378 真三昧靴 +15379 真三昧靴 +15390 真云踏战靴 +15391 真云踏战靴 +15392 真云踏战靴 +15393 真云踏战靴 +15394 真云踏战靴 +15395 真云踏战靴 +15396 真云踏战靴 +15397 真云踏战靴 +15398 真云踏战靴 +15399 真云踏战靴 +15410 真麒麟靴 +15411 真麒麟靴 +15412 真麒麟靴 +15413 真麒麟靴 +15414 真麒麟靴 +15415 真麒麟靴 +15416 真麒麟靴 +15417 真麒麟靴 +15418 真麒麟靴 +15419 真麒麟靴 +15430 真翠鸟靴 +15431 真翠鸟靴 +15432 真翠鸟靴 +15433 真翠鸟靴 +15434 真翠鸟靴 +15435 真翠鸟靴 +15436 真翠鸟靴 +15437 真翠鸟靴 +15438 真翠鸟靴 +15439 真翠鸟靴 +16500 红玉项链 +16501 红玉项链 +16502 红玉项链 +16503 红玉项链 +16504 红玉项链 +16505 红玉项链 +16506 红玉项链 +16507 红玉项链 +16508 红玉项链 +16509 红玉项链 +16540 翡翠项链 +16541 翡翠项链 +16542 翡翠项链 +16543 翡翠项链 +16544 翡翠项链 +16545 翡翠项链 +16546 翡翠项链 +16547 翡翠项链 +16548 翡翠项链 +16549 翡翠项链 +16560 青玉项链 +16561 青玉项链 +16562 青玉项链 +16563 青玉项链 +16564 青玉项链 +16565 青玉项链 +16566 青玉项链 +16567 青玉项链 +16568 青玉项链 +16569 青玉项链 +17500 红玉耳环 +17501 红玉耳环 +17502 红玉耳环 +17503 红玉耳环 +17504 红玉耳环 +17505 红玉耳环 +17506 红玉耳环 +17507 红玉耳环 +17508 红玉耳环 +17509 红玉耳环 +17540 翡翠耳环 +17541 翡翠耳环 +17542 翡翠耳环 +17543 翡翠耳环 +17544 翡翠耳环 +17545 翡翠耳环 +17546 翡翠耳环 +17547 翡翠耳环 +17548 翡翠耳环 +17549 翡翠耳环 +17560 青玉耳环 +17561 青玉耳环 +17562 青玉耳环 +17563 青玉耳环 +17564 青玉耳环 +17565 青玉耳环 +17566 青玉耳环 +17567 青玉耳环 +17568 青玉耳环 +17569 青玉耳环 +18010 皮革腹带 +18011 皮革腹带 +18012 皮革腹带 +18013 皮革腹带 +18014 皮革腹带 +18015 皮革腹带 +18016 皮革腹带 +18017 皮革腹带 +18018 皮革腹带 +18019 皮革腹带 +18020 五星腹带 +18021 五星腹带 +18022 五星腹带 +18023 五星腹带 +18024 五星腹带 +18025 五星腹带 +18026 五星腹带 +18027 五星腹带 +18028 五星腹带 +18029 五星腹带 +18030 智慧腹带 +18031 智慧腹带 +18032 智慧腹带 +18033 智慧腹带 +18034 智慧腹带 +18035 智慧腹带 +18036 智慧腹带 +18037 智慧腹带 +18038 智慧腹带 +18039 智慧腹带 +18040 帝王腹带 +18041 帝王腹带 +18042 帝王腹带 +18043 帝王腹带 +18044 帝王腹带 +18045 帝王腹带 +18046 帝王腹带 +18047 帝王腹带 +18048 帝王腹带 +18049 帝王腹带 +18050 暗影腹带 +18051 暗影腹带 +18052 暗影腹带 +18053 暗影腹带 +18054 暗影腹带 +18055 暗影腹带 +18056 暗影腹带 +18057 暗影腹带 +18058 暗影腹带 +18059 暗影腹带 +18060 符文腹带 +18061 符文腹带 +18062 符文腹带 +18063 符文腹带 +18064 符文腹带 +18065 符文腹带 +18066 符文腹带 +18067 符文腹带 +18068 符文腹带 +18069 符文腹带 +18070 七星腹带 +18071 七星腹带 +18072 七星腹带 +18073 七星腹带 +18074 七星腹带 +18075 七星腹带 +18076 七星腹带 +18077 七星腹带 +18078 七星腹带 +18079 七星腹带 +18080 灵魂腹带 +18081 灵魂腹带 +18082 灵魂腹带 +18083 灵魂腹带 +18084 灵魂腹带 +18085 灵魂腹带 +18086 灵魂腹带 +18087 灵魂腹带 +18088 灵魂腹带 +18089 灵魂腹带 +20000 魔角铠 +20001 魔角铠 +20002 魔角铠 +20003 魔角铠 +20004 魔角铠 +20005 魔角铠 +20006 魔角铠 +20007 魔角铠 +20008 魔角铠 +20009 魔角铠 +20250 龙骑士衣 +20251 龙骑士衣 +20252 龙骑士衣 +20253 龙骑士衣 +20254 龙骑士衣 +20255 龙骑士衣 +20256 龙骑士衣 +20257 龙骑士衣 +20258 龙骑士衣 +20259 龙骑士衣 +20500 骨板甲 +20501 骨板甲 +20502 骨板甲 +20503 骨板甲 +20504 骨板甲 +20505 骨板甲 +20506 骨板甲 +20507 骨板甲 +20508 骨板甲 +20509 骨板甲 +20750 金缕衫 +20751 金缕衫 +20752 金缕衫 +20753 金缕衫 +20754 金缕衫 +20755 金缕衫 +20756 金缕衫 +20757 金缕衫 +20758 金缕衫 +20759 金缕衫 +27051 新手血瓶(小) +27052 新手魔法瓶(小) +27053 新手速攻药剂(小) +27054 新手轻身药剂(小) +28000 屠杀石 +28004 偏执石 +28008 创伤石 +28012 愚钝石 +28100 屠杀石 +28104 偏执石 +28108 创伤石 +28112 愚钝石 +28200 屠杀石 +28204 偏执石 +28208 创伤石 +28212 愚钝石 +28300 屠杀石 +28304 偏执石 +28308 创伤石 +28312 愚钝石 +28400 屠杀石 +28404 偏执石 +28408 创伤石 +28412 愚钝石 +28500 屠杀石 +28504 偏执石 +28508 创伤石 +28512 愚钝石 +28600 鸡血石 +28601 羊脂玉 +28602 羚角石 +28603 虹河石 +28604 青胆石 +28605 精石 +28606 鹰翼石 +28607 木鱼石 +28608 龙骨石 +28609 青石 +28610 彩芒石 +28611 鹿影石 +28612 东海石 +28613 帝王石 +30169 血红花 +30170 橙色花 +30171 芬芳黄花 +30172 高贵花束 +30173 无狼毛皮 +30174 熊掌 +30175 钻芯鲸矛 +30176 闪光虎皮 +30177 麝香油 +30178 刻文石 +30179 扭曲的钥匙 +30180 龙神护符 +30181 书页 +30182 传说日记 +30183 阴沉木面具 +30184 着魔的灰烬 +30185 黑冰 +30186 泪之化石 +30187 血滴 +30188 黑曜石 +30189 埃尔维丁之泪 +30190 血石 +30191 血石 +30192 树液 +30193 指骨 +30194 鬼族棍棒 +30195 石英砂 +30196 恶鬼之灰 +30197 魔法种子 +30198 水之宝石 +30199 蓝水晶 +30228 水龙灵魂石 +30251 恶念宝石 +30252 智慧宝石 +30253 忠诚宝石 +30254 天玉 +30265 证物碎片(1) +30266 证物碎片(2) +30267 证物碎片(3) +30268 证物碎片(4) +30269 证物碎片(5) +30270 龙魂石碎片 +30311 灵魂水晶钥匙 +30312 鬼脸图腾 +30315 大麦面包 +30316 镶嵌面包盘 +30317 大麦面包篮 +30318 无花果 +30319 死神证物 +30320 死神证物 +30321 万圣节南瓜 +30322 万圣节魔法棒 +30323 南瓜头面具兑换券 +30324 蜘蛛神钥匙 +30325 蜘蛛神钥匙 +30326 圣母蛛毒液 +30327 蜘蛛召唤笛 +30329 金齿轮 +30330 赤龙解封石 +30500 红色刀柄碎片 +30501 金色刀柄头 +30502 红色剑饰碎片 +30503 紫色刀柄碎片 +30504 银色刀柄头 +30505 绿色剑饰碎片 +30506 蓝色刀柄碎片 +30507 青铜刀柄头 +30508 蓝色剑饰碎片 +30509 蓝色甲胄碎片 +30510 宝石肩甲碎片 +30511 红色布条碎片 +30512 红色甲胄碎片 +30513 华丽肩甲碎片 +30514 紫色布条碎片 +30515 紫色甲胄碎片 +30516 古朴肩甲碎片 +30517 黄色布条碎片 +30518 闪亮金丝 +30519 精巧金环扣 +30520 华丽金饰 +30521 朴素金戒指 +30522 精细金链 +30523 简洁金饰 +30524 火焰石 +30525 冰焰石 +30526 金指环 +30527 银指环 +30528 铜指环 +30529 木指环 +30530 青指环 +30531 绿指环 +30532 红指环 +30533 紫指环 +30534 精工金戒指 +30535 精工银戒指 +30536 精工铜戒指 +30537 精工木戒指 +30538 精工青色戒指 +30539 精工绿色戒指 +30540 精工红色戒指 +30541 精工紫色戒指 +30542 朴素银戒指 +30543 朴素铜戒指 +30544 朴素木戒指 +30545 朴素青色戒指 +30546 朴素绿色戒指 +30547 朴素红色戒指 +30548 朴素黄色皮带 +30549 朴素褐色皮带 +30550 朴素青色皮带 +30551 朴素黑色皮带 +30552 朴素紫色皮带 +30553 朴素绿色皮带 +30554 闪亮橙丝 +30555 闪亮黑丝 +30556 闪亮青丝 +30557 闪亮绿丝 +30558 闪亮红丝 +30559 闪亮紫丝 +30560 精巧银环扣 +30561 精巧铜环扣 +30562 精巧木环扣 +30563 精巧青色环扣 +30564 精巧绿色环扣 +30565 精巧红色环扣 +30566 精巧紫色环扣 +30567 精细银链 +30568 精细铜链 +30569 精细青色链 +30570 精细绿色链 +30571 精细红色链 +30572 精细紫色链 +30573 华丽银饰 +30574 华丽铜饰 +30575 华丽木饰 +30576 华丽青色饰品 +30577 华丽绿色饰品 +30578 华丽红色饰品 +30579 华丽紫色饰品 +30580 简洁银饰 +30581 简洁铜饰 +30582 简洁木饰 +30583 简洁青色饰品 +30584 简洁绿色饰品 +30585 简洁红色饰品 +30586 简洁紫色饰品 +31001 古代碑文拓本 +31002 古代文书 +31003 解毒剂 +31004 神树树液 +31005 蓝水晶精块 +31006 红色地狱结晶 +31007 火山之土 +31008 天之泪 +31009 狼人族书信碎片 +31050 药材包 +31051 铁 +31052 魂灵草 +31053 狼人族的毛团 +31054 虎翼族的毛皮 +31055 无名药草 +31056 独眼斗鬼的爪子 +31057 巨人族之角 +31058 猪人族之蹄 +31059 魍魉族的归魂石 +31060 鱼人香 +31061 鬼木枝 +31062 优质寒铁 +31063 饭团 +31064 佛性草 +31065 虎翼族的皮纸 +31066 仙草 +31067 紧锁的原石箱 +31068 龙骨 +31069 鬼木树液 +31070 魔犬项圈 +31071 神秘名牌 +31072 魔镜 +31073 金猎人之环 +31074 铆钉项链 +31075 倒刺项链 +31076 太阳草 +31077 基石粉末 +31078 净化之液 +31079 给药还的信 +31080 火焰王之血 +31081 黑暗物质 +31082 熔岩之环 +31083 萨麦尔之戒 +31084 灵光药剂 +31085 重水 +31086 骨粉 +31087 火灵之焰 +31088 火焰珠 +31089 浸染术法的冰片 +31090 冰帝寒魔之心 +31091 真火焰王之血 +31092 超冷怪的皮肤碎片 +35000 药草+ +35001 虎肝+ +38001 VIP特权 +38002 抽奖券(今日) +38003 抽奖券(明日) +38004 抽奖重抽券 +38005 高级抽奖转盘券 +38006 Momo重抽券 +38010 金币包 +38050 力量宝箱 +38051 皇虎水晶 +38052 王者宝箱 +38053 永冬宝箱 +38054 沙暴宝箱 +38055 幼宠箱 +38056 节日宝箱 +38057 北境宝箱 +38100 能量零食(7天) +38101 能量零食(15天) +38102 能量零食(30天) +38200 【令】战金幽狱 +38201 【令】战金幽狱 +39001 玄铁 +39002 经验戒指 +39003 万能药 +39004 祝福之珠 +39005 万年寒铁 +39006 勇气披风 +39007 高手指南针 +39008 咒安秘籍 +39009 技能初始化 +39010 快速酒 +39011 生命之丸 +39012 精神之丸 +39013 龙神之丸 +39014 武神的祝福书 +39015 夜光石 +39016 黄龙石 +39017 龙神的生命 +39018 龙神的攻击 +39019 龙神的智力 +39020 龙神的防御 +39021 龙神的支援 +39022 龙神的祝福书 +39023 迷惑药水 +39024 双倍伤害丸 +39025 无视防御丸 +39026 加速酒 +39027 吸石秘籍 +39028 物品属性转换秘籍 +39029 追加物品属性秘籍 +39030 先人的教训 +39031 速灵酒 +39032 仙桃 +39033 宝石挖掘秘笈 +39034 方糖 +39035 金刚镜 +39036 风之靴 +39037 红龙的祝福(小) +39038 红龙的祝福(中) +39039 红龙的祝福(大) +39040 蓝龙的祝福(小) +39041 蓝龙的祝福(中) +39042 蓝龙的祝福(大) +39043 超级记忆卷 +41005 奥运击剑服(男) +41006 奥运击剑服(女) +41007 德国击剑服(男) +41008 德国击剑服(女) +41011 巴西击剑服(男) +41012 巴西击剑服(女) +41015 德国足球服(男) +41016 德国足球服(女) +41019 巴西足球服(男) +41020 巴西足球服(女) +41023 德国拳击服(男) +41024 德国拳击服(女) +41027 巴西拳击服(男) +41028 巴西拳击服(女) +41029 臻|沙漠战士(男) +41030 臻|沙漠战士(女) +41031 臻|奥运击剑服(男) +41032 臻|奥运击剑服(女) +41033 臻|德国击剑服(男) +41034 臻|德国击剑服(女) +41037 臻|巴西击剑服(男) +41038 臻|巴西击剑服(女) +41041 臻|德国足球服(男) +41042 臻|德国足球服(女) +41045 臻|巴西足球服(男) +41046 臻|巴西足球服(女) +41049 臻|德国拳击服(男) +41050 臻|德国拳击服(女) +41053 臻|巴西拳击服(男) +41054 臻|巴西拳击服(女) +41055 法国击剑服(男) +41056 法国击剑服(女) +41057 意大利击剑服(男) +41058 意大利击剑服(女) +41059 波兰击剑服(男) +41060 波兰击剑服(女) +41061 罗马尼亚击剑服(男) +41062 罗马尼亚击剑服(女) +41063 西班牙击剑服(男) +41064 西班牙击剑服(女) +41065 法国足球服(男) +41066 法国足球服(女) +41067 意大利足球服(男) +41068 意大利足球服(女) +41069 波兰足球服(男) +41070 波兰足球服(女) +41071 罗马尼亚足球服(男) +41072 罗马尼亚足球服(女) +41073 西班牙足球服(男) +41074 西班牙足球服(女) +41075 法国拳击服(男) +41076 法国拳击服(女) +41077 意大利拳击服(男) +41078 意大利拳击服(女) +41079 波兰拳击服(男) +41080 波兰拳击服(女) +41081 罗马尼亚拳击服(男) +41082 罗马尼亚拳击服(女) +41083 西班牙拳击服(男) +41084 西班牙拳击服(女) +41085 臻|法国击剑服(男) +41086 臻|法国击剑服(女) +41087 臻|意大利击剑服(男) +41088 臻|意大利击剑服(女) +41089 臻|波兰击剑服(男) +41090 臻|波兰击剑服(女) +41091 臻|罗马尼亚击剑服(男) +41092 臻|罗马尼亚击剑服(女) +41093 臻|西班牙击剑服(男) +41094 臻|西班牙击剑服(女) +41095 臻|法国足球服(男) +41096 臻|法国足球服(女) +41097 臻|意大利足球服(男) +41098 臻|意大利足球服(女) +41099 臻|波兰足球服(男) +41100 臻|波兰足球服(女) +41101 臻|罗马尼亚足球服(男) +41102 臻|罗马尼亚足球服(女) +41103 臻|西班牙足球服(男) +41104 臻|西班牙足球服(女) +41105 臻|法国拳击服(男) +41106 臻|法国拳击服(女) +41107 臻|意大利拳击服(男) +41108 臻|意大利拳击服(女) +41109 臻|波兰拳击服(男) +41110 臻|波兰拳击服(女) +41111 臻|罗马尼亚拳击服(男) +41112 臻|罗马尼亚拳击服(女) +41113 臻|西班牙拳击服(男) +41114 臻|西班牙拳击服(女) +41125 南瓜燕尾服 +41126 南瓜礼裙 +41135 臻|南瓜燕尾服 +41136 臻|南瓜礼裙 +41143 驯鹿服装(男) +41144 驯鹿服装(女) +41151 臻|驯鹿服装(男) +41152 臻|驯鹿服装(女) +41153 AC密阳(男) +41154 AC密阳(女) +41155 AFC夜阳(男) +41156 AFC夜阳(女) +41157 RSC蛇原(男) +41158 RSC蛇原(女) +41159 龙安FC(男) +41160 龙安FC(女) +41161 FC巴克拉(男) +41162 FC巴克拉(女) +41163 FC福井(男) +41164 FC福井(女) +41165 FC巴伐利亚黄(男) +41166 FC巴伐利亚黄(女) +41167 波鲁西亚秦皇(男) +41168 波鲁西亚秦皇(女) +41169 SC道扬(男) +41170 SC道扬(女) +41171 河顺洞FC(男) +41172 河顺洞FC(女) +41173 CFR龙火07(男) +41174 CFR龙火07(女) +41175 大雅美FC(男) +41176 大雅美FC(女) +41177 GNK瓜罗洞(男) +41178 GNK瓜罗洞(女) +41179 1927林河(男) +41180 1927林河(女) +41181 FC冰原(男) +41182 FC冰原(女) +41183 FC锦山(男) +41184 FC锦山(女) +41185 加拉瓦龙(男) +41186 加拉瓦龙(女) +41187 尤文图斯龙飞(男) +41188 尤文图斯龙飞(女) +41189 OSC龙三(男) +41190 OSC龙三(女) +41191 道扬还CF(男) +41192 道扬还CF(女) +41193 软玉城FC(男) +41194 软玉城FC(女) +41195 盛唐联(男) +41196 盛唐联(女) +41197 龙谷SC(男) +41198 龙谷SC(女) +41199 雷山FC(男) +41200 雷山FC(女) +41201 平武圣黄(男) +41202 平武圣黄(女) +41203 皇家素汉(男) +41204 皇家素汉(女) +41205 FC汉武04(男) +41206 FC汉武04(女) +41207 FC上善洞(男) +41208 FC上善洞(女) +41209 SL中善洞(男) +41210 SL中善洞(女) +41211 斯巴达克中郎(男) +41212 斯巴达克中郎(女) +41213 火焰之地CF(男) +41214 火焰之地CF(女) +41215 FC圣升龙(男) +41216 FC圣升龙(女) +41217 臻|AC密阳(男) +41218 臻|AC密阳(女) +41219 臻|AFC夜阳(男) +41220 臻|AFC夜阳(女) +41221 臻|RSC蛇原(男) +41222 臻|RSC蛇原(女) +41223 臻|龙安FC(男) +41224 臻|龙安FC(女) +41225 臻|FC巴克拉(男) +41226 臻|FC巴克拉(女) +41227 臻|FC福井(男) +41228 臻|FC福井(女) +41229 臻|FC巴伐利亚黄(男) +41230 臻|FC巴伐利亚黄(女) +41231 臻|波鲁西亚秦皇(男) +41232 臻|波鲁西亚秦皇(女) +41233 臻|SC道扬(男) +41234 臻|SC道扬(女) +41235 臻|河顺洞FC(男) +41236 臻|河顺洞FC(女) +41237 臻|CFR龙火07(男) +41238 臻|CFR龙火07(女) +41239 臻|大雅美FC(男) +41240 臻|大雅美FC(女) +41241 臻|GNK瓜罗洞(男) +41242 臻|GNK瓜罗洞(女) +41243 臻|1927林河(男) +41244 臻|1927林河(女) +41245 臻|FC冰原(男) +41246 臻|FC冰原(女) +41247 臻|FC锦山(男) +41248 臻|FC锦山(女) +41249 臻|加拉瓦龙(男) +41250 臻|加拉瓦龙(女) +41251 臻|尤文图斯龙飞(男) +41252 臻|尤文图斯龙飞(女) +41253 臻|OSC龙三(男) +41254 臻|OSC龙三(女) +41255 臻|道扬还CF(男) +41256 臻|道扬还CF(女) +41257 臻|软玉城FC(男) +41258 臻|软玉城FC(女) +41259 臻|盛唐联(男) +41260 臻|盛唐联(女) +41261 臻|龙谷SC(男) +41262 臻|龙谷SC(女) +41263 臻|雷山FC(男) +41264 臻|雷山FC(女) +41265 臻|平武圣黄(男) +41266 臻|平武圣黄(女) +41267 臻|皇家素汉(男) +41268 臻|皇家素汉(女) +41269 臻|FC汉武04(男) +41270 臻|FC汉武04(女) +41271 臻|FC上善洞(男) +41272 臻|FC上善洞(女) +41273 臻|SL中善洞(男) +41274 臻|SL中善洞(女) +41275 臻|斯巴达克中郎(男) +41276 臻|斯巴达克中郎(女) +41277 臻|火焰之地CF(男) +41278 臻|火焰之地CF(女) +41279 臻|FC圣升龙(男) +41280 臻|FC圣升龙(女) +41307 黑蛇甲(男) +41308 黑蛇甲(女) +41309 臻|黑蛇甲(男) +41310 臻|黑蛇甲(女) +41311 黑色沙漠战士(男) +41312 黑色沙漠战士(女) +41313 臻|黑色沙漠战士(男) +41314 臻|黑色沙漠战士(女) +41315 破皇甲(男) +41316 破皇甲(女) +41317 臻|破皇甲(男) +41318 臻|破皇甲(女) +41320 南瓜杰克礼服(男) +41321 南瓜杰克礼裙(女) +41322 臻|南瓜杰克礼服(男) +41323 臻|南瓜杰克礼裙(女) +45061 南瓜头 +45062 南瓜头 +45073 臻|南瓜头 +45074 臻|南瓜头 +45123 兔子发型(黑) +45124 兔子发型(蓝) +45125 兔子发型(绿) +45126 兔子发型(粉) +45131 臻|兔子发型(黑) +45132 臻|兔子发型(蓝) +45133 臻|兔子发型(绿) +45134 臻|兔子发型(粉) +45137 臻|黑蛇头盔(男) +45138 臻|黑蛇头盔(女) +45145 臻|基夫韦贝面具 +45146 臻|玛雅面具 +45149 臻|黑头巾(男) +45150 臻|黑头巾(女) +45151 破皇盔(男) +45152 破皇盔(女) +45153 臻|破皇盔(男) +45154 臻|破皇盔(女) +45156 大南瓜头(男) +45157 大南瓜头(女) +45158 臻|大南瓜头(男) +45159 臻|大南瓜头(女) +50067 进化之书 +50068 觉醒之书 +50095 南瓜 +50096 足球 +50118 龙角宝箱 +50119 灵石包裹 +50120 破损手镯箱 +50121 破损项链箱 +50122 破损耳环箱 +50123 冰淇淋 +50124 腐朽的木箱 +50125 不知名的箱子 +50126 不知名的箱子 +50127 神秘翡翠箱 +50128 神秘青玉箱 +50129 神秘紫水晶箱 +50130 奥运宝箱 +50131 神秘黄水晶箱 +50132 神秘红玉箱 +50133 神秘翡翠箱 +50134 神秘青玉箱 +50135 神秘紫水晶箱 +50136 神秘黄玉箱 +50137 神秘黄水晶箱 +50150 红玉钥匙 +50151 翡翠钥匙 +50152 青玉钥匙 +50153 紫水晶钥匙 +50154 黄玉钥匙 +50155 黄水晶钥匙 +50156 钻石钥匙 +50160 复活节彩蛋 +50161 复活节彩蛋 +50162 复活节彩蛋 +50163 复活节彩蛋 +50164 复活节彩蛋 +50165 复活节彩蛋 +50166 复活节彩蛋 +50167 复活节彩蛋 +50168 复活节彩蛋 +50169 复活节彩蛋 +50170 复活节彩蛋 +50171 复活节彩蛋 +50172 复活节彩蛋 +50173 复活节彩蛋 +50174 复活节彩蛋 +50175 复活节彩蛋 +50176 复活节彩蛋 +50177 复活节彩蛋 +50178 复活节彩蛋 +50179 复活节彩蛋 +50180 空篮子 +50181 彩蛋篮 +50182 祝福宝箱 +50183 糖果 +50184 召唤牌兑换券(7天) +50185 召唤牌兑换券(30天) +50186 饿鬼大帝宝箱 +50187 学徒宝箱I +50188 学徒宝箱II +50189 学徒宝箱III +50190 专家宝箱I +50191 专家宝箱II +50192 专家宝箱III +50193 大师宝箱I +50194 大师宝箱II +50195 大师宝箱III +50196 宗师宝箱 +50197 神秘包裹 +50198 神秘包裹 +50199 神秘包裹 +50212 学徒宝箱I +50213 学徒宝箱I +50215 万圣节礼盒 +50216 热红酒 +50249 凯旋宝箱 +50252 龙魂原石 +50253 紫色金箔箱 +50254 黑暗宝箱 +50255 龙魂原石 +50256 华丽龙魂原石 +50257 稀有龙魂原石 +50258 古代龙魂原石 +50259 传说龙魂原石 +50260 龙魂原石 +50614 龙牙原石 +50615 红玉原石 +50616 碧玉原石 +50617 翡翠原石 +50618 青玉原石 +51501 龙魂原石 +51502 龙魂原石 +51503 龙魂原石(至华丽) +51504 龙魂原石(至稀有) +51505 龙魂原石(至古代) +51506 龙魂原石(华丽~传说) +51507 华丽龙魂原石 +51508 稀有龙魂原石 +51509 古代龙魂原石 +51510 传说龙魂原石 +51511 白龙魂原石 +51512 火龙魂原石 +51513 风龙魂原石 +51514 铁龙魂原石 +51515 雷龙魂原石 +51516 黑龙魂原石 +51517 白龙魂原石(至华丽) +51518 火龙魂原石(至华丽) +51519 风龙魂原石(至华丽) +51520 铁龙魂原石(至华丽) +51521 雷龙魂原石(至华丽) +51522 黑龙魂原石(至华丽) +51523 白龙魂原石(至稀有) +51524 火龙魂原石(至稀有) +51525 风龙魂原石(至稀有) +51526 铁龙魂原石(至稀有) +51527 雷龙魂原石(至稀有) +51528 黑龙魂原石(至稀有) +51529 白龙魂原石(至古代) +51530 火龙魂原石(至古代) +51531 风龙魂原石(至古代) +51532 铁龙魂原石(至古代) +51533 雷龙魂原石(至古代) +51534 黑龙魂原石(至古代) +51535 白龙魂原石(至传说) +51536 火龙魂原石(至传说) +51537 风龙魂原石(至传说) +51538 铁龙魂原石(至传说) +51539 雷龙魂原石(至传说) +51540 黑龙魂原石(至传说) +51541 龙魂原石(华丽~稀有) +51542 白龙魂原石(华丽~稀有) +51543 火龙魂原石(华丽~稀有) +51544 风龙魂原石(华丽~稀有) +51545 铁龙魂原石(华丽~稀有) +51546 雷龙魂原石(华丽~稀有) +51547 黑龙魂原石(华丽~稀有) +51548 龙魂原石(全阶) +51549 龙魂原石(华丽~古代) +51550 白龙魂原石(华丽~古代) +51551 火龙魂原石(华丽~古代) +51552 风龙魂原石(华丽~古代) +51553 铁龙魂原石(华丽~古代) +51554 雷龙魂原石(华丽~古代) +51555 黑龙魂原石(华丽~古代) +51556 白龙魂原石(华丽~传说) +51557 火龙魂原石(华丽~传说) +51558 风龙魂原石(华丽~传说) +51559 铁龙魂原石(华丽~传说) +51560 雷龙魂原石(华丽~传说) +51561 黑龙魂原石(华丽~传说) +51562 龙魂原石(稀有~古代) +51563 白龙魂原石(稀有~古代) +51564 火龙魂原石(稀有~古代) +51565 风龙魂原石(稀有~古代) +51566 铁龙魂原石(稀有~古代) +51567 雷龙魂原石(稀有~古代) +51568 黑龙魂原石(稀有~古代) +51569 龙魂原石(稀有~传说) +51570 白龙魂原石(稀有~传说) +51571 火龙魂原石(稀有~传说) +51572 风龙魂原石(稀有~传说) +51573 铁龙魂原石(稀有~传说) +51574 雷龙魂原石(稀有~传说) +51575 黑龙魂原石(稀有~传说) +51576 强龙魂原石(普通~华丽) +51577 强白龙魂原石(普通~华丽) +51578 强火龙魂原石(普通~华丽) +51579 强风龙魂原石(普通~华丽) +51580 强铁龙魂原石(普通~华丽) +51581 强雷龙魂原石(普通~华丽) +51582 强黑龙魂原石(普通~华丽) +51583 强龙魂原石(普通~稀有) +51584 强白龙魂原石(普通~稀有) +51585 强火龙魂原石(普通~稀有) +51586 强风龙魂原石(普通~稀有) +51587 强铁龙魂原石(普通~稀有) +51588 强雷龙魂原石(普通~稀有) +51589 强黑龙魂原石(普通~稀有) +51590 强龙魂原石(普通~古代) +51591 强白龙魂原石(普通~古代) +51592 强火龙魂原石(普通~古代) +51593 强风龙魂原石(普通~古代) +51594 强铁龙魂原石(普通~古代) +51595 强雷龙魂原石(普通~古代) +51596 强黑龙魂原石(普通~古代) +51597 强龙魂原石(普通~传说) +51598 强白龙魂原石(普通~传说) +51599 强火龙魂原石(普通~传说) +51600 强风龙魂原石(普通~传说) +51601 强铁龙魂原石(普通~传说) +51602 强雷龙魂原石(普通~传说) +51603 强黑龙魂原石(普通~传说) +51604 强龙魂原石(华丽~稀有) +51605 强白龙魂原石(华丽~稀有) +51606 强火龙魂原石(华丽~稀有) +51607 强风龙魂原石(华丽~稀有) +51608 强铁龙魂原石(华丽~稀有) +51609 强雷龙魂原石(华丽~稀有) +51610 强黑龙魂原石(华丽~稀有) +51611 强龙魂原石(华丽~古代) +51612 强白龙魂原石(华丽~古代) +51613 强火龙魂原石(华丽~古代) +51614 强风龙魂原石(华丽~古代) +51615 强铁龙魂原石(华丽~古代) +51616 强雷龙魂原石(华丽~古代) +51617 强黑龙魂原石(华丽~古代) +51618 强龙魂原石(华丽~传说) +51619 强白龙魂原石(华丽~传说) +51620 强火龙魂原石(华丽~传说) +51621 强风龙魂原石(华丽~传说) +51622 强铁龙魂原石(华丽~传说) +51623 强雷龙魂原石(华丽~传说) +51624 强黑龙魂原石(华丽~传说) +51625 强龙魂原石(稀有~古代) +51626 强白龙魂原石(稀有~古代) +51627 强火龙魂原石(稀有~古代) +51628 强风龙魂原石(稀有~古代) +51629 强铁龙魂原石(稀有~古代) +51630 强雷龙魂原石(稀有~古代) +51631 强黑龙魂原石(稀有~古代) +51632 强龙魂原石(稀有~传说) +51633 强白龙魂原石(稀有~传说) +51634 强火龙魂原石(稀有~传说) +51635 强风龙魂原石(稀有~传说) +51636 强铁龙魂原石(稀有~传说) +51637 强雷龙魂原石(稀有~传说) +51638 强黑龙魂原石(稀有~传说) +51639 龙魂原石箱(至华丽) +51640 强龙魂原石箱(至华丽) +51641 强龙魂原石套装箱(至华丽) +51642 龙魂原石套装箱(至华丽) +51643 龙魂原石箱(至稀有) +51644 强龙魂原石箱(至稀有) +51645 强龙魂原石套装箱(至稀有) +51646 龙魂原石套装箱(至稀有) +52020 酷|幼年狼召唤牌 +52021 酷|野性狼召唤牌 +52022 酷|野性狼召唤牌 +52023 酷|野性狼召唤牌 +52024 酷|野性狼召唤牌 +52025 酷|野性狼召唤牌 +52026 酷|勇猛狼召唤牌 +52027 酷|勇猛狼召唤牌 +52028 酷|勇猛狼召唤牌 +52029 酷|勇猛狼召唤牌 +52030 酷|勇猛狼召唤牌 +52031 酷|幼年狮召唤牌 +52032 酷|幼年狮召唤牌 +52033 酷|幼年狮召唤牌 +52034 酷|幼年狮召唤牌 +52035 酷|幼年狮召唤牌 +52036 酷|野性狮召唤牌 +52037 酷|野性狮召唤牌 +52038 酷|野性狮召唤牌 +52039 酷|野性狮召唤牌 +52040 酷|野性狮召唤牌 +52041 酷|勇猛狮召唤牌 +52042 酷|勇猛狮召唤牌 +52043 酷|勇猛狮召唤牌 +52044 酷|勇猛狮召唤牌 +52045 酷|勇猛狮召唤牌 +52046 酷|幼年虎召唤牌 +52047 酷|幼年虎召唤牌 +52048 酷|幼年虎召唤牌 +52049 酷|幼年虎召唤牌 +52050 酷|幼年虎召唤牌 +52051 酷|野性虎召唤牌 +52052 酷|野性虎召唤牌 +52053 酷|野性虎召唤牌 +52054 酷|野性虎召唤牌 +52055 酷|野性虎召唤牌 +52056 酷|勇猛虎召唤牌 +52057 酷|勇猛虎召唤牌 +52058 酷|勇猛虎召唤牌 +52059 酷|勇猛虎召唤牌 +52060 酷|勇猛虎召唤牌 +52061 酷|幼年驯鹿召唤牌 +52062 酷|幼年驯鹿召唤牌 +52063 酷|幼年驯鹿召唤牌 +52064 酷|幼年驯鹿召唤牌 +52065 酷|幼年驯鹿召唤牌 +52066 酷|野性驯鹿召唤牌 +52067 酷|野性驯鹿召唤牌 +52068 酷|野性驯鹿召唤牌 +52069 酷|野性驯鹿召唤牌 +52070 酷|野性驯鹿召唤牌 +52071 酷|勇猛驯鹿召唤牌 +52072 酷|勇猛驯鹿召唤牌 +52073 酷|勇猛驯鹿召唤牌 +52074 酷|勇猛驯鹿召唤牌 +52075 酷|勇猛驯鹿召唤牌 +52076 酷|幼年驯鹿召唤牌 +52077 酷|幼年驯鹿召唤牌 +52078 酷|幼年驯鹿召唤牌 +52079 酷|幼年驯鹿召唤牌 +52080 酷|幼年驯鹿召唤牌 +52081 酷|野性驯鹿召唤牌 +52082 酷|野性驯鹿召唤牌 +52083 酷|野性驯鹿召唤牌 +52084 酷|野性驯鹿召唤牌 +52085 酷|野性驯鹿召唤牌 +52086 酷|勇猛驯鹿召唤牌 +52087 酷|勇猛驯鹿召唤牌 +52088 酷|勇猛驯鹿召唤牌 +52089 酷|勇猛驯鹿召唤牌 +52090 酷|勇猛驯鹿召唤牌 +52091 酷|幼年石龙子召唤牌 +52092 酷|幼年石龙子召唤牌 +52093 酷|幼年石龙子召唤牌 +52094 酷|幼年石龙子召唤牌 +52095 酷|幼年石龙子召唤牌 +52096 酷|野性石龙子召唤牌 +52097 酷|野性石龙子召唤牌 +52098 酷|野性石龙子召唤牌 +52099 酷|野性石龙子召唤牌 +52100 酷|野性石龙子召唤牌 +52101 酷|勇猛石龙子召唤牌 +52102 酷|勇猛石龙子召唤牌 +52103 酷|勇猛石龙子召唤牌 +52104 酷|勇猛石龙子召唤牌 +52105 酷|勇猛石龙子召唤牌 +52701 战甲狮兑换券 +52702 战甲虎兑换券 +52703 战甲野猪兑换券 +52704 战甲狼兑换券 +52705 战甲公驯鹿兑换券 +52706 战甲母驯鹿兑换券 +52707 战甲石龙子兑换券 +53501 鹿角保管箱 +53502 幼驯鹿保管箱 +53503 冰凤凰笼 +53504 鹿角保管箱 +53505 鹿角保管箱 +53506 鹿角保管箱 +53516 骨箱 +53517 玉笼 +70056 石榴汁+ +70057 勇气披风 +70058 移动戒指 +71099 帮主更替书 +71100 军职初始化 +71101 速灵酒 +71102 速灵酒+ +71103 体力初始化 +71104 智力初始化 +71105 力量初始化 +71106 敏捷初始化 +71114 野猪坐骑券(黄) +71115 野猪坐骑券(蓝) +71116 狼坐骑券(黄) +71117 狼坐骑券(蓝) +71119 虎坐骑券(蓝) +71120 狮坐骑券(黄) +71121 狮坐骑券(蓝) +71122 水龙房入场券 +71123 水龙鳞片 +71124 白狮坐骑券 +71125 战野猪坐骑券 +71126 战狼坐骑券 +71127 风暴虎坐骑券 +71128 战狮坐骑券 +71129 水龙爪 +71130 通行证明书 +71131 黑马召唤券 +71132 黑马召唤券 +71133 黑马召唤券 +71134 黑马召唤券 +71136 棒棒糖 +71137 皇虎召唤券(蓝) +71138 皇虎召唤券(红) +71139 皇虎召唤券(金) +71140 皇虎召唤券(绿) +71141 皇虎召唤券(灰) +71142 皇虎召唤券(白) +71144 圣诞礼盒 +71145 永恒之爱吊坠 +71146 丘比特宝箱(粉) +71147 丘比特宝箱(蓝) +71150 魔法蛋 +71157 经验药水 +71158 英雄勋章 +71159 米隆宝箱 +71160 雅典娜宝箱 +71161 万圣黑马召唤券 +71164 彗星驯鹿召唤券 +71165 战甲白熊召唤牌 +71166 战甲熊猫召唤牌 +71171 白独角兽召唤牌 +71172 梦魇召唤牌 +72301 祝福之书 +72302 兵法六统 +72303 经验戒指 +72304 祝福之珠 +72305 消失手镜 +72306 武器匠的笔记 +72307 武器匠秘籍 +72308 万年寒铁 +72309 高手指南针 +72310 咒安秘籍 +72311 龙神之丸 +72312 龙神的攻击 +72313 龙神的防御 +72314 龙神的祝福书 +76000 万能药 +76001 属性初始化秘籍 +76002 死神证物 +76003 速灵酒 +76004 蓝龙的祝福(小) +76005 蓝龙的祝福(中) +76006 共鸣石 +76007 勇气披风 +76008 龙神的保护 +76009 龙神的祝福书 +76010 血石 +76011 兵法六统 +76012 加速酒 +76013 追加物品属性秘籍 +76014 物品属性转换秘籍 +76015 祝福之珠 +76016 祝福之书 +76017 快速酒 +76018 快速酒 +76019 通行证明书 +76020 迷惑药水 +76021 红龙的祝福(小) +76022 红龙的祝福(中) +76023 初级物品属性转换秘笈 +76024 初级追加物品属性秘笈 +80015 龙币兑换券(500) +80016 龙币兑换券(1000) +80017 龙币兑换券(50) +100000 时间灵药(小) +100001 时间灵药(中) +100002 时间灵药(大) +100100 龙魂石提取器 +100200 时间灵药提取器 +100300 绿龙豆 +100400 蓝龙豆 +100500 粉龙豆 +110000 普通白龙石 +110100 普通白龙石 +110200 普通白龙石 +110300 普通白龙石 +110400 普通白龙石 +111000 华丽白龙石 +111100 华丽白龙石 +111200 华丽白龙石 +111300 华丽白龙石 +111400 华丽白龙石 +112000 稀有白龙石 +112100 稀有白龙石 +112200 稀有白龙石 +112300 稀有白龙石 +112400 稀有白龙石 +113000 古代白龙石 +113100 古代白龙石 +113200 古代白龙石 +113300 古代白龙石 +113400 古代白龙石 +114000 传说白龙石 +114100 传说白龙石 +114200 传说白龙石 +114300 传说白龙石 +114400 传说白龙石 +120000 普通火龙石 +120100 普通火龙石 +120200 普通火龙石 +120300 普通火龙石 +120400 普通火龙石 +121000 华丽火龙石 +121100 华丽火龙石 +121200 华丽火龙石 +121300 华丽火龙石 +121400 华丽火龙石 +122000 稀有火龙石 +122100 稀有火龙石 +122200 稀有火龙石 +122300 稀有火龙石 +122400 稀有火龙石 +123000 古代火龙石 +123100 古代火龙石 +123200 古代火龙石 +123300 古代火龙石 +123400 古代火龙石 +124000 传说火龙石 +124100 传说火龙石 +124200 传说火龙石 +124300 传说火龙石 +124400 传说火龙石 +130000 普通风龙石 +130100 普通风龙石 +130200 普通风龙石 +130300 普通风龙石 +130400 普通风龙石 +131000 华丽风龙石 +131100 华丽风龙石 +131200 华丽风龙石 +131300 华丽风龙石 +131400 华丽风龙石 +132000 稀有风龙石 +132100 稀有风龙石 +132200 稀有风龙石 +132300 稀有风龙石 +132400 稀有风龙石 +133000 古代风龙石 +133100 古代风龙石 +133200 古代风龙石 +133300 古代风龙石 +133400 古代风龙石 +134000 传说风龙石 +134100 传说风龙石 +134200 传说风龙石 +134300 传说风龙石 +134400 传说风龙石 +140000 普通铁龙石 +140100 普通铁龙石 +140200 普通铁龙石 +140300 普通铁龙石 +140400 普通铁龙石 +141000 华丽铁龙石 +141100 华丽铁龙石 +141200 华丽铁龙石 +141300 华丽铁龙石 +141400 华丽铁龙石 +142000 稀有铁龙石 +142100 稀有铁龙石 +142200 稀有铁龙石 +142300 稀有铁龙石 +142400 稀有铁龙石 +143000 古代铁龙石 +143100 古代铁龙石 +143200 古代铁龙石 +143300 古代铁龙石 +143400 古代铁龙石 +144000 传说铁龙石 +144100 传说铁龙石 +144200 传说铁龙石 +144300 传说铁龙石 +144400 传说铁龙石 +150000 普通雷龙石 +150100 普通雷龙石 +150200 普通雷龙石 +150300 普通雷龙石 +150400 普通雷龙石 +151000 华丽雷龙石 +151100 华丽雷龙石 +151200 华丽雷龙石 +151300 华丽雷龙石 +151400 华丽雷龙石 +152000 稀有雷龙石 +152100 稀有雷龙石 +152200 稀有雷龙石 +152300 稀有雷龙石 +152400 稀有雷龙石 +153000 古代雷龙石 +153100 古代雷龙石 +153200 古代雷龙石 +153300 古代雷龙石 +153400 古代雷龙石 +154000 传说雷龙石 +154100 传说雷龙石 +154200 传说雷龙石 +154300 传说雷龙石 +154400 传说雷龙石 +160000 普通黑龙石 +160100 普通黑龙石 +160200 普通黑龙石 +160300 普通黑龙石 +160400 普通黑龙石 +161000 华丽黑龙石 +161100 华丽黑龙石 +161200 华丽黑龙石 +161300 华丽黑龙石 +161400 华丽黑龙石 +162000 稀有黑龙石 +162100 稀有黑龙石 +162200 稀有黑龙石 +162300 稀有黑龙石 +162400 稀有黑龙石 +163000 古代黑龙石 +163100 古代黑龙石 +163200 古代黑龙石 +163300 古代黑龙石 +163400 古代黑龙石 +164000 传说黑龙石 +164100 传说黑龙石 +164200 传说黑龙石 +164300 传说黑龙石 +164400 传说黑龙石 diff --git a/data/zh_40250/locale_game_zh_overlay.txt b/data/zh_40250/locale_game_zh_overlay.txt new file mode 100644 index 00000000..97c0aac1 --- /dev/null +++ b/data/zh_40250/locale_game_zh_overlay.txt @@ -0,0 +1,46 @@ +KEY VALUE +CANNOT_USE 无法使用。 +CHANNEL_NOTIFY_FULL 该频道已满!请选择其他频道。 +DRAGON_SOUL_CANNOT_REFINE 该龙魂石无法强化。 +DRAGON_SOUL_CANNOT_REFINE_MORE 该龙魂石已达到最高等级。 +DRAGON_SOUL_EQUIP_WARNING1 龙魂石一旦装备便很难取下。 +DRAGON_SOUL_EQUIP_WARNING2 确定要继续吗? +DRAGON_SOUL_EXPIRED 已过期的龙魂石 +DRAGON_SOUL_INVALID_DRAGON_SOUL 龙魂石不正确。 +DRAGON_SOUL_IS_NOT_DRAGON_SOUL 这不是龙魂石。 +DRAGON_SOUL_NOT_DRAGON_SOUL_REFINE_STONE 这不是正确的龙魂石。 +DRAGON_SOUL_NOT_ENOUGH_MATERIAL 强化材料不足。 +DRAGON_SOUL_NOT_MATCHED_SLOT 该龙魂石不适合这个栏位。 +DRAGON_SOUL_STEP_LEVEL1 阶段:最低级, +DRAGON_SOUL_STEP_LEVEL2 阶段:低级, +DRAGON_SOUL_STEP_LEVEL3 阶段:中级, +DRAGON_SOUL_STEP_LEVEL4 阶段:高级, +DRAGON_SOUL_STEP_LEVEL5 阶段:最高级, +DRAGON_SOUL_STRENGTH 强化:+%d +DRAGON_SOUL_UNEQUIP_WARNING1 取下时龙魂石可能会损坏。 +DRAGON_SOUL_UNEQUIP_WARNING2 确定要继续吗? +DRAGON_SOUL_UNMATCHED_SLOT 栏位不正确。 +FISHING_NOTIFY1 好像钓到了%s。 +FISHING_NOTIFY2 %s好像上钩了。 +FISHING_SUCCESS1 钓到了%s! +FISHING_SUCCESS2 把%s从水里拉上来了! +GUILD_MARK_NOT_ENOUGH_LEVEL 帮会等级需达到3级 +HOW_MANY_ITEM_DO_YOU_DROP2 确定要扔掉%s %d个吗? +LOGIN_FAILURE_BLOCK_LOGIN 请选择其他服务器! +LOGIN_FAILURE_UNKNOWN 因未知原因无法登录。 +REFINE_FAILURE_CAN_NOT_ATTACH %s无法镶嵌到该物品上。 +REFINE_FAILURE_NO_GOLD_SOCKET 没有可镶嵌%s的金色插槽。 +REFINE_FAILURE_NO_SOCKET0 没有可镶嵌%s的插槽。 +TOOLTIP_ATT_SPEED 攻击速度 : +%d%% +TOOLTIP_COSTUME_ATTR_BONUS 时装属性 %d%% +TOOLTIP_DRAGON_SOUL_DECK1 地 +TOOLTIP_DRAGON_SOUL_DECK2 天 +TOOLTIP_HP_REGEN 生命恢复量 : +%d%% +TOOLTIP_MOV_SPEED 移动速度 : %d%% +TOOLTIP_RESIST_DARK 黑暗抗性 +%d%% +TOOLTIP_RESIST_EARTH 大地抗性 +%d%% +TOOLTIP_RESIST_ICE 冰冻抗性 +%d%% +TOOLTIP_SKILL_DURATION 持续时间 : %d秒 +TOOLTIP_SP_REGEN 精力恢复量 : +%d%% +TOOLTIP_TIME_CHARGER_FIX 时间充能 %d秒 +TOOLTIP_TIME_CHARGER_PER 时间充能 %d%% diff --git a/data/zh_40250/mob_names_zh_overlay.txt b/data/zh_40250/mob_names_zh_overlay.txt new file mode 100644 index 00000000..2666751d --- /dev/null +++ b/data/zh_40250/mob_names_zh_overlay.txt @@ -0,0 +1,232 @@ +VNUM LOCALE_NAME +101 幼狼 +1096 亡灵战神 +1308 真狼神守护者 +1309 真狼神守护者 +1310 狼神守护者 +1904 真三尾银狐 +1905 真三尾银狐 +1906 真三尾银狐 +6001 火精 +6002 炎精 +6003 极热精 +6004 血火精 +6005 火魔 +6006 炎魔 +6007 火怪 +6008 炎怪 +6009 超热怪 +6051 真超热怪 +6091 夜摩天 +6101 冰精 +6102 冷精 +6103 极冰精 +6104 寒冰精 +6105 冰傀魔 +6106 冷魔 +6107 冰怪 +6108 冷怪 +6109 超冷怪 +6151 真超冷怪 +6191 寒魔 +7029 冰魔 +7030 冰魔鲸 +7031 冰异虫 +7032 雪地兽 +7033 冰魔人 +7034 雪魔兽人 +7035 冰雕魔 +7036 冰魔 +7037 冰魔鲸 +7038 冰异虫 +7039 雪地兽 +7040 冰魔人 +7041 雪魔兽人 +7042 冰雕魔 +7043 冰魔 +7044 冰魔鲸 +7045 冰异虫 +7046 雪地兽 +7047 冰魔人 +7048 雪魔兽人 +7049 冰雕魔 +7050 破坏者 +7051 蝎人王 +7052 蝎人 +7053 蝎人弓箭手 +7054 半兽刀兵 +7055 半兽弓箭手 +7056 裁决者 +7057 破坏者 +7058 蝎人王 +7059 蝎人 +7060 蝎人弓箭手 +7061 半兽刀兵 +7062 半兽弓箭手 +7063 裁决者 +7064 破坏者 +7065 蝎人王 +7066 蝎人 +7067 蝎人弓箭手 +7068 半兽刀兵 +7069 半兽弓箭手 +7070 裁决者 +7071 火云虎 +7072 火灵 +7073 焰赤虎 +7074 夜魔 +7075 赤皇武夫 +7076 火云虎 +7077 火灵 +7078 焰赤虎 +7079 夜魔 +7080 赤皇武夫 +7081 火云虎 +7082 火灵 +7083 焰赤虎 +7084 夜魔 +7085 赤皇武夫 +7086 天刹鬼木 +7087 青叶妖姬 +7088 狂暴树鬼 +7089 邪魔树鬼 +7090 天刹鬼木 +7091 青叶妖姬 +7092 狂暴树鬼 +7093 邪魔树鬼 +7094 天刹鬼木 +7095 青叶妖姬 +7096 狂暴树鬼 +8041 复活节陨石 +8042 复活节陨石 +8043 复活节陨石 +8044 复活节陨石 +8045 复活节陨石 +8046 复活节陨石 +8047 复活节陨石 +8048 复活节陨石 +8049 复活节陨石 +8050 复活节陨石 +8051 魂魔石 +8052 鬼魔石 +8053 暴魔石 +8054 龙魔石 +8055 地魔石 +8056 亡魔石 +8057 炼狱火魔石 +8101 鱼跃陨石 +8102 蝴仙陨石 +8103 鹿鸣陨石 +8104 鹰神陨石 +8105 盘蛇陨石 +8106 虎啸陨石 +8107 狮吼陨石 +8108 兽目陨石 +8109 凤凰陨石 +8110 麒麟陨石 +8111 蛟龙陨石 +8600 真鬼族守护神 +8601 真鬼族守护神 +8602 真秘宗教主 +8603 真秘宗教主 +8604 真亡灵战神 +8605 真亡灵战神 +8606 真死神 +8607 真死神 +8608 真三尾银狐 +8609 真三尾银狐 +8610 真狼神守护者 +8611 真狼神守护者 +8612 真圣母蛛 +8613 真圣母蛛 +8614 真龟龙王 +8615 真龟龙王 +20035 战旗 +20036 战旗 +20037 战旗 +20110 野猪 +20201 野猪坐骑 +20202 狼坐骑 +20203 虎坐骑 +20204 狮子坐骑 +20205 战甲野猪 +20206 战甲狼 +20207 战甲虎 +20208 战甲狮子 +20209 战甲野猪 +20210 战甲狼 +20211 战甲虎 +20212 战甲狮子 +20213 公驯鹿 +20214 战甲公驯鹿 +20215 战甲公驯鹿 +20216 母驯鹿 +20217 战甲母驯鹿 +20218 战甲母驯鹿 +20219 万圣黑马 +20367 墓穴守卫 +20368 铁算盘 +20369 李忠 +20370 中毒的士兵 +20371 古代石碑 +20372 残旧石碑 +20373 护卫 +20374 卫兵 +20375 木栅 +20376 蔡基雄 +20377 药还 +20378 警备队长岳穆尔 +20379 受伤的士兵 +20380 学者 +20381 村长 +20382 石花 +20383 工匠莫雷汉 +20384 圣诞树 +20385 赤龙城碑 +20386 赤龙封印碑 +20387 命运之锁 +20388 赤龙结界门 +20389 夜摩天石 +20390 赤龙封印石 +20391 太阳草 +20392 阵亡的士兵 +20393 负伤的士兵 +20394 火焰之地卫兵 +20395 冰原守卫 +30101 库德石像 +30102 玄武岩方尖碑 +30103 龟岩 +30104 符文桩 +30111 第一道地狱之门 +30112 第二道地狱之门 +30113 第三道地狱之门 +30114 第四道地狱之门 +30115 第五道地狱之门 +30116 第六道地狱之门 +30117 第七道地狱之门 +30118 第八道地狱之门 +30119 第九道地狱之门 +30120 药草袋 +30121 修罗之魂 +30122 猛将之魂 +30123 暗黑祭坛 +30124 修罗骸骨1 +30125 修罗骸骨2 +30126 修罗骸骨3 +30127 修罗骸骨4 +30128 修罗骸骨5 +30129 复活节兔子 +30130 墓碑 +30301 龙牙矿脉 +30302 红玉矿脉 +30303 碧玉矿脉 +30304 翡翠矿脉 +30305 青玉矿脉 +33001 修罗骸骨 +33007 饥民 +33008 南瓜杰克 +33009 龙牙炼圣 +33010 红玉炼圣 +33011 碧玉炼圣 +33012 翡翠炼圣 +33013 青玉炼圣 diff --git a/docs/ZH-LOCALE.md b/docs/ZH-LOCALE.md index 656a9a6f..727cdc83 100644 --- a/docs/ZH-LOCALE.md +++ b/docs/ZH-LOCALE.md @@ -1,22 +1,42 @@ -# 40250 简体中文适配(进行中) +# 40250 简体中文适配 -## 当前可运行原型 - -先运行 `cmake --build build-release --target locale_proto_patch`,再运行 -`python3 script/build_zh_locale.py`,在 `build/zh-locale/Client` 生成独立的测试客户端目录。 -它复制 40250 `Eternexus/locale_en/locale/en` 的文件结构,配置 `locale.cfg` 为 -`10002 936 zh`,并将中文内容按键合入 `locale_game.txt`、`locale_interface.txt`。 -旧龙驹语言包中的占位符与 40250 不一致时保留英文原文;报告在 -`build/zh-locale/zh-locale-report.json`。职业与帝国说明采用龙驹中文文本。 - -生成目录通过符号链接共用仓库 `Client` 的 pack 等资源,仅用于本机验证; -不会修改原始 40250 客户端,也不是可分发包。可用以下命令启动: +## 语言包(2026-09-28 起为正式 pack) ```sh -build-release/native_render/mt_native_render \ +cmake --build build-native --target locale_proto_patch eterpack_pack +python3 script/build_zh_locale.py +``` + +生成器复制 40250 `Eternexus/locale_en/locale/en` 的文件结构到暂存目录 +`build/zh-locale/locale-src/zh`,将中文内容按键合入 `locale_game.txt`、`locale_interface.txt`。 +旧龙驹语言包缺失或占位符与 40250 不一致的键,改用 `data/zh_40250/locale_game_zh_overlay.txt` +中经审核的译文(同样校验占位符);报告在 +`build/zh-locale/zh-locale-report.json`。职业与帝国说明采用龙驹中文文本。 + +暂存目录随后由 `eterpack_pack`(`extension/tests/eterpack_pack.cpp`,直接调用移植的 40250 +`CEterPack::Put` / `EncryptIndexFile`)打成 `locale_zh.eix/.epk`,格式与原版 `locale_en` 等 +相同:231 个文件全部为 COMPRESS(LZO),索引经 LZO + TEA 加密。工具写完后以只读方式 +重新打开 pack,逐个经 `CEterPack::Get` 读回并与源文件比对。用同一工具重打原版 +`locale_en` 源文件,文件名、类型、CRC 和解压内容与原版 pack 逐项一致。 + +输出客户端 `build/zh-locale/Client`: + +- `pack/`:原版 pack 逐个符号链接,加上 `locale_zh.eix/.epk`,以及在 `locale/en/` 之前 + 注册 `locale/zh/` → `locale_zh` 的 `Index`(CRLF,与原版一致)。 +- `locale.cfg` 与 `locale_zh.cfg`:`10002 936 zh`。 +- 不留散装的 `locale/zh`:客户端默认先查散装文件,留着会绕过 pack。 + +仓库 `Client/` 是 `audit/assets/40250-client.json` 校验的原版资源,不做修改。 +macOS 启动: + +```sh +build-native/native_render/mt_native_render \ --live-client build/zh-locale/Client --login-screen --width 1024 --height 768 ``` +Android:`./push-android-client.sh --locale zh`(默认推送 `build/zh-locale/Client`,符号链接按 +目标文件复制,并以 `locale_zh.cfg` 写入设备上的 `locale.cfg`)。 + 物品与怪物原型从 40250 原版解压,按线上 40250 服务端 `gb2312name` 的 VNUM 快照 只替换 `szLocaleName` 字段后按原格式 重新压缩加密;生成器验证记录数、结构大小、除名称外所有字节不变,以及写出后的 @@ -25,11 +45,15 @@ build-release/native_render/mt_native_render \ ## 已核对的差异 -- 40250 `locale_game.txt` 有 817 个键;当前 771 个安全替换为中文,46 个保留英文。 - 其中 12 个在龙驹文件中的格式占位符不一致,需逐条重译或验证。 +- 40250 `locale_game.txt` 有 817 个键;771 个来自龙驹,45 个来自审核译文, + 仅 `TOOLTIP_UNSEAL_LEFT_TIME` 保留英文原文(原文为空,40250 脚本未使用)。 - 40250 `locale_interface.txt` 的 326 个键均找到中文对应项。 - 40250 客户端物品原型 5,743 条、怪物/NPC 原型 1,334 条都已按 VNUM 写入 - `data/zh_40250/` 中的服务端名称快照;其中分别有 1,608 和 346 个名称仍不含汉字。 + `data/zh_40250/` 中的服务端名称快照;快照里分别有 1,608 和 346 个名称不含汉字。 + 2026-09-28 审核确认的译文放在 `item_names_zh_overlay.txt`(1,607 条)和 + `mob_names_zh_overlay.txt`(231 条),构建时覆盖快照同 VNUM 的名称;快照本身保持服务端原样。 + 剩余英文仅为 vnum 2 占位物品 `YMIR` 和 115 个名称为地图坐标的传送点标记(游戏内不显示)。 + 客户端显示译名,服务端数据库仍为英文,服务端发出的含名称消息会显示英文。 - 40250 服务端(`192.168.21.203`,2026-09-27 只读 SSH 核对)当前数据库配置为 `LOCALE=newcibn`、`DB_NAME_COLUMN=gb2312name`,有 `newcibn` 语言目录。 因此客户端采用 CP936;服务端广播字典已在 2026-09-28 完成首轮补齐,名称与任务文本仍需核对。 @@ -40,12 +64,13 @@ build-release/native_render/mt_native_render \ ## 后续验收 -1. 根据线上 40250 VNUM 及用途逐条翻译仍为英文的 1,608 个物品、346 个怪物/NPC; - 同步更新服务端名称和客户端快照。`itemdesc`、`skilldesc` 也需按 40250 键和语义逐项合并。 -2. 补齐 46 个英文回退键,复核 12 个格式不兼容键。 +1. 物品/怪物译名和界面回退键已在客户端补齐(overlay);是否同步写入服务端 + `gb2312name` 需另行核对后决定。`itemdesc`、`skilldesc` 仍需按 40250 键和语义逐项合并。 +2. (已完成)46 个英文回退键已补齐。 3. 在游戏窗口验证中文字符、字体、输入法、换行和 UI 宽度,再联机检查登录、背包、 商店、任务、公告及聊天。 -4. 完成后制作注册到 `pack/Index` 的中文 pack,并在 macOS 和 Android 上分别测试。 +4. 中文 pack 已注册到 `pack/Index`(见上);macOS 登录界面已确认从 pack 读取中文, + Android 真机/模拟器待测。 ## 服务端适配进度(2026-09-28) diff --git a/extension/src/port/CMakeLists.txt b/extension/src/port/CMakeLists.txt index 1fb3705d..0f2c5a06 100644 --- a/extension/src/port/CMakeLists.txt +++ b/extension/src/port/CMakeLists.txt @@ -238,6 +238,9 @@ if(BUILD_TESTING AND CMAKE_SYSTEM_NAME STREQUAL CMAKE_HOST_SYSTEM_NAME) # Offline locale data builder: retains 40250 proto layout and gameplay fields. add_executable(locale_proto_patch ${CMAKE_CURRENT_SOURCE_DIR}/../../tests/locale_proto_patch.cpp) target_link_libraries(locale_proto_patch PRIVATE port_platform) + # Locale packs in the original format (script/build_zh_locale.py packs locale/zh with it). + add_executable(eterpack_pack ${CMAKE_CURRENT_SOURCE_DIR}/../../tests/eterpack_pack.cpp) + target_link_libraries(eterpack_pack PRIVATE port_platform) # 2V2-e: the same fake server as a process, for the Godot-driven render run # (script/python_game_render_test.sh). add_executable(port_fake_login_server ${CMAKE_CURRENT_SOURCE_DIR}/../../tests/port_fake_login_server_main.cpp diff --git a/extension/tests/eterpack_pack.cpp b/extension/tests/eterpack_pack.cpp new file mode 100644 index 00000000..91c6c5a7 --- /dev/null +++ b/extension/tests/eterpack_pack.cpp @@ -0,0 +1,105 @@ +// eterpack_pack — build a 40250 pack (.eix/.epk) from a directory with the ported CEterPack, the way +// the original locale packs are made: every file COMPRESS (LZO), index LZO + TEA encrypted. +// +// eterpack_pack +// +// Pack names are , lowercased with '/' (inlineConvertPackFilename). +// An existing .eix/.epk is replaced. The result is reopened read-only and every file read back +// through CEterPack::Get and compared with its source. +#include "EterPack/StdAfx.h" +#include "EterPack/EterPack.h" +#include "EterPack/EterPackManager.h" +#include "EterBase/MappedFile.h" +#include "../src/platform/UserInterface/UserInterface.h" + +#include +#include +#include +#include +#include +#include +#include + +namespace fs = std::filesystem; + +static std::vector read_file(const fs::path& path) +{ + std::ifstream in(path, std::ios::binary); + return {std::istreambuf_iterator(in), std::istreambuf_iterator()}; +} + +int main(int argc, char** argv) +{ + if (argc != 4) + { + std::fprintf(stderr, "usage: %s \n", argv[0]); + return 2; + } + const std::string db = argv[1]; + const fs::path source = argv[2]; + std::string prefix = argv[3]; + if (!prefix.empty() && prefix.back() != '/') + prefix += '/'; + + std::vector> files; + for (const auto& e : fs::recursive_directory_iterator(source)) + if (e.is_regular_file()) + files.emplace_back(prefix + e.path().lexically_relative(source).generic_string(), e.path()); + std::sort(files.begin(), files.end()); + if (files.empty()) + { + std::fprintf(stderr, "no files under %s\n", source.c_str()); + return 1; + } + + PackSingletons(); + std::error_code ec; + fs::remove(db + ".eix", ec); + fs::remove(db + ".epk", ec); + { + CEterFileDict dict; + CEterPack pack; + if (!pack.Create(dict, db.c_str(), "", false)) + { + std::fprintf(stderr, "cannot create %s.eix/.epk\n", db.c_str()); + return 1; + } + for (const auto& [name, path] : files) + { + const std::vector data = read_file(path); + if (name.size() > FILENAME_MAX_LEN || !pack.Put(name.c_str(), data.data(), long(data.size()), COMPRESSED_TYPE_COMPRESS)) + { + std::fprintf(stderr, "cannot put %s\n", name.c_str()); + return 1; + } + } + if (!pack.EncryptIndexFile()) + return 1; + } + + // Read back as the client does. + CEterFileDict dict; + CEterPack pack; + if (!pack.Create(dict, db.c_str(), prefix.c_str(), true)) + { + std::fprintf(stderr, "cannot reopen %s\n", db.c_str()); + return 1; + } + int failures = 0; + for (const auto& [name, path] : files) + { + const std::vector want = read_file(path); + CMappedFile mapped; + LPCVOID data = nullptr; + if (!pack.Get(mapped, name.c_str(), &data) || mapped.Size() != want.size() || + (!want.empty() && std::memcmp(data, want.data(), want.size()) != 0)) + { + std::fprintf(stderr, "read-back mismatch: %s\n", name.c_str()); + ++failures; + } + } + if (failures) + return 1; + std::printf("%s: %zu files, COMPRESS, encrypted index\n", db.c_str(), files.size()); + return 0; +} diff --git a/script/build_zh_locale.py b/script/build_zh_locale.py index 75515986..5d191798 100644 --- a/script/build_zh_locale.py +++ b/script/build_zh_locale.py @@ -3,6 +3,12 @@ The Longju locale is an older client version. Only keyed UI text with compatible format arguments is overlaid; 40250 proto, scripts, and UI layouts stay intact. + +The files are staged in build/zh-locale/locale-src/zh and packed like the original +locale packs (eterpack_pack: COMPRESS entries, encrypted index) into locale_zh.eix/.epk. +The output Client has a pack/ of its own: links to the original packs, the zh pack and +an Index registering locale/zh/ -> locale_zh. No loose locale/zh is left next to it, +since the client searches loose files first. """ import argparse @@ -17,7 +23,8 @@ REPO = Path(__file__).resolve().parents[1] DEFAULT_EN = REPO.parent / "40250/Server Client TMP4/Client/Eternexus/locale_en/locale/en" DEFAULT_ZH = REPO.parent / "server_extracted/client_locale/longju/locale/taiwan" DEFAULT_OUTPUT = REPO / "build/zh-locale/Client" -DEFAULT_PROTO_TOOL = REPO / "build-release/extension/src/port/locale_proto_patch" +BUILD_DIRS = ("build-release", "build-native") +LOCALE_CFG = "10002 936 zh\n" DEFAULT_NAMES = REPO / "data/zh_40250" FORMAT = re.compile(r"%(?:\([^)]*\))?[-+#0 ]*\d*(?:\.\d+)?[diouxXeEfFgGcrs%]") KEYED_FILES = ("locale_game.txt", "locale_interface.txt") @@ -33,35 +40,99 @@ def keyed_lines(path: Path, encoding: str): return lines, values -def convert_names(source: Path, destination: Path): +def name_rows(path: Path): + rows = {} + if path.is_file(): + for line in path.read_text(encoding="utf-8").splitlines(): + parts = line.split("\t", 1) + if len(parts) == 2 and parts[0].isdigit(): + rows[parts[0]] = parts[1] + return rows + + +def convert_names(source: Path, destination: Path, overlay: Path = None): + """The server name snapshot, with the reviewed translations of its English names laid over it.""" + names = name_rows(source) + overlaid = {vnum: name for vnum, name in name_rows(overlay).items() if vnum in names} if overlay else {} + names.update(overlaid) converted = 0 unrepresentable = [] rows = [] - for line in source.read_text(encoding="utf-8").splitlines(): - parts = line.split("\t", 1) - if len(parts) != 2 or not parts[0].isdigit(): - continue + for vnum, name in names.items(): try: - rows.append(f"{parts[0]}\t{parts[1]}\n".encode("gbk")) + rows.append(f"{vnum}\t{name}\n".encode("gbk")) converted += 1 except UnicodeEncodeError: - unrepresentable.append(parts[0]) + unrepresentable.append(vnum) destination.write_bytes(b"".join(rows)) - return {"converted": converted, "unrepresentable_vnums": unrepresentable} + return {"converted": converted, "overlaid": len(overlaid), "unrepresentable_vnums": unrepresentable} + + +def tool(name: str) -> Path: + for build_dir in BUILD_DIRS: + path = REPO / build_dir / "extension/src/port" / name + if path.is_file(): + return path + return REPO / BUILD_DIRS[0] / "extension/src/port" / name + + +def register(index: bytes) -> bytes: + """pack/Index with locale/zh/ -> locale_zh added before the locale/en/ pair (CRLF, as the original).""" + lines = index.split(b"\r\n") + if b"locale/zh/" in lines: + return index + at = lines.index(b"locale/en/") + if at % 2 != 1: + raise ValueError("pack/Index: locale/en/ is not a folder line") + lines[at:at] = [b"locale/zh/", b"locale_zh"] + return b"\r\n".join(lines) + + +def make_pack_dir(client_base: Path, pack_dir: Path, locale_dir: Path, pack_tool: Path): + source = client_base / "pack" + if pack_dir.is_symlink(): + pack_dir.unlink() # earlier builds linked the whole directory + pack_dir.mkdir(parents=True, exist_ok=True) + for entry in source.iterdir(): + if entry.name == "Index" or entry.name.lower().startswith("locale_zh."): + continue + target = pack_dir / entry.name + if target.is_symlink() or target.exists(): + target.unlink() + target.symlink_to(entry.resolve()) + (pack_dir / "Index").write_bytes(register((source / "Index").read_bytes())) + result = subprocess.run([str(pack_tool), str(pack_dir / "locale_zh"), str(locale_dir), "locale/zh/"], + check=True, capture_output=True, text=True) + return result.stdout.strip() def build(en: Path, zh: Path, output: Path, client_base: Path, - proto_tool: Path = None, names_dir: Path = None): + proto_tool: Path = None, names_dir: Path = None, pack_tool: Path = None): + """names_dir holds the server name snapshot and the reviewed *_zh_overlay.txt translations.""" if not (en / "locale_game.txt").is_file() or not (zh / "locale_game.txt").is_file(): raise ValueError("English 40250 and Chinese Longju locale directories are required") + if pack_tool is None or not pack_tool.is_file(): + raise ValueError("build eterpack_pack (cmake --build --target eterpack_pack)") - locale_dir = output / "locale/zh" - shutil.copytree(en, locale_dir, dirs_exist_ok=True) + locale_dir = output.parent / "locale-src/zh" + if locale_dir.exists(): + shutil.rmtree(locale_dir) + shutil.copytree(en, locale_dir) + loose = output / "locale/zh" + if loose.exists(): + shutil.rmtree(loose) # earlier builds ran the locale from loose files + if not any((output / "locale").iterdir()): + (output / "locale").rmdir() report = {"english_source": str(en), "chinese_source": str(zh), "files": {}} for filename in KEYED_FILES: english_lines, english = keyed_lines(en / filename, "cp1252") _, chinese = keyed_lines(zh / filename, "gbk") + # Reviewed translations of the keys Longju lacks or formats differently; they win over Longju. + overlay = names_dir / (Path(filename).stem + "_zh_overlay.txt") if names_dir else None + if overlay and overlay.is_file(): + _, reviewed = keyed_lines(overlay, "utf-8") + chinese.update(reviewed) translated = [] incompatible = [] merged = [] @@ -100,8 +171,10 @@ def build(en: Path, zh: Path, output: Path, client_base: Path, raise ValueError("build locale_proto_patch and provide the Chinese name tables") item_names = output.parent / "item_names_gbk.txt" mob_names = output.parent / "mob_names_gbk.txt" - item_conversion = convert_names(names_dir / "item_names_zh.txt", item_names) - mob_conversion = convert_names(names_dir / "mob_names_zh.txt", mob_names) + item_conversion = convert_names(names_dir / "item_names_zh.txt", item_names, + names_dir / "item_names_zh_overlay.txt") + mob_conversion = convert_names(names_dir / "mob_names_zh.txt", mob_names, + names_dir / "mob_names_zh_overlay.txt") result = subprocess.run( [str(proto_tool), str(en / "item_proto"), str(en / "mob_proto"), str(item_names), str(mob_names), str(locale_dir / "item_proto"), @@ -114,10 +187,12 @@ def build(en: Path, zh: Path, output: Path, client_base: Path, "patch_result": result.stdout.strip().splitlines(), } - (output / "locale.cfg").write_text("10002 936 zh\n", encoding="ascii") - # A local test checkout can share the original 40250 packs without copying - # gigabytes of assets. Distribution builds must package these separately. - for name in ("pack", "sound", "BGM", "miles", "lib", "channel.inf"): + report["pack"] = make_pack_dir(client_base, output / "pack", locale_dir, pack_tool) + (output / "locale.cfg").write_text(LOCALE_CFG, encoding="ascii") + (output / "locale_zh.cfg").write_text(LOCALE_CFG, encoding="ascii") + # A local test checkout shares the original 40250 assets through symbolic links + # (pack/ holds one per original pack); push-android-client.sh copies their targets. + for name in ("sound", "BGM", "miles", "lib", "channel.inf"): source = client_base / name target = output / name if not source.exists(): @@ -141,12 +216,13 @@ def main(): parser.add_argument("--chinese", type=Path, default=DEFAULT_ZH) parser.add_argument("--output", type=Path, default=DEFAULT_OUTPUT) parser.add_argument("--client-base", type=Path, default=REPO / "Client") - parser.add_argument("--proto-tool", type=Path, default=DEFAULT_PROTO_TOOL) + parser.add_argument("--proto-tool", type=Path, default=tool("locale_proto_patch")) + parser.add_argument("--pack-tool", type=Path, default=tool("eterpack_pack")) parser.add_argument("--name-tables", type=Path, default=DEFAULT_NAMES) args = parser.parse_args() report_path = build(args.english, args.chinese, args.output, args.client_base, - args.proto_tool, args.name_tables) - print(f"Built {args.output / 'locale/zh'}") + args.proto_tool, args.name_tables, args.pack_tool) + print(f"Built {args.output / 'pack/locale_zh.eix'} (.epk), registered in pack/Index") print(f"Report: {report_path}") diff --git a/script/test_build_zh_locale.py b/script/test_build_zh_locale.py index b9e88aa9..b187643f 100644 --- a/script/test_build_zh_locale.py +++ b/script/test_build_zh_locale.py @@ -27,18 +27,55 @@ class LocaleBuildTest(unittest.TestCase): (en / "item_proto").write_bytes(b"40250-proto") (zh / "item_proto").write_bytes(b"old-proto") (client / "pack").mkdir() + (client / "pack/Index").write_bytes(b"PACK\r\nlocale/de/\r\nlocale_de\r\nlocale/en/\r\nlocale_en\r\n") + (client / "pack/locale_en.eix").write_bytes(b"en") + # Stands in for eterpack_pack: records its arguments as the .eix. + pack_tool = root / "eterpack_pack" + pack_tool.write_text("#!/bin/sh\necho \"$2 $3\" > \"$1.eix\"\n") + pack_tool.chmod(0o755) - report_path = MODULE.build(en, zh, output, client) - merged = (output / "locale/zh/locale_game.txt").read_bytes().decode("gbk") + report_path = MODULE.build(en, zh, output, client, pack_tool=pack_tool) + staged = output.parent / "locale-src/zh" + merged = (staged / "locale_game.txt").read_bytes().decode("gbk") self.assertIn("GOOD\t中文 %d\tSA", merged) self.assertIn("BAD\t%s at %d\tSA", merged) self.assertIn("ONLY_EN\tFallback", merged) - self.assertEqual((output / "locale/zh/item_proto").read_bytes(), b"40250-proto") + self.assertEqual((staged / "item_proto").read_bytes(), b"40250-proto") self.assertEqual((output / "locale.cfg").read_text(), "10002 936 zh\n") - self.assertTrue((output / "pack").is_symlink()) + self.assertEqual((output / "locale_zh.cfg").read_text(), "10002 936 zh\n") + # A pack/ of its own: original packs linked, locale_zh packed from the staging dir and registered. + self.assertFalse((output / "pack").is_symlink()) + self.assertFalse((output / "locale/zh").exists()) + self.assertTrue((output / "pack/locale_en.eix").is_symlink()) + self.assertEqual((output / "pack/locale_zh.eix").read_text(), f"{staged} locale/zh/\n") + self.assertEqual((output / "pack/Index").read_bytes(), + b"PACK\r\nlocale/de/\r\nlocale_de\r\nlocale/zh/\r\nlocale_zh\r\n" + b"locale/en/\r\nlocale_en\r\n") + self.assertEqual((client / "pack/Index").read_bytes()[:4], b"PACK") + self.assertFalse((client / "pack/locale_zh.eix").exists()) report = json.loads(report_path.read_text()) self.assertEqual(report["files"]["locale_game.txt"]["incompatible_format_keys"], ["BAD"]) + # Reviewed overlay: fills an English-only key and replaces a format-incompatible Longju value. + names = root / "names" + names.mkdir() + (names / "locale_game_zh_overlay.txt").write_text("KEY\tVALUE\nBAD\t%s 于 %d\nONLY_EN\t备用\n") + report_path = MODULE.build(en, zh, output, client, names_dir=names, pack_tool=pack_tool) + merged = (staged / "locale_game.txt").read_bytes().decode("gbk") + self.assertIn("BAD\t%s 于 %d\tSA", merged) + self.assertIn("ONLY_EN\t备用", merged) + game = json.loads(report_path.read_text())["files"]["locale_game.txt"] + self.assertEqual((game["english_fallback_keys"], game["incompatible_format_keys"]), ([], [])) + + def test_name_overlay_replaces_snapshot_rows(self): + with tempfile.TemporaryDirectory() as directory: + root = Path(directory) + (root / "names.txt").write_text("VNUM\tLOCALE_NAME\n1\tYang\n2\t剑\n") + (root / "overlay.txt").write_text("VNUM\tLOCALE_NAME\n1\t金币\n99\t不存在\n") + result = MODULE.convert_names(root / "names.txt", root / "out.txt", root / "overlay.txt") + self.assertEqual((root / "out.txt").read_bytes().decode("gbk"), "1\t金币\n2\t剑\n") + self.assertEqual(result["overlaid"], 1) + if __name__ == "__main__": unittest.main()