feat(软著): 新增软著登记全套文档与资源整理

1. 新增软著登记所需的源程序生成脚本、docx生成脚本与依赖配置
2. 新增像素大勇者游戏软件V1.0设计说明书与填表指引文档
3. 更新技能配置相关的prefab与meta文件,清理冗余的up状态预制件
4. 修复部分技能buff的颜色与精灵帧引用,更新动画资源引用
5. 新增两个技能图标资源文件
This commit is contained in:
panFD
2026-09-14 20:46:31 +08:00
parent f2a4926c49
commit 2dc44b2393
35 changed files with 15108 additions and 8189 deletions

233
docs/软著/gen_docx.mjs Normal file
View File

@@ -0,0 +1,233 @@
/**
* 软著材料 docx 生成脚本
* 1) 源程序:解析已生成的 txt,按显示宽度折行(每行 ≤84 列),每页 60 行,
* 页眉左侧"软件全称 版本号"、右侧 Word 自动页码域"第 X 页 共 Y 页"。
* 2) 设计说明书:解析 md,生成封面 + 章节正文 docx。
* 用法:node gen_docx.mjs
*/
import fs from 'fs';
import path from 'path';
import {
Document, Packer, Paragraph, TextRun, Header, AlignmentType,
PageNumber, TabStopType, HeadingLevel,
} from 'docx';
const DIR = import.meta.dirname;
const SRC_TXT = path.join(DIR, '像素大勇者游戏软件_V1.0_源程序.txt');
const MANUAL_MD = path.join(DIR, '像素大勇者游戏软件_V1.0_设计说明书.md');
const OUT_SRC = path.join(DIR, '像素大勇者游戏软件_V1.0_源程序.docx');
const OUT_MANUAL = path.join(DIR, '像素大勇者游戏软件_V1.0_设计说明书.docx');
const SOFTWARE = '像素大勇者游戏软件 V1.0';
const LINES_PER_PAGE = 60;
const FOLD_WIDTH = 84; // A4 折行限制:显示列宽(ASCII=1 列,中文=2 列)
// A4 纵向,边距:上下 1.27cm,左右 1.5cm
const PAGE = {
size: { width: 11906, height: 16838 },
margin: { top: 720, bottom: 720, left: 850, right: 850, header: 400 },
};
const TEXT_WIDTH = PAGE.size.width - PAGE.margin.left - PAGE.margin.right;
/** 统一页眉:左侧软件名+版本号,右侧自动页码域 */
function makeHeader() {
return new Header({
children: [
new Paragraph({
tabStops: [{ type: TabStopType.RIGHT, position: TEXT_WIDTH }],
children: [
new TextRun({ text: SOFTWARE, size: 18, font: { ascii: 'Consolas', eastAsia: 'SimSun' } }),
new TextRun({
children: ['\t第 ', PageNumber.CURRENT, ' 页 共 ', PageNumber.TOTAL_PAGES, ' 页'],
size: 18, font: { ascii: 'Consolas', eastAsia: 'SimSun' },
}),
],
}),
],
});
}
// ============================================================
// 一、源程序 docx
// ============================================================
/** 字符显示宽度(ASCII 记 1 列,全角/中文记 2 列) */
const charWidth = (ch) => (ch.charCodeAt(0) > 0xff ? 2 : 1);
/** 超宽行按显示列宽折行,续行缩进 4 空格 */
function foldLine(line, width = FOLD_WIDTH) {
if ([...line].reduce((w, c) => w + charWidth(c), 0) <= width) return [line];
const out = [];
let cur = '', curW = 0;
for (const ch of line) {
const cw = charWidth(ch);
if (curW + cw > width) {
out.push(cur);
cur = ' ' + ch; // 续行缩进
curW = 4 + cw;
} else {
cur += ch;
curW += cw;
}
}
if (cur) out.push(cur);
return out;
}
/** 解析 txt:剔除页眉/分隔线/页间空行,还原连续代码行 */
function parseSourceTxt() {
const raw = fs.readFileSync(SRC_TXT, 'utf8').split('\n');
const codeLines = [];
let count = 0;
for (const line of raw) {
if (line.startsWith('像素大勇者游戏软件')) continue; // 页眉行
if (/^=+$/.test(line.trim()) && line.trim() !== '') continue; // 分隔线
if (count > 0 && count % LINES_PER_PAGE === 0 && line.trim() === '') continue; // 页间空行
codeLines.push(line);
count++;
}
return codeLines;
}
async function buildSourceDocx() {
const codeLines = parseSourceTxt();
// 折行后按每页 50 个显示行分页
const folded = codeLines.flatMap((l) => foldLine(l.replace(/\t/g, ' ')));
const totalPages = Math.ceil(folded.length / LINES_PER_PAGE);
const foldedPages = Math.ceil(codeLines.length / LINES_PER_PAGE); // 折行前理论页数(对照 txt)
const children = folded.map((line, i) =>
new Paragraph({
pageBreakBefore: i > 0 && i % LINES_PER_PAGE === 0,
spacing: { after: 0, line: 230, lineRule: 'auto' },
children: [
new TextRun({
text: line,
size: 19, // 9.5pt
font: { ascii: 'Consolas', eastAsia: 'SimSun' },
}),
],
})
);
const doc = new Document({
sections: [{
properties: { page: PAGE },
headers: { default: makeHeader() },
children,
}],
});
await Packer.toBuffer(doc).then((buf) => fs.writeFileSync(OUT_SRC, buf));
console.log('=== 源程序 docx 生成完成 ===');
console.log(`代码行: ${codeLines.length},折行后显示行: ${folded.length}`);
console.log(`折行前页数(对照txt): ${foldedPages},docx 实际页数: ${totalPages}`);
console.log(`输出: ${OUT_SRC}`);
}
// ============================================================
// 二、设计说明书 docx
// ============================================================
const BODY_FONT = { ascii: 'Times New Roman', eastAsia: 'SimSun' };
const HEAD_FONT = { ascii: 'Arial', eastAsia: 'SimHei' };
/** 解析 **粗体** 行内标记 */
function parseRuns(text, base = {}) {
return text
.split(/(\*\*[^*]+\*\*)/g)
.filter(Boolean)
.map((p) =>
p.startsWith('**')
? new TextRun({ text: p.slice(2, -2), bold: true, font: BODY_FONT, ...base })
: new TextRun({ text: p, font: BODY_FONT, ...base })
);
}
async function buildManualDocx() {
const md = fs.readFileSync(MANUAL_MD, 'utf8').split('\n');
const children = [];
let coverDone = false;
for (const rawLine of md) {
const line = rawLine.trimEnd();
if (!coverDone) {
// 封面区:软件名 / 文档名 / 著作权人 / 日期,居中排布,遇 --- 结束封面
if (line.startsWith('# ')) {
children.push(new Paragraph({
alignment: AlignmentType.CENTER,
spacing: { before: 240, after: 240 },
children: [new TextRun({ text: line.slice(2), bold: true, size: 52, font: HEAD_FONT })],
}));
} else if (/^(著作权人|开发完成日期):/.test(line)) {
children.push(new Paragraph({
alignment: AlignmentType.CENTER,
spacing: { before: 120, after: 120 },
children: [new TextRun({ text: line, size: 24, font: BODY_FONT })],
}));
} else if (line.trim() === '---') {
coverDone = true;
// 封面独立成页
children.push(new Paragraph({ children: [new TextRun({ text: '', size: 2 })], pageBreakBefore: false }));
children.push(new Paragraph({ pageBreakBefore: true, children: [] }));
}
continue;
}
// 正文区
if (line.trim() === '' || line.trim() === '---') continue;
if (line.startsWith('## ')) {
children.push(new Paragraph({
heading: HeadingLevel.HEADING_1,
spacing: { before: 240, after: 120 },
children: [new TextRun({ text: line.slice(3), bold: true, size: 30, font: HEAD_FONT, color: '000000' })],
}));
} else if (line.startsWith('### ')) {
children.push(new Paragraph({
heading: HeadingLevel.HEADING_2,
spacing: { before: 180, after: 100 },
children: [new TextRun({ text: line.slice(4), bold: true, size: 26, font: HEAD_FONT, color: '000000' })],
}));
} else if (line.startsWith('【建议插入截图')) {
children.push(new Paragraph({
spacing: { before: 60, after: 60 },
indent: { left: 420 },
children: [new TextRun({ text: line, italics: true, color: '808080', size: 21, font: BODY_FONT })],
}));
} else if (/^\d+\.\s/.test(line)) {
children.push(new Paragraph({
spacing: { after: 100 },
indent: { left: 420 },
children: parseRuns(line, { size: 21 }),
}));
} else {
children.push(new Paragraph({
spacing: { after: 100, line: 300 },
children: parseRuns(line, { size: 21 }),
}));
}
}
const doc = new Document({
styles: {
default: {
document: { run: { font: BODY_FONT, size: 21 } },
},
},
sections: [{
properties: { page: PAGE },
headers: { default: makeHeader() },
children,
}],
});
await Packer.toBuffer(doc).then((buf) => fs.writeFileSync(OUT_MANUAL, buf));
console.log('=== 设计说明书 docx 生成完成 ===');
console.log(`输出: ${OUT_MANUAL}`);
}
await buildSourceDocx();
await buildManualDocx();