功能
- 给定其实ID和类名后缀;
- 找出字面量字符串、模板字符串(替换为{x});
- 生成.ts枚举ID类、可直接复制到表的文本。
流程
1.动态部分要改:ID起点、类名
2.脚本执行后——>复制.ts到游戏 —— 复制.txt到表
3.修改代码
4.提交:表、代码
源码(nodejs环境运行)
const fs = require('fs');
const path = require('path');
const { parse } = require('@typescript-eslint/typescript-estree');
const readline = require('readline');
const POSTFIX_TS = ".ts"
const START_ID = 2000;
const FILENAME_POSTFIX = "Equip";
const scriptDir = __dirname;
const CharConstClsName = "CharConst";
const IO_ENCODING = "utf8";
const COMBINE_NAME = `${ CharConstClsName }${ FILENAME_POSTFIX }`;
const CharConstTSFile = `${COMBINE_NAME}${POSTFIX_TS}`;
const input_dir = `${scriptDir}\\input\\`;
const output_dir = `${scriptDir}\\output\\`;
const output = `${COMBINE_NAME}.txt`;
const output_simple = `${COMBINE_NAME}_Simple.txt`;
const SHOW_FILE_LINE = true;
const IS_GEN_SIMPLE_STR = true;
let strings = new Set();
let strings_simple = new Set();
function traverse(filePath) {
const content = fs.readFileSync(filePath, IO_ENCODING);
try {
const ast = parse(content, {
ecmaVersion: 2020,
sourceType: 'module',
loc: true,
range: true
});
traverseAST(ast, filePath);
} catch (e) {
console.warn(`Parse error in ${filePath}`);
}
}
let tempDiffIdx = 1;
const __Mark = "___";
function traverseAST(node, file) {
if (node.type === 'Literal' &&
typeof node.value === 'string' &&
/[\u4e00-\u9fa5]/.test(node.value)) {
handleStringLiteral(node, file);
}
if (node.type === 'TemplateLiteral') {
let processedStr = '';
let argIndex = 0;
for (let i = 0; i < node.quasis.length; i++) {
const quasi = node.quasis[i];
processedStr += quasi.value.raw;
if (i < node.quasis.length - 1) {
processedStr += `{${argIndex++}}`;
}
}
if (/[\u4e00-\u9fa5]/.test(processedStr)) {
handleStringLiteral({
value: processedStr,
loc: node.loc
}, file);
}
}
for (let key in node) {
if (node.hasOwnProperty(key) &&
typeof node[key] === 'object' &&
node[key] !== null) {
traverseAST(node[key], file);
}
}
}
function handleStringLiteral(node, file) {
if (SHOW_FILE_LINE) {
const line = node.loc ? node.loc.start.line : '未知行';
strings.add(`${file}:${line}\n${node.value}`);
}
if (IS_GEN_SIMPLE_STR) {
strings_simple.add(`${node.value}${__Mark}${tempDiffIdx++}`);
}
}
function walkDir(currentPath) {
if (!fs.existsSync(currentPath)) {
console.error(`路径不存在: ${currentPath}`);
return;
}
const files = fs.readdirSync(currentPath);
for (const file of files) {
const fullPath = path.join(currentPath, file);
const stat = fs.statSync(fullPath);
if (stat.isDirectory()) {
walkDir(fullPath);
} else if (file.endsWith(POSTFIX_TS)) {
traverse(fullPath);
} else {
}
}
}
function generateEnumFile() {
const entries = Array.from(strings).map(entry => {
const [fileLine, value] = entry.split('\n');
const lastColonIndex = fileLine.lastIndexOf(':');
return {
file: fileLine.slice(0, lastColonIndex),
line: fileLine.slice(lastColonIndex + 1),
value: value
};
});
const enumLines = entries.map(({ file, line, value }, index) => {
const id = index + START_ID;
const key = `ID_${id}`;
const fileName = path.basename(file);
return `\t// ${fileName}:${line}\n\t${key} = ${id}, //${JSON.stringify(value)}`;
});
const enumContent = `/** Attention! The annotations will not change along with the actual table. */\nexport enum ${COMBINE_NAME} {\n${enumLines.join('\n')}\n}`;
fs.writeFileSync(output_dir+CharConstTSFile, enumContent, IO_ENCODING);
}
const enableDelOutput = true;
const delOutputTipsShow = true;
async function clearOutputDir() {
const dirPath = output_dir;
if (!enableDelOutput) return;
console.log(`enableDelOutput:${output_dir}`)
if (!fs.existsSync(dirPath)) {
console.log(`目录不存在: ${dirPath}`);
return;
}
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
try {
let rst = "y";
if (delOutputTipsShow){
rst = await new Promise(resolve => {
rl.question(`即将清空目录 ${dirPath},确认继续吗?(y/n) `, resolve);
});
}
if (rst.trim().toLowerCase() === 'y') {
fs.rmSync(dirPath, { recursive: true, force: true });
fs.mkdirSync(dirPath);
console.log('√ 目录已清空');
} else {
console.log('× 操作已取消');
}
} finally {
rl.close();
}
}
async function main() {
try {
await clearOutputDir();
walkDir(input_dir);
fs.writeFileSync(
path.join(output_dir, output),
Array.from(strings).join('\n'),
IO_ENCODING
);
fs.writeFileSync(path.join(output_dir, output_simple), Array.from(strings_simple).map(str=>str.split(__Mark)[0]).join('\n'), IO_ENCODING);
generateEnumFile();
console.log(`
√ 提取完成:
- 找到 ${strings.size} 条中文字符串(已写入 ${output} 和 ${output_simple})
- 生成枚举文件 ${CharConstTSFile}
`);
} catch (err) {
console.error('× 运行出错:', err);
process.exit(1);
}
}
main();