From b6f4435b848d41300c0d91d013e0fa036f6d4716 Mon Sep 17 00:00:00 2001 From: unanmed <1319491857@qq.com> Date: Tue, 13 Oct 2026 17:02:51 +0800 Subject: [PATCH] docs(09): data-migration audit (task 8) + 09-18 per-directive revision --- .../09-17-SUMMARY.md | 164 +++ .../09-minimum-browser-runnable/09-18-PLAN.md | 350 +++++++ .../09-18-SUMMARY.md | 149 +++ .../09-DATA-MIGRATION-AUDIT.md | 984 ++++++++++++++++++ 4 files changed, 1647 insertions(+) create mode 100644 .planning/phases/09-minimum-browser-runnable/09-17-SUMMARY.md create mode 100644 .planning/phases/09-minimum-browser-runnable/09-18-PLAN.md create mode 100644 .planning/phases/09-minimum-browser-runnable/09-18-SUMMARY.md create mode 100644 .planning/phases/09-minimum-browser-runnable/09-DATA-MIGRATION-AUDIT.md diff --git a/.planning/phases/09-minimum-browser-runnable/09-17-SUMMARY.md b/.planning/phases/09-minimum-browser-runnable/09-17-SUMMARY.md new file mode 100644 index 0000000..584b5f1 --- /dev/null +++ b/.planning/phases/09-minimum-browser-runnable/09-17-SUMMARY.md @@ -0,0 +1,164 @@ +--- +phase: 09-minimum-browser-runnable +plan: 17 +subsystem: data-migration +tags: [legacy-migration, read-only-audit, tile, textures, enemy, item, npc, map, texture-list-schema] + +# Dependency graph +requires: + - phase: 09-minimum-browser-runnable + provides: "旧引擎数据(public/project)与新引擎落点(src/content)的只读映射台账" +provides: + - "09-DATA-MIGRATION-AUDIT.md:任务 8 只读盘点台账(§0..§9,235 tile / 17 texture / 42 split / 81 enemy / 62 item / 24 npc / 5 map)" +affects: [data-migration-plan, texture-list-schema] + +# Actuals (#2632) — no commit (per user instruction to leave output in working tree) +actuals: + tokens: 13680 + tasks: 2 + commits: 0 + +tech-stack: + added: [] + patterns: ["只读台账 + 执行前基线对比只读守卫 + 命名候选集中报备"] + +key-files: + created: + - ".planning/phases/09-minimum-browser-runnable/09-DATA-MIGRATION-AUDIT.md" + modified: [] + +key-decisions: + - "全程只读:唯一写入为盘点文档 + SUMMARY(+ 仓库外只读基线),零 .ts/.tsx、零 public/project/**、零 src/content/** 改动,零 PNG 生成" + - "命名候选(拆分文件名 / identifier / 参数名 / .list.jsonc 字段名)只入台账并标注「待用户报备批准」,不落地" + - "textures 维度显式含 materials/*.png + autotiles/*.png + tilesets/*.png;autotiles/tilesets 不套用 D-72⑤" + - "D-72⑤ 读法(单帧 4×4、多帧 帧数列×4 行)与 SPLIT-COUNT=42 记入 §8 U-1 待用户确认" + - "无法静态确定者一律入 §8「未确定项 / 待确认观察」,未登记为缺陷" + +patterns-established: + - "只读盘点以执行前基线(仓库外)对比作只读守卫,可检出执行期越界写入" + +requirements-completed: [] + +coverage: + - id: D1 + description: "只读盘点台账 09-DATA-MIGRATION-AUDIT.md(§0 口径 + §1 tile + §2 textures + §3 enemy + §4 items + §5 NPC + §6 maps + §7 schema 草案 + §8 未确定项 + §9 汇总)" + verification: + - kind: other + ref: "node -e 静态门禁(AUDIT-TILE-TEX / AUDIT-MIG-FINAL:章节 / 计数 / token / 锚点有效性)+ READONLY 守卫" + status: pass + human_judgment: true + rationale: "盘点的映射候选、拆分方案读法与命名候选均需用户逐项报备 / 裁定(D-73);自动化只能校验结构、计数与锚点有效性,不能替代人工确认语义" + - id: D2 + description: "只读保证:零源码 / 零数据 / 零 PNG 改动(执行前基线对比 READONLY 门禁全绿)" + verification: + - kind: other + ref: "node READONLY gate(Task 1 / Task 2)+ git status --porcelain" + status: pass + human_judgment: false + +duration: 1h +completed: 2026-10-13 +status: complete +--- + +# Phase 9 Plan 17: 旧引擎数据迁移只读盘点 摘要 + +**旧引擎 `public/project` 数据到新引擎接口(`ITileRawData` / `IEnemyRawData` / `IItemRawData` / `IMapRawData` / `TextureManager`)的只读映射台账:235 tile / 17 texture / 42 split / 81 enemy / 62 item / 24 npc / 5 map** + +## Performance + +- **Duration:** ~1h +- **Completed:** 2026-10-13 +- **Tasks:** 2(Task 0 为已闭环的 `blocking-human` 汇报关卡,见下) +- **Files modified:** 1(新增盘点文档;另新增 1 份 SUMMARY) + +## Task 0 说明(执行前汇报关卡) + +本计划第一个 block 点是 Task 0 `checkpoint:decision@blocking-human`(AGENTS.md 硬要求 / D-03 / D-74)。**该关卡在本次执行前已闭环**:用户已就预执行汇报明确回复「可以执行」四个字(含盘点范围六类、textures 维度含 materials + autotiles + tilesets、七个维度、D-72 五条拆分规则与 tilesets/autotiles 例外、命名候选报备方式、产出路径、只读保证与开放项 OI-1..OI-6)。据此执行 Task 1 / Task 2;未再重复询问。 + +## Accomplishments + +- 产出只读盘点台账 `09-DATA-MIGRATION-AUDIT.md`(54718 字节 / 817 行 / 裸 LF),含 §0..§9 完整章节。 +- §1 登记 `blocksInfo` 全表 **235** 图块(8 个 `cls`)+ `cls → TileType` 与字段映射候选 + id/num 全清单。 +- §2 登记 **17** 个 PNG(materials 12 + autotiles 4 + tilesets 1)与实际像素尺寸,按 D-72 五规则给出 materials 拆分方案(目标图 **42** 张)+ autotiles/tilesets 例外参数候选。 +- §3/§4/§5/§6 分别给出 enemy(81) / items(62) / NPC(24) / maps(5) 映射候选。 +- §7 给出 `texture/.list.jsonc` 字段结构草案(与 §2 逐条一致);§8 集中登记 18 项未确定项(未登记为缺陷);§9 汇总七维度计数。 +- 全程只读:只读基线对比守卫(Task 1 / Task 2)与 `git status --porcelain` 全绿。 + +## Files Created/Modified + +- `.planning/phases/09-minimum-browser-runnable/09-DATA-MIGRATION-AUDIT.md`(新增,只读盘点台账 §0..§9) +- `.planning/phases/09-minimum-browser-runnable/09-17-SUMMARY.md`(新增,本摘要) + +## 各维度计数 + +| 维度 | 计数 | 机器可读行 | +| --- | --- | --- | +| 图块 tile | 235 | `TILE-COUNT: 235` | +| 贴图素材 textures | 17 | `MATERIAL-COUNT: 17` | +| 素材拆分目标图 split | 42 | `SPLIT-COUNT: 42` | +| 怪物 enemy | 81 | `ENEMY-COUNT: 81` | +| 道具 items | 62 | `ITEM-COUNT: 62` | +| NPC | 24 | `NPC-COUNT: 24` | +| 地图 maps | 5 | `MAP-COUNT: 5` | + +## 命名候选清单(待用户报备批准) + +均**只作候选入台账,本计划不落地使用**(D-73 / AGENTS.md): + +| 类别 | 候选 | +| --- | --- | +| materials 拆分图(terrains 单帧) | `terrains0.png` / `terrains1.png` / `terrains2.png` | +| materials 拆分图(animates 并入 terrains) | `terrainsAnim0.png` … `terrainsAnim7.png` | +| materials 拆分图(items) | `items0.png` … `items3.png` | +| materials 拆分图(enemy) | `enemy0.png` … `enemy20.png` | +| materials 拆分图(npc) | `npc0.png` … `npc5.png` | +| `addAutotile` 的 `identifier` 参数 | `20` / `151` / `152` / `153` | +| `addAutotile` 的 `type` 参数 | `AutoTileType.Big3x4` | +| `addTileset` 的 `identifier.alias` | `magictower` | +| `addTileset` 的 `cellWidth` / `cellHeight` | `32` / `32`(待确认) | +| `.list.jsonc` 承载字段名 | `grid` / `rowAnimate` / `autotile` / `tileset` | + +## OI-1..OI-6 最终裁定(本计划处置) + +| 开放项 | 处置 | +| --- | --- | +| OI-1(产出文档命名 / 路径) | 采用 `.planning/phases/09-minimum-browser-runnable/09-DATA-MIGRATION-AUDIT.md`(Task 0 已获批) | +| OI-2(文档章节结构) | 采用 §0..§9 五节序列(如实现) | +| OI-3(命名候选呈现方式) | 采用「命名候选(待报备)」集中表,逐条标注「待用户报备批准」(§2.5 / §4 / §7 / §9) | +| OI-4(拆分方案粒度,含例外) | 按 D-72 五规则逐组列出目标图 + 来源 + 帧数 + 行列布局;tilesets/autotiles 各给参数候选(§2.2–§2.4) | +| OI-5(schema 草案深度) | 仅给 `texture/.list.jsonc` 字段结构草案与到四个方法的对应,不含最终命名落地(§7) | +| OI-6(未确定项与只读守卫口径) | 无法确定者一律入 §8(未登记为缺陷);只读守卫采用「执行前基线对比」,基线由 Task 1 步骤 (0) 一次性写入仓库外,`` 幂等不覆写 | + +## Deviations from Plan + +None - plan executed exactly as written(Task 0 关卡已预先闭环;无源码 / 数据 / PNG 改动;无命名落地)。 + +## Issues Encountered + +无。所有静态门禁(Task 1:基线幂等 + 结构 / 计数 / token / 锚点 + READONLY;Task 2:结构 / 七计数 / token / 锚点 + READONLY + `git status`)全绿。 + +## 只读确认 + +- 未修改 / 删除 / 新增任何生产 `.ts` / `.tsx` 源码。 +- 未修改 / 删除 / 新增任何 `public/project/**` 与 `src/content/**` 数据文件。 +- 未生成任何 `.png`;未改动任何既有注释;未改 `ROADMAP.md` / `STATE.md` / `09-CONTEXT.md`。 +- 唯一新增文件为盘点文档与本 SUMMARY(另在仓库外临时目录写入只读基线 `%TEMP%/opencode/gsd-09-17-baseline.txt`)。 +- 未执行 `git add` / `git commit`(按执行指示将产出留在工作树,供用户审阅后自行决定提交);未触碰用户并发改动(`AGENTS.md`)。 + +## Next Phase Readiness + +- 盘点台账已可直接供后续「旧引擎数据迁移」计划消费(字段映射候选 + 拆分方案 + 命名候选)。 +- §8 的 18 项未确定项与 §2.5 命名候选待用户裁定 / 报备后,方可进入迁移实施计划。 + +## Self-Check + +- FOUND: `.planning/phases/09-minimum-browser-runnable/09-DATA-MIGRATION-AUDIT.md` +- FOUND: `.planning/phases/09-minimum-browser-runnable/09-17-SUMMARY.md` +- COMMITS: 0(按执行指示未提交,产出留在工作树) + +## Self-Check: PASSED + +--- +*Phase: 09-minimum-browser-runnable* +*Completed: 2026-10-13* diff --git a/.planning/phases/09-minimum-browser-runnable/09-18-PLAN.md b/.planning/phases/09-minimum-browser-runnable/09-18-PLAN.md new file mode 100644 index 0000000..38ccc0b --- /dev/null +++ b/.planning/phases/09-minimum-browser-runnable/09-18-PLAN.md @@ -0,0 +1,350 @@ +--- +phase: 09-minimum-browser-runnable +plan: 18 +type: execute +wave: 15 +depends_on: [09-17] +files_modified: + - .planning/phases/09-minimum-browser-runnable/09-DATA-MIGRATION-AUDIT.md + - .planning/phases/09-minimum-browser-runnable/09-18-SUMMARY.md +autonomous: false +requirements: [] +estimate: + tokens: 60000 + raw_tokens: 60000 + tasks: 3 + confidence: low +must_haves: + truths: + - "**本计划的第一个 block 点是 Task 0 的 `blocking-human` 汇报关卡(AGENTS.md 硬要求,D-03 / D-74)**:Task 0 必须先汇报「本计划 = 09-18 · 按用户 2026-10-13 四条指示**修订**既有的 `09-DATA-MIGRATION-AUDIT.md`(只读盘点台账,09-17 产出)」的要做什么 / 为什么 / 大致怎么做 / 影响范围,**必须列明:四条指示的逐条落实方式、只读保证、修订方式(派发子代理执行)、产出文件(`09-DATA-MIGRATION-AUDIT.md` 原地修订 + 新增 `09-18-SUMMARY.md`)、静态门禁方式与计数一致性要求**,然后必须**停下等待**用户明确回复「可以执行」四个字;未收到前不得执行本计划任何一步、不得派发执行子代理,且不因触发方式或 `mode: yolo` / `workflow.auto_advance` 自动放行。" + - "**指令 1(`animates` 合并至 `terrains`,确认 D-72③):** 在 §0(D-72 规则原文处)**及** §2(§2.1 登记 / §2.4 拆分方案 / §2.5 命名候选)**及** §5 相关处,以及一切出现 `animates` / `terrains` 归属的位置(§1.1 / §1.4 / §9),以一致表述强化「`animates` 并入 `terrains`,不设独立 `animates` 素材组」,消除「`animates` 是否仍为独立素材组」的歧义;同时明确区分「逻辑类型映射(§1.1 `animates` → `TileType.Animate`,保留)」与「素材 / 分图分组(`animates` 素材并入 `terrains` 组,D-72③)」。" + - "**指令 2(严格界定「只记录、不移植」):** 「只记录、不移植」**仅**当新接口(`ITileRawData` / `ITilePassData` / `IEnemyRawData` / `IItemRawData` / `IMapRawData`)**不存在对应字段**时适用;据此在 §1 / §3 / §4 / §6 的映射候选表**逐字段显式标注**三类(映射候选可确定 / 有目标字段但映射不确定 → 待定 / 无目标字段 → 只记录、不移植),并在 §8 **重构**为 §8.1「无目标字段 → 只记录、不移植」与 §8.2「有目标字段但映射不确定 → 待定」两分区(保留非字段级观察为 §8.3);**不得**把有明确目标字段的项(如旧 tile `trigger` → `ITileRawData.events` / `eventPass`、旧 map `events` → `IMapRawData.events`)误标为「只记录、不移植」。" + - "**指令 3(图块 `num` 可全部重新排序):** §1 须明确「图块 `num` 可全部重新分配,不沿用旧引擎顺序,唯一约束是 `id` ↔ `num` 一致」;给出 **235 条 `id` 全清单**与 **新 `num` 分配策略候选**(≥3 个策略,均标注「待用户报备批准」);旧 `num` 仅作参考对照。" + - "**指令 4(`texture/.list.jsonc` schema 须显式类型声明):** §7 草案必须给出**显式的 TypeScript 类型声明**(`interface` 与 `type` 别名形式,可读、可落地参考),至少含四个 entry 接口(grid / rowAnimate / autotile / tileset)、四者联合类型别名、顶层配置接口;而非仅文字描述;所有新增命名仍标注「待用户报备批准」。" + - "**只读边界(D-74 / D-75):** 本计划**唯一允许写入**的两份 `.md` 为 `.planning/phases/09-minimum-browser-runnable/09-DATA-MIGRATION-AUDIT.md`(原地修订)与 `.planning/phases/09-minimum-browser-runnable/09-18-SUMMARY.md`(新增);**零** `.ts` / `.tsx` 改动、**零** `public/project/**` 改动、**零** `src/content/**` 改动、**零** `.png` 生成;**不得**改 09-17 计划 / 摘要、`ROADMAP.md`、`STATE.md`、`09-CONTEXT.md`;命名仍只作「命名候选(待报备)」。" + - "**计数一致 + 保留既有正确内容:** 修订只做增改;既有正确内容保留;机器可读计数行保持一致(`TILE-COUNT: 235` / `MATERIAL-COUNT: 17` / `SPLIT-COUNT: 42` / `ENEMY-COUNT: 81` / `ITEM-COUNT: 62` / `NPC-COUNT: 24` / `MAP-COUNT: 5`);若因重构导致行数变化,计数行与各节须同步一致。" + - "**锚点真实 + 观察纪律:** 台账 `file:line` 锚点必须指向真实存在且行号在范围内的行;无法从阅读 / 静态解析唯一确定者一律入 §8(不猜测编造);疑似问题**不得**登记为缺陷,只作待确认观察。" + artifacts: + - path: .planning/phases/09-minimum-browser-runnable/09-DATA-MIGRATION-AUDIT.md + provides: "按用户四条指示修订后的只读盘点台账:§0(`animates` 并入 `terrains` 强化表述)、§1(`num` 可全部重排说明 + `id` 全清单 + 新 `num` 分配策略候选 + 字段分类标注)、§2(`animates` 并入 `terrains` 强化)、§3 / §4 / §6(字段分类标注)、§5(交叉引用)、§7(显式 TypeScript 类型声明)、§8(重构为 8.1 只记录 / 8.2 待定 / 8.3 其他)、§9(计数与命名说明)。" + - path: .planning/phases/09-minimum-browser-runnable/09-18-SUMMARY.md + provides: "本修订计划的执行摘要:四条指示的落实位置、命名候选清单、各维度计数(不变)、只读确认、遗留待用户裁定项。" + key_links: + - "`09-DATA-MIGRATION-AUDIT.md` §1.2 / §3.1 / §4.2 / §6.2 的分类标注 ↔ `packages-user/data-common/src/store/types.ts`(`ITilePassData`:38 / `ITileRawData`:47 / `IItemEffect`:138 / `IItemEquipData`:161 / `IItemRawData`:176 / `IMapRawData`:260 / `IEnemyRawData`:319)—— 「是否有对应目标字段」的唯一判定基准" + - "`09-DATA-MIGRATION-AUDIT.md` §7 显式 TypeScript 类型声明 ↔ `packages-user/client-base/src/material/types.ts`(`AutotileType`:12 / `IIndexedIdentifier`:50 / `addGrid`:261 / `addRowAnimate`:275 / `addAutotile`:289 / `addTileset`:302)—— schema 类型声明的能力依据" + - "`09-DATA-MIGRATION-AUDIT.md` §2.4 / §2.5(`animates` 并入 `terrains`)↔ D-72③ —— 指令 1 的规则依据" + - "`src/content/client.jsonc`:9(`textureListDir: texture/.list.jsonc`)/ `src/content/texture/.list.jsonc`:1(现状 `{}`)—— §7 schema 草案落点侧参考(D-70 消费接线由用户后续提供)" + assumptions: + - "**执行日行号可能漂移**:本计划内引用行号为 2026-10-13 规划日实测;文档锚点与门禁以**执行日实测**为准,门禁按「文件存在 + 行号在范围内」校验,不硬编码任何行号。" + - "**被修订对象为工作树未跟踪产物**:`09-DATA-MIGRATION-AUDIT.md` 与 `09-17-SUMMARY.md` 由 09-17 产出、当前在工作树中未提交;本计划**原地修订**该文档,不新建。" + - "**用户并发改动(D-56)**:工作树已有用户并发改动(`AGENTS.md` 处于 `M`);本计划只读守卫采用「执行前基线对比」,仅对本计划执行期**新增**的 `.ts` / `.tsx` / `public/project/**` / `src/content/**` 改动判红,不触碰、不暂存、不提交用户既有改动。" + - "**用户指示中「§5」的定位**:本文档 §5 为 NPC 节,其正文与 `animates` / `terrains` 无关;故「§0 / §2 / §5 相关处」按「一切出现 `animates` / `terrains` 归属的位置优先,§5 以交叉引用方式落实」执行,避免在 NPC 节强塞无关内容。" + - "**Markdown 使用 LF**:仓库既有 phase 文档与盘点产物均为裸 LF;修订随此约定(**不**强制 CRLF)。" + prohibitions: + - "不得在用户明确回复「可以执行」四个字之前执行本计划的任何任务、或派发任何执行子代理。" + - "**只读边界:** 除 `09-DATA-MIGRATION-AUDIT.md`(原地修订)与 `09-18-SUMMARY.md`(新增)两份 `.md` 外,**不得写入任何文件**;**不得**修改 / 删除 / 新增任何生产 `.ts` / `.tsx`;**不得**改 `public/project/**` 与 `src/content/**`;**不得生成任何 `.png`**;**不得**改 09-17 计划 / 摘要、`ROADMAP.md`、`STATE.md`、`09-CONTEXT.md`。" + - "**命名边界:** 修订中提出的任何新增 / 修改命名(`num` 分配策略、`texture/.list.jsonc` 字段名与 TypeScript 类型名等)**只能作为「命名候选」写入台账并标注「待用户报备批准」**,**不得落地使用**、**不得写入任何 `src/content/**` 文件**(D-73 / AGENTS.md:公共 / 受保护 / 私有方法、字段命名须先报备获批)。" + - "**分类纪律:** 只有目标接口**确实无对应字段**的旧字段才可标注「只记录、不移植」;**不得**把有明确目标字段的项误标为只记录;判定须引用执行日实测的接口定义作为依据。" + - "**不得臆造条目:** 文档中每个 `file:line` 锚点必须指向真实存在且范围内的一行;**不得**编造字段名 / 映射关系 / 尺寸。" + - "**不得把观察当缺陷:** 发现的任何「疑似问题 / 不一致 / 缺失」**不得登记为缺陷**,只能写入 §8「未确定项 / 待确认观察」。" + - "**不得破坏既有正确内容:** 只按四条指示增改;既有正确的章节、表格、计数与锚点须保留;计数行若因重构变化须同步一致(本计划预期七项计数不变)。" + - "不得把门禁建立在「全仓 TS 诊断数」「`pnpm check:type`」「`pnpm build`」之上;不得为了「验证」去改动源码或数据。" + - "不得触碰、暂存、提交用户的并发改动(D-56):提交前 `git status` 逐条核对暂存面,只暂存本计划 `files_modified` 的条目。" + open_items: + - "**OI-1(`num` 分配策略候选):** §1 将给出 ≥3 个 `num` 分配策略候选(如「按 `TileType` 分段」「沿用旧 `num`」「按目标贴图分组顺序」「按 `id` 次序」),均标注「待用户报备批准」。**待用户裁定采用何种策略**(本计划只记录候选,不落地)。" + - "**OI-2(`texture/.list.jsonc` 类型声明命名):** §7 将给出候选 TypeScript 类型名(entry 接口、联合别名、顶层配置接口)与字段名,均标注「待用户报备批准」。**待用户报备批准命名**(D-73)。" + - "**OI-3(用户指示中的「§5」):** 本文档 §5 为 NPC 节,与 `animates` / `terrains` 无关;本计划以「§0 / §2 为主 + §5 交叉引用」方式落实指令 1。**待用户确认该处理方式。**" + - "**OI-4(`animates` 逻辑类型与素材分组的区分):** 本计划明确「§1.1 `animates` → `TileType.Animate`(逻辑类型,保留)」与「素材 / 分图分组并入 `terrains`(D-72③)」为两件事。**待用户确认该消歧方式。**" +--- + + +Phase 9 · 任务 8 后续 · 第 18 个计划 —— **按用户 2026-10-13 四条指示,原地修订 09-17 产出的只读盘点台账 `09-DATA-MIGRATION-AUDIT.md`**(**不实施实际迁移**)。 + +**要解决的问题(用户四条指示 + D-70..D-75):** 09-17 产出的盘点台账存在四处需要收敛之处:① `animates` 并入 `terrains`(D-72③)的表述散布在 §0 / §1 / §2 / §9,易于被理解为「`animates` 仍为独立素材组」,需强化到无歧义;② 文档把「无目标字段(只记录、不移植)」与「有目标字段但映射不确定(待定)」混在一起(尤见 §8 与 §1.2 / §3.1 / §4.2 / §6.2),需按「新接口是否存在对应字段」严格二分,并在各映射候选表逐字段显式标注;③ 图块 `num` 被默认为沿用旧引擎顺序,需明确「`num` 可全部重新分配,只要 `id` ↔ `num` 一致」,并给出 `id` 全清单与新 `num` 分配策略候选;④ §7 的 `texture/.list.jsonc` schema 草案仅有文字描述,需补上**显式 TypeScript 类型声明**。**本计划只修订文档,不改任何源码 / 数据、不生成素材、不实施迁移。** + +**大致解决方案(本计划 = 09-18):** +- **Task 0(`checkpoint:decision@blocking-human`)= 预执行汇报关卡**:汇报本修订计划要解决的问题(上述四处)、四条指示的逐条落实方式、只读保证、修订方式(**派发子代理执行**)、产出文件、静态门禁与计数一致性要求,并等待用户「可以执行」。 +- **Task 1 = 指令 1(§0 部分)+ 指令 2(§1 部分)+ 指令 3**:采集只读基线;在 §0 强化 `animates` 并入 `terrains`(D-72③);在 §1 加入「`num` 可全部重新分配」说明与 §1.5「`id` 全清单 + 新 `num` 分配策略候选(待报备)」;在 §1.2 逐字段加分类标注;§1.4 同步强化。 +- **Task 2 = 指令 1(§2 / §5 / §9)+ 指令 2(§3 / §4 / §6 + §8 重构)+ 指令 4(§7)**:在 §2 / §5 / §9 强化 `animates` 并入 `terrains`;在 §3 / §4 / §6 逐字段加分类标注;重构 §8 为 8.1 / 8.2 / 8.3;在 §7 补显式 TypeScript 类型声明;收口跑最终静态门禁并写 `09-18-SUMMARY.md`。 + +**为何分两个工作任务:** 指令 1 与指令 3 都以 §1 为主承载区,合并在 Task 1 一次成形;指令 2(§3 / §4 / §6 + §8)与指令 4(§7)与指令 1 的余下位置(§2 / §5 / §9)合并到 Task 2,避免对同一节反复改写、并让 §8 的二分在 §1 / §3 / §4 / §6 标注全部就位后集中完成。两任务同属一个只读文档的原地修订,无 `files_modified` 外扩。 + +**修改方式声明(AGENTS.md):** 本计划的文档修订由**派发子代理(`gsd-executor`)执行**,非内联执行;除 `files_modified` 两份 `.md` 外无任何写入。 + +**API coverage:** `detected:false`。**`No external API integration: 本计划仅为只读盘点台账的文档修订,不涉及任何外部 API。`** + + +@C:/Users/13194/.config/opencode/gsd-core/workflows/execute-plan.md +@C:/Users/13194/.config/opencode/gsd-core/templates/summary.md + + + +@.planning/PROJECT.md +@.planning/ROADMAP.md +@.planning/STATE.md +@.planning/phases/09-minimum-browser-runnable/09-CONTEXT.md +@.planning/phases/09-minimum-browser-runnable/09-DATA-MIGRATION-AUDIT.md +@.planning/phases/09-minimum-browser-runnable/09-17-SUMMARY.md +@AGENTS.md +@dev.md + + + + + + Task 0: 预执行汇报关卡 —— 汇报「按用户四条指示修订 09-DATA-MIGRATION-AUDIT.md」的要做什么 / 为什么 / 大致怎么做 / 影响范围,等待用户回复「可以执行」 + `09-DATA-MIGRATION-AUDIT.md`(09-17 产出)已存在;`packages-user/data-common/src/store/types.ts`、`packages-user/client-base/src/material/types.ts`、`src/content/texture/.list.jsonc`、`src/content/tile.jsonc` 均存在;`09-18-SUMMARY.md` **尚不存在**。 + (只读汇报,不修改任何文件) + + - .planning/phases/09-minimum-browser-runnable/09-CONTEXT.md(D-03 / D-04 / D-06 / **D-70 / D-71 / D-72 / D-73 / D-74 / D-75**) + - .planning/phases/09-minimum-browser-runnable/09-DATA-MIGRATION-AUDIT.md(被修订对象现状:§0..§9) + - .planning/phases/09-minimum-browser-runnable/09-17-SUMMARY.md(上游摘要,只读参考) + - .planning/phases/09-minimum-browser-runnable/09-18-PLAN.md(本计划全文,含四条指示的落实方式与 OI-1..OI-4) + - AGENTS.md(执行前须获批、命名须先反馈、修改方式须预先声明);dev.md(命名规则、注释规范、Markdown 连字符命名) + + + **停下并向用户汇报,等待明确回复「可以执行」后才继续;汇报本身只读。** + + ① **要解决的问题:** 09-17 产出的只读盘点台账 `09-DATA-MIGRATION-AUDIT.md` 有四处需按用户 2026-10-13 指示收敛:**①** `animates` 并入 `terrains`(D-72③)表述需强化到无歧义;**②** 「无目标字段 → 只记录、不移植」与「有目标字段但映射不确定 → 待定」需严格二分并逐字段标注;**③** 图块 `num` 需明确可全部重新分配(`id` ↔ `num` 一致即可)并给出 `id` 全清单与新 `num` 分配策略候选;**④** §7 的 `texture/.list.jsonc` schema 需补显式 TypeScript 类型声明。**只修订文档,不迁移。** + + ② **范围与产出:** 只修订 `.planning/phases/09-minimum-browser-runnable/09-DATA-MIGRATION-AUDIT.md`(原地);另新增 `.planning/phases/09-minimum-browser-runnable/09-18-SUMMARY.md`。**全程只读**:零 `.ts` / `.tsx` 改动、零 `public/project/**` 与 `src/content/**` 改动、零 `.png` 生成;不改 09-17 计划 / 摘要、`ROADMAP.md`、`STATE.md`、`09-CONTEXT.md`。 + + ③ **四条指示的逐条落实方式:** + - **指令 1(`animates` 合并至 `terrains`,确认 D-72③):** 在 §0(D-72 原文处)与 §2(§2.1 / §2.4 / §2.5)及 §5,以及一切出现 `animates` / `terrains` 的位置(§1.1 / §1.4 / §9)强化固定表述「`animates` 并入 `terrains`,不设独立 `animates` 素材组」;并区分「§1.1 逻辑类型映射 `animates` → `TileType.Animate`(保留)」与「素材 / 分图分组并入 `terrains`(D-72③)」。 + - **指令 2(严格界定「只记录、不移植」):** 判定基准为 `ITileRawData` / `ITilePassData` / `IEnemyRawData` / `IItemRawData` / `IMapRawData`(`packages-user/data-common/src/store/types.ts`);**仅当接口不存在对应字段**才标「只记录、不移植」;§1 / §3 / §4 / §6 的映射表逐字段标注三类;§8 重构为 §8.1「无目标字段 → 只记录、不移植」与 §8.2「有目标字段但映射不确定 → 待定」(非字段级观察入 §8.3);**不得**把有目标字段项(如旧 tile `trigger` → `ITileRawData.events` / `eventPass`、旧 map `events` → `IMapRawData.events`)误标为只记录。 + - **指令 3(`num` 可全部重新排序):** §1 明确「`num` 可全部重新分配,不沿用旧引擎顺序,唯一约束是 `id` ↔ `num` 一致」;新增 §1.5「`id` 全清单(235 条)+ 新 `num` 分配策略候选(≥3 个,标「待用户报备批准」)」;旧 `num` 仅作参考。 + - **指令 4(schema 须显式类型声明):** §7 增补显式 TypeScript 类型声明区块(`interface` 与 `type` 别名),至少含四个 entry 接口(grid / rowAnimate / autotile / tileset)、四者联合类型别名、顶层配置接口;所有新增命名标注「待用户报备批准」。 + + ④ **只读保证(D-74 / D-75):** 唯一允许写入 = 上述两份 `.md`;禁止任何源码 / 数据写入与 PNG 生成;命名只作候选、不落地。 + + ⑤ **验证方式:** 静态断言——文档存在、含 §0..§9 与 §8.1 / §8.2 分区、含七项计数(值不变)、含四条指示对应的标记 token、锚点逐一校验「文件存在 + 行号在范围内」;**只读守卫**采用「执行前基线对比」(基线由 Task 1 `` 步骤 (0) 一次性写入仓库外临时目录,`` 断言幂等不覆写)。**不设全仓类型 / 构建门禁。** + + ⑥ **修改方式声明(AGENTS.md):** 本修订由**派发子代理执行**(`gsd-executor`),非内联执行。 + + ⑦ **开放项:** OI-1(`num` 分配策略候选,待裁定)、OI-2(`texture/.list.jsonc` 类型命名候选,待报备)、OI-3(用户指示中「§5」的定位:本文档 §5 为 NPC 节,以交叉引用落实)、OI-4(`animates` 逻辑类型与素材分组的消歧方式,待确认)。 + + ⑧ **关卡性质:** **无论触发方式或 `mode: yolo` / `workflow.auto_advance`,执行者 / 子代理必须停下等待用户明确回复「可以执行」四个字**;回复其它内容视为未通过;若用户给出修订意见,退出本计划、修订后重新汇报。 + + 是否按本计划(09-18)执行「按用户四条指示修订 `09-DATA-MIGRATION-AUDIT.md`」(原地修订文档 + 新增 `09-18-SUMMARY.md`;只读,不改任何源码与数据,不生成素材,不实施迁移),并按 OI-1..OI-4 的默认处置产出文档 + + AGENTS.md / D-03 / D-04 要求执行前与命名先获批;`.planning/config.json` 为 `mode: yolo`,故本关卡显式 `blocking-human` 且 `autonomous: false`。本次核心 = 四条指示的落实方式确认(含 §5 的定位与 `animates` 消歧方式)+ 只读边界确认 + 产出文件确认 + 修订方式(派发子代理)声明。执行期工作树存在用户并发改动(`AGENTS.md`,D-56),故只读守卫采用「执行前基线对比」。行号为规划日实测、可能漂移,文档锚点与门禁以执行日实测为准。 + + + + + + + 回复「可以执行」四个字(如需调整 OI-1..OI-4 的处置,请在回复中指明;修订意见请按 action ⑧ 处理) + + node -e "const fs=require('fs');const bad=[];const doc='.planning/phases/09-minimum-browser-runnable/09-DATA-MIGRATION-AUDIT.md';const sum='.planning/phases/09-minimum-browser-runnable/09-18-SUMMARY.md';if(!fs.existsSync(doc))bad.push('missing audit doc (09-17 output)');if(fs.existsSync(sum))bad.push('post-state: 09-18-SUMMARY already exists');for(const p of ['packages-user/data-common/src/store/types.ts','packages-user/client-base/src/material/types.ts','src/content/texture/.list.jsonc','src/content/tile.jsonc']){if(!fs.existsSync(p))bad.push('missing ref: '+p)}if(bad.length>0){console.error('PRESTATE FAILED :: '+bad.join(' ; '));process.exit(1)}console.log('OK pre-execution state: audit doc present; 09-18 summary absent; type/target refs present')" + non-zero exit, 或 stderr 含 "PRESTATE FAILED"(被修订对象缺失,或产出摘要已存在,或关键类型 / 落点参考文件缺失) + 用户在对话中明确回复「可以执行」四个字(人工核对回复原文;用户若指定非默认的落实方式 / §5 定位 / `num` 策略口径 / 命名处置,须在汇报与后续任务中据其指定执行) + + + - 用户回复中明确包含「可以执行」四个字 + - 汇报内容涵盖:本修订计划要解决的问题(四处)、四条指示的逐条落实方式、只读保证、修订方式(派发子代理)、产出文件、验证方式(静态断言,无全仓门禁)、OI-1..OI-4 + - 汇报中已原样告知:**只读(不改任何 `.ts`/`.tsx` 与 `public/project/**` 与 `src/content/**`、不生成任何 `.png`、不实施迁移、命名候选只报备不落地、锚点不得臆造、疑似问题只入 §8 不当缺陷)** + - 汇报中已原样告知**关卡性质**(必须停下等待,不因触发方式或 `yolo` / `auto_advance` 自动放行) + + 用户明确回复「可以执行」;在此之前未执行任何任务、未修改任何文件、未派发任何执行子代理 + + + + Task 1(指令 1 §0 + 指令 2 §1 + 指令 3):强化 §0 的 `animates` 并入 `terrains`;在 §1 加入 `num` 可全部重分配说明、`id` 全清单与新 `num` 分配策略候选,并逐字段加分类标注 + Task 0 已闭环:用户明确回复「可以执行」四个字(若用户就 OI-1..OI-4 另有指定,按指定执行)。 + .planning/phases/09-minimum-browser-runnable/09-DATA-MIGRATION-AUDIT.md(原地修订;另向预批准临时目录写只读基线,仓库外) + + - .planning/phases/09-minimum-browser-runnable/09-DATA-MIGRATION-AUDIT.md(现状:§0 口径 / §1 tile 含 §1.1 `cls`→`TileType`、§1.2 字段映射候选、§1.3 `num`/`id` 全清单、§1.4 图块→贴图归属) + - .planning/phases/09-minimum-browser-runnable/09-CONTEXT.md(D-72 / D-73 / D-74 / D-75) + - packages-user/data-common/src/store/types.ts:27-63(`PassBit` / `ITilePassData` / `ITileRawData`)—— 分类判定的唯一依据 + - packages-user/data-common/src/store/types.ts:6-25(`TileType`)、:125-136(`ItemCategory`)—— 逻辑类型映射参考 + - AGENTS.md(命名须先报备;只读边界);dev.md(命名规则、注释规范、Markdown 连字符命名) + + + (0) **采集只读基线(先于任何写入):** 运行 `git status --porcelain`,取其改动路径(去重排序)中匹配 `.ts` / `.tsx` 或前缀 `public/project/` / `src/content/` 的条目,写入**预批准临时目录**的基线文件 `process.env.TEMP + '/opencode/gsd-09-18-baseline.txt'`(仓库外;与 09-17 的基线文件相互独立)。基线只记录执行前的既有改动,**不修改任何仓库文件**。**此步骤是本计划中唯一写入基线的动作**;后续 `` 只读该基线、不覆写(幂等)。 + + (1) **指令 1(`animates` 并入 `terrains`,D-72③)——§0 部分:** 在 §0.5「D-72 五条拆分规则原文(含 tilesets / autotiles 例外)」的原文引用之后,追加一段**强化说明**,使用**固定表述**「`animates` 并入 `terrains`,不设独立 `animates` 素材组」,并明确:**①** 这是**素材 / 分图分组**层面的合并(`animates` 的图块贴图与 `terrain` 同归 `terrains` 素材组),但受规则④约束(`animates` 为多帧,与单帧 `terrains` 分开成图);**②** **不得**与「逻辑类型映射」混淆——§1.1 中旧 `cls: animates` → `TileType.Animate` 的映射**保留不变**(`TileType` 枚举里 `Terrain` 与 `Animate` 并存)。 + + (2) **指令 3(`num` 可全部重新排序)——§1:** + - 在 §1 数据来源段落后,加入**固定表述**:「图块 `num` 可全部重新分配,不沿用旧引擎顺序,唯一约束是 `id` ↔ `num` 一致(`addTile` 的 `num` 与 `id` 均不冲突即可)」;并注明旧 `num` 仅作参考对照。 + - 新增 **§1.5「`id` 全清单与新 `num` 分配策略候选」**:**①** 给出 **235 条 `id` 全清单**(可复用 / 汇总 §1.3 的 `id` 列,或独立按 `cls` 分组列出;须完整覆盖 235 条,不遗漏);**②** 给出 **≥3 个新 `num` 分配策略候选**,每条含「策略要点 + 优点 / 缺点 + 与旧 `num` 的对照说明」,例如:按 `TileType` 分段(`Terrain` / `Animate` / `Item` / `Enemy` / `Npc` / `Autotile` / `Tileset` 各占一段)、沿用旧 `num`(不重排,最省迁移)、按目标贴图(拆分后图)分组顺序重排、按 `id` 次序分配;**③** 每个策略候选均标注「待用户报备批准」;**④** 明确「本盘点只记录候选,不落地任何 `num` 分配」。 + + (3) **§1.4 强化:** 在 §1.4「图块 → 贴图归属候选」中,把 `animates` 行(现为「`terrains` 组(多帧,D-72③ 并入)」)强化为包含固定表述「`animates` 并入 `terrains`,不设独立 `animates` 素材组」,与 §0.5 表述一致。 + + (4) **指令 2(严格界定「只记录、不移植」)——§1 部分:** 在 §1.2「字段映射候选 → `ITileRawData`」的表格**新增一列「分类」**,对**每一个**旧字段逐条标注三类之一:**映射候选(可确定)** / **待定(有目标字段但映射不确定)** / **只记录、不移植(目标接口无对应字段)**。判定基准为 `ITileRawData` 与 `ITilePassData`(`packages-user/data-common/src/store/types.ts`,执行日实测)。 + - 对用户点名示例 **`trigger` / `canBreak` / `script` / `doorInfo`** 必须**逐条判定并写明依据**(引用对应目标接口字段或其缺失):旧 `trigger` 若确有对应目标字段(`ITileRawData.events` / `eventPass`)则标**待定**,**不得**标只记录;旧 `canBreak` / `script` / `doorInfo` 若无对应字段(经实测确认)则标**只记录、不移植**。 + - 逐条核对既有 §1.2 行(如 `id` / `cls` / `canPass` / `cannotOut` / `cannotIn` / `animate` / `faceIds` / `autotileConnection` / `name`)的分类,凡**有明确目标字段**者**不得**标「只记录、不移植」。 + + (5) **收口:** 不改任何源码 / 数据;不改任何既有注释;§0 与 §1(含新增 §1.5)LF;既有正确内容保留;机器可读行 `TILE-COUNT:` 保持 `235`、`MATERIAL-COUNT:` 保持 `17`、`SPLIT-COUNT:` 保持 `42`(本任务**不**改动任何计数数值)。 + + 原地修订只读 `.md` 文档(+ 仓库外临时基线);`git checkout` / 手动撤销可整体回退;未触及任何源码或数据文件。 + + node -e "const {execSync}=require('child_process');const fs=require('fs');const base=process.env.TEMP+'/opencode/gsd-09-18-baseline.txt';if(fs.existsSync(base)){console.log('OK baseline already captured by action step (0) (idempotent; verify does NOT overwrite) -> '+base)}else{const lines=execSync('git status --porcelain',{encoding:'utf8'}).split(/\r?\n/).filter(Boolean).map(l=>l.slice(3).trim()).filter(p=>/(\.(ts|tsx)$|^public\/project\/|^src\/content\/)/.test(p)).sort();fs.mkdirSync(require('path').dirname(base),{recursive:true});fs.writeFileSync(base,lines.join('\n'));console.log('OK baseline captured ('+lines.length+' pre-existing source/data changes) -> '+base+' | fallback: action step (0) did not persist baseline')}" + non-zero exit(无法读取 / 采集只读基线;后续只读守卫将无对照) + node -e "const fs=require('fs');const p='.planning/phases/09-minimum-browser-runnable/09-DATA-MIGRATION-AUDIT.md';const bad=[];if(!fs.existsSync(p)){bad.push('doc missing')}else{const t=fs.readFileSync(p,'utf8').replace(/\r\n/g,'\n');for(const s of ['## 0.','## 1.']){if(t.indexOf(s)<0)bad.push('missing heading: '+s)}const counts=[['TILE-COUNT:',235],['MATERIAL-COUNT:',17],['SPLIT-COUNT:',42]];for(const c of counts){if(!new RegExp(c[0]+'\\s*'+c[1]).test(t))bad.push('missing count '+c[0]+c[1])}for(const m of ['animates','terrains','不设独立','可全部重新分配','id 全清单','num 分配策略候选','只记录、不移植','待用户报备批准','TileType.Animate']){if(t.indexOf(m)<0)bad.push('missing token: '+m)}if(t.indexOf('不设独立')>=0){if(t.indexOf('不设独立')>t.indexOf('## 1.'))bad.push('animates merge note not placed in section 0')}const anchors=[...t.matchAll(/((?:public\/project|packages-user|src\/content)\/[^\s|)\u0060]+\.(?:js|png|ts|tsx|jsonc)):(\d+)/g)];if(anchors.length<1)bad.push('no file:line anchors');for(const m of anchors){const f=m[1];const ln=parseInt(m[2],10);if(!fs.existsSync(f)){bad.push('anchor to missing file: '+f);continue}const lines=fs.readFileSync(f,'utf8').split(/\r?\n/);if(ln<1||ln>lines.length)bad.push('anchor out of range: '+f+':'+ln)}}if(bad.length>0){console.error('REV-AUDIT-A GATE FAILED :: '+bad.join(' ; '));process.exit(1)}console.log('OK revision A (section 0 animates merge + section 1 num realloc + id list + num strategy candidates + per-field classification)')" + non-zero exit, 或 stderr 含 "REV-AUDIT-A GATE FAILED"(文档缺失 / 缺 §0 或 §1 / 缺计数 / 缺指令 1 标记(`animates` `terrains` `不设独立`)/ 缺指令 3 标记(`可全部重新分配` `id 全清单` `num 分配策略候选`)/ 缺指令 2 标记(`只记录、不移植`)/ 缺命名报备标记 / `animates` 强化未落在 §0 / 锚点指向不存在的文件或越界行号) + node -e "const {execSync}=require('child_process');const fs=require('fs');const base=process.env.TEMP+'/opencode/gsd-09-18-baseline.txt';const cur=execSync('git status --porcelain',{encoding:'utf8'}).split(/\r?\n/).filter(Boolean).map(l=>l.slice(3).trim()).filter(p=>/(\.(ts|tsx)$|^public\/project\/|^src\/content\/)/.test(p)).sort();const prev=fs.existsSync(base)?fs.readFileSync(base,'utf8').split(/\r?\n/).filter(Boolean).sort():[];const added=cur.filter(x=>!prev.includes(x));if(added.length){console.error('READONLY FAILED :: new source/data changes :: '+added.join(' | '));process.exit(1)}console.log('OK read-only after Task 1: no new .ts/.tsx/public-project/src-content changes vs pre-write baseline')" + non-zero exit, 或 stderr 含 "READONLY FAILED"(本计划执行期(含 Task 1 动作阶段)新增了 `.ts`/`.tsx` 或 `public/project/**` 或 `src/content/**` 改动,违反 D-74/D-75 只读) + + + - §0.5 含固定表述「`animates` 并入 `terrains`,不设独立 `animates` 素材组」,并区分逻辑类型映射(`TileType.Animate`)与素材分组 + - §1 含「`num` 可全部重新分配,不沿用旧引擎顺序,唯一约束是 `id` ↔ `num` 一致」;含新增 §1.5(235 条 `id` 全清单 + ≥3 个 `num` 分配策略候选,均标「待用户报备批准」,旧 `num` 仅作参考) + - §1.2 表格新增「分类」列,逐字段标注三类;**有明确目标字段者未被标为「只记录、不移植」**;对 `trigger` / `canBreak` / `script` / `doorInfo` 逐条给出判定与依据 + - §1.4 的 `animates` 行含与 §0.5 一致的固定表述 + - 只读基线由 `` 步骤 (0) 一次性写入(仓库外),`` 断言幂等不覆写;未改动任何 `.ts`/`.tsx`、`public/project/**`、`src/content/**`;未生成任何 `.png`;未改任何既有注释;计数数值未变 + + §0 与 §1(含新增 §1.5)修订完成;`animates` 强化落在 §0 / §1.4;`num` 重排说明与 `id` 全清单 + 策略候选就位;§1.2 分类标注完成;只读基线与静态门禁全绿 + + + + Task 2(指令 1 §2 / §5 / §9 + 指令 2 §3 / §4 / §6 + §8 重构 + 指令 4 §7):完成余下强化与分类标注、§8 二分重构、§7 显式类型声明,跑最终门禁并写 SUMMARY + Task 1 已完成(文档 §0 与 §1 已就位;只读基线文件存在于仓库外,由 Task 1 `` 步骤 (0) 采集且未被覆写)。 + .planning/phases/09-minimum-browser-runnable/09-DATA-MIGRATION-AUDIT.md, .planning/phases/09-minimum-browser-runnable/09-18-SUMMARY.md + + - .planning/phases/09-minimum-browser-runnable/09-DATA-MIGRATION-AUDIT.md(本文件现状:§0 与 §1 已修订;待改 §2 / §3 / §4 / §5 / §6 / §7 / §8 / §9) + - packages-user/data-common/src/store/types.ts:125-213(`ItemCategory` / `IItemEffect` / `IItemEquipData` / `IItemRawData`)、:260-271(`IMapRawData`)、:319-326(`IEnemyRawData`)—— §3 / §4 / §6 分类判定的唯一依据 + - packages-user/client-base/src/material/types.ts:12-17(`AutotileType`)、:50-55(`IIndexedIdentifier`)、:261-307(`addGrid` / `addRowAnimate` / `addAutotile` / `addTileset`)—— §7 类型声明的依据 + - src/content/texture/.list.jsonc:1(现状 `{}`)、src/content/client.jsonc:9(`textureListDir`)—— §7 草案落点参考 + - .planning/phases/09-minimum-browser-runnable/09-CONTEXT.md(D-72 / D-73 / D-74 / D-75) + - AGENTS.md(命名须先报备;只读边界);dev.md(命名规则、注释规范) + + + (1) **指令 1(§2 / §5 / §9 部分):** + - §2.1 / §2.4 / §2.5:强化固定表述「`animates` 并入 `terrains`,不设独立 `animates` 素材组」(D-72③);确认 §2.4 B 组(`animates` 多帧并入 `terrains`)与 §2.5 命名(`terrainsAnim*`)表述一致、无歧义。 + - §5(NPC):加一行**交叉引用**,明确 D-72③(`animates` 并入 `terrains`)与本节的 NPC 分组(依 D-72① 并入 `npc`)互不冲突;**不**在 NPC 节强塞无关的 `animates` 内容。 + - §9:在汇总 / 命名说明处同步强化 `animates` 并入 `terrains`(D-72③),与 §0.5 / §2.4 一致。 + + (2) **指令 2(§3 / §4 / §6):** 在 §3.1 / §4.2 / §6.2 的映射候选表**各新增一列「分类」**,对**每一个**旧字段逐条标注三类之一:**映射候选(可确定)** / **待定(有目标字段但映射不确定)** / **只记录、不移植(目标接口无对应字段)**。判定基准(执行日实测):§3 用 `IEnemyRawData`;§4 用 `IItemRawData` / `IItemEffect` / `IItemEquipData`;§6 用 `IMapRawData`。 + - 特别判定并写明依据:旧 map **`events`** → `IMapRawData.events`(**有目标字段** → 标**待定**,**不得**标只记录);旧 map `title` / `name` / `height` / `firstArrive` / `eachArrive` / `parallelDo` / `changeFloor` / `afterBattle` / `afterGetItem` / `afterOpenDoor` / `beforeBattle` / `cannotMove` / `cannotMoveIn` / `autoEvent` / `images` / `bgm` / `weather` / `ratio` / `defaultGround` / `canFlyTo` / `canFlyFrom` / `canUseQuickShop` / `cannotViewMap` / `upFloor` 等(经实测确认无对应字段者)标**只记录、不移植**;`bgmap` / `fgmap` / `bg2map` / `fg2map` 是否落入 `IMapRawData.map` / `layerAlias` 判定为**待定**或**只记录**(以实测为准并写明依据)。 + - §3:旧 `name` / `hp` / `atk` / `def` / `money` / `exp` / `point` → `attribute`(**有目标字段** → 映射候选);`special` → 数字键映射对象(**待定**);`value` / `range` / `zoneSquare` / `add` / `n` / `bigImage` / `notBomb` / `atkValue` / `defValue` / `faceIds` 逐条判定并写明依据(凡有对应字段者不得标只记录)。 + - §4:旧 `name` / `text` / `hideInToolbox` → 直接对应字段(映射候选);`itemEffect` / `itemEffectTip` / `useItemEffect` / `useItemEvent` / `canUseItemEffect` → `IItemEffect`(**有目标字段** → 待定 / 映射候选,按实测);`equip` → `IItemEquipData`(映射候选);`hideInReplay`(经实测确认无对应字段)→ **只记录、不移植**。 + + (3) **指令 2(§8 重构):** 将 §8 重构为三分区:**§8.1「无目标字段 → 只记录、不移植」**(字段级,逐条注明所查接口与其缺失字段)、**§8.2「有目标字段但映射不确定 → 待定」**(字段级)、**§8.3「其他待确认观察」**(保留既有非字段级观察:D-72⑤ 读法、`ground` / `airwall` / `keyboard` / `icons` / `icons_old` 归类、`autotile1.png` 类型、`magictower.png` 切格尺寸、48px 单元格、`None` / `Tileset` 图块等);确保 §8.1 / §8.2 的归类与 §1.2 / §3.1 / §4.2 / §6.2 的「分类」列**逐条一致**;疑似问题**不得**登记为缺陷。**保留既有 `U-` 编号观察项**(可将其重排入三分区,但内容不得丢失)。 + + (4) **指令 4(§7 显式类型声明):** 在 §7 schema 草案中新增「**显式 TypeScript 类型声明**」区块(保留原有字段结构说明,并补充可读、可落地参考的 `interface` 与 `type` 别名)。至少含: + - 四个 entry 接口(候选名 `ITextureListGridEntry` / `ITextureListRowAnimateEntry` / `ITextureListAutotileEntry` / `ITextureListTilesetEntry`),字段与 `addGrid` / `addRowAnimate` / `addAutotile` / `addTileset` 的参数形状一致(grid / rowAnimate 含 `source` / `map` / `width` / `height`;autotile 含 `source` / `identifier` / `type`(`AutotileType`);tileset 含 `source` / `identifier`(`IIndexedIdentifier`)/ `cellWidth` / `cellHeight`); + - 一个联合类型别名(候选名 `TextureListEntry`)联合上述四者; + - 一个顶层配置接口(候选名 `ITextureListConfig`)含 `textures` 数组字段(元素为 `TextureListEntry`)。 + **所有新增类型名 / 字段名均标注「待用户报备批准」**,与 §2 登记逐条一致,**不落地写入任何文件**(D-73 / OI-2)。 + + (5) **§9 一致化:** 计数行与各节一致(`TILE-COUNT:` 235 / `MATERIAL-COUNT:` 17 / `SPLIT-COUNT:` 42 / `ENEMY-COUNT:` 81 / `ITEM-COUNT:` 62 / `NPC-COUNT:` 24 / `MAP-COUNT:` 5);命名说明更新为含 §7 类型声明命名候选。 + + (6) **写 `09-18-SUMMARY.md`:** 记录四条指示的落实位置、命名候选清单(含 `num` 策略与 §7 类型名)、各维度计数(不变)、只读确认、遗留待用户裁定项(OI-1..OI-4)。 + + (7) **收口:** 不改任何源码 / 数据;不改任何既有注释;不生成任何 PNG;文档 LF;本任务 `` 运行最终静态门禁(§0..§9 + §8.1 / §8.2 + 七计数 + 四条指示标记 + 锚点有效性 + 只读守卫)。 + + 原地修订只读 `.md` 文档 + 新增 SUMMARY;删除 / `git checkout` 可整体回退;未触及任何源码或数据文件。 + + node -e "const fs=require('fs');const p='.planning/phases/09-minimum-browser-runnable/09-DATA-MIGRATION-AUDIT.md';const s='.planning/phases/09-minimum-browser-runnable/09-18-SUMMARY.md';const bad=[];if(!fs.existsSync(p)){bad.push('doc missing')}if(!fs.existsSync(s)){bad.push('summary missing')}if(fs.existsSync(p)){const t=fs.readFileSync(p,'utf8').replace(/\r\n/g,'\n');for(const h of ['## 0.','## 1.','## 2.','## 3.','## 4.','## 5.','## 6.','## 7.','## 8.','## 9.','### 8.1','### 8.2']){if(t.indexOf(h)<0)bad.push('missing heading: '+h)}const counts=[['TILE-COUNT:',235],['MATERIAL-COUNT:',17],['SPLIT-COUNT:',42],['ENEMY-COUNT:',81],['ITEM-COUNT:',62],['NPC-COUNT:',24],['MAP-COUNT:',5]];for(const c of counts){if(!new RegExp(c[0]+'\\s*'+c[1]).test(t))bad.push('missing count '+c[0]+c[1])}for(const m of ['animates','terrains','不设独立','只记录、不移植','待定','interface ','type ','ITextureListConfig','ITextureListGridEntry','AutotileType','IIndexedIdentifier','待用户报备批准']){if(t.indexOf(m)<0)bad.push('missing token: '+m)}if(t.indexOf('不设独立')>=0){if(t.lastIndexOf('不设独立')lines.length)bad.push('anchor out of range: '+f+':'+ln)}}if(bad.length>0){console.error('REV-AUDIT-B GATE FAILED :: '+bad.join(' ; '));process.exit(1)}console.log('OK final revision (sections 0-9 + 8.1/8.2 split + seven counts + four instruction markers + explicit TS types + anchors valid + summary present)')" + non-zero exit, 或 stderr 含 "REV-AUDIT-B GATE FAILED"(文档或 SUMMARY 缺失 / 缺 §0..§9 或 §8.1 或 §8.2 / 缺任一维度计数 / 缺指令 1 标记(`animates` `terrains` `不设独立`,且未在 §2 及之后强化)/ 缺指令 2 标记(`只记录、不移植` `待定`)/ 缺指令 4 标记(`interface ` `type ` `ITextureListConfig` `ITextureListGridEntry`)/ 缺命名报备标记 / 锚点指向不存在的文件或越界行号) + node -e "const {execSync}=require('child_process');const fs=require('fs');const base=process.env.TEMP+'/opencode/gsd-09-18-baseline.txt';const cur=execSync('git status --porcelain',{encoding:'utf8'}).split(/\r?\n/).filter(Boolean).map(l=>l.slice(3).trim()).filter(p=>/(\.(ts|tsx)$|^public\/project\/|^src\/content\/)/.test(p)).sort();const prev=fs.existsSync(base)?fs.readFileSync(base,'utf8').split(/\r?\n/).filter(Boolean).sort():[];const added=cur.filter(x=>!prev.includes(x));if(added.length){console.error('READONLY FAILED :: new source/data changes :: '+added.join(' | '));process.exit(1)}console.log('OK read-only final: no new .ts/.tsx/public-project/src-content changes vs pre-write baseline')" + non-zero exit, 或 stderr 含 "READONLY FAILED"(本计划执行期新增了 `.ts`/`.tsx` / `public/project/**` / `src/content/**` 改动,违反 D-74/D-75 只读) + git status --porcelain + 输出包含本计划 `files_modified`(`09-DATA-MIGRATION-AUDIT.md` + `09-18-SUMMARY.md`)之外、且**基线不存在**的生产文件或数据改动(如 `.ts`/`.tsx`、`public/project/**`、`src/content/**`、`package.json`);用户既有并发改动(D-56)不计 + + + - §2 / §5 / §9 含固定表述「`animates` 并入 `terrains`,不设独立 `animates` 素材组」(D-72③),与 §0.5 / §1.4 一致 + - §3.1 / §4.2 / §6.2 表格各含「分类」列,逐字段标注三类;**有明确目标字段者(如旧 map `events`)未被标为「只记录、不移植」** + - §8 重构为 §8.1(无目标字段 → 只记录、不移植)/ §8.2(有目标字段但映射不确定 → 待定)/ §8.3(其他待确认观察),与 §1.2 / §3.1 / §4.2 / §6.2 的「分类」列一致;疑似问题未登记为缺陷 + - §7 含显式 TypeScript 类型声明(四个 entry 接口 + 联合别名 + 顶层配置接口,候选名 `ITextureListConfig` / `ITextureListGridEntry` 等),字段与 `addGrid` / `addRowAnimate` / `addAutotile` / `addTileset` 一致,命名标注「待用户报备批准」 + - §9 七项计数与各节一致(值不变);`09-18-SUMMARY.md` 已写 + - 全部 `file:line` 锚点有效;未改动任何 `.ts`/`.tsx`、`public/project/**`、`src/content/**`;未生成任何 `.png`;未改任何既有注释 + + §2..§9 修订完成;§8 二分重构就位并与各映射表一致;§7 显式类型声明就位;七项计数不变;最终静态门禁 + 锚点校验 + 只读守卫全绿;`09-18-SUMMARY.md` 落盘 + + + + + +## Trust Boundaries + +| Boundary | Description | +|----------|-------------| +| 只读边界 | 修订只读取 `09-DATA-MIGRATION-AUDIT.md` 与类型定义;**禁止任何源码 / 数据写入与 PNG 生成**(D-74 / D-75) | +| 范围边界 | 只修订既有盘点台账的四类内容(指令 1..4);不扩范围、不动 09-17 计划 / 摘要 / `ROADMAP.md` / `STATE.md` / `09-CONTEXT.md` | +| 判定边界 | 「只记录、不移植」的判定基准严格限定为 `ITileRawData` / `ITilePassData` / `IEnemyRawData` / `IItemRawData` / `IMapRawData` 是否存在对应字段 | +| 命名边界 | 任何新增 / 修改命名(`num` 策略、schema 字段名与 TypeScript 类型名)只作候选入台账、标注「待用户报备批准」,不得落地使用(D-73 / AGENTS.md) | +| 产出边界 | 仅允许写 `09-DATA-MIGRATION-AUDIT.md`(原地修订)与 `09-18-SUMMARY.md`(新增)(+ 仓库外只读基线) | +| 提交边界 | 不得把用户并发改动(D-56,`AGENTS.md`)纳入暂存 / 提交;不改 `ROADMAP.md` / `STATE.md` | + +**威胁级别说明(security_enforcement = ASVS L1,block on high):** 本计划为**纯只读 Markdown 台账修订**,不执行任何生产代码、不引入依赖、不做任何写入面扩展,**不存在 `high` 级威胁**;下列寄存器均为 `medium` 及以下,并逐条给出缓解。 + +## STRIDE Threat Register + +| Threat ID | Category | Component | Severity | Disposition | Mitigation Plan | +|-----------|----------|-----------|----------|-------------|-----------------| +| T-09-18-01 | Tampering | 借「修订」之机修改 / 新增生产源码或数据(`.ts`/`.tsx`、`public/project/**`、`src/content/**`) | medium | mitigate | `files_modified` 白名单 + prohibitions + Task 1/2 `READONLY` 门禁(与**执行前**(Task 1 `` 步骤 (0) 写入、`` 幂等不覆写)的基线对比,检出执行期任何新增改动) | +| T-09-18-02 | Elevation of Privilege | 未经批准即执行 / 使用未报备命名(`num` 策略、schema 字段名与 TypeScript 类型名) | medium | mitigate | Task 0 `blocking-human`;命名候选仅入台账并标注「待用户报备批准」;prohibitions 禁未获批命名落地 | +| T-09-18-03 | Tampering | 分类误判:把**有明确目标字段**的项误标为「只记录、不移植」(或反之),导致台账误导后续迁移 | medium | mitigate | `` 要求以执行日实测接口为唯一判定依据;Task 1/2 门禁校验标记 token 与 `### 8.1`/`### 8.2` 分区;acceptance_criteria 明列「有目标字段者不得标只记录」 | +| T-09-18-04 | Tampering | 臆造条目(编造字段 / 映射 / 锚点,或指向不存在文件 / 越界行号) | medium | mitigate | `` 要求逐项实测;无法确定者入 §8;Task 1/2 门禁逐一校验「文件存在 + 行号在范围内」 | +| T-09-18-05 | Tampering | 生成 / 覆写 PNG 素材(违反 D-75) | medium | mitigate | prohibitions 明列不生成素材;只读守卫覆盖 `public/project/**` 与 `src/content/**`(基线对比) | +| T-09-18-06 | Tampering | 破坏既有正确内容(错误地删除 / 改写既有章节、计数、锚点,或使 §8 与各映射表不一致) | medium | mitigate | prohibitions「不得破坏既有正确内容」;Task 2 门禁校验七项计数与 §8.1/§8.2 分区;acceptance_criteria 要求 §8 与 §1.2/§3.1/§4.2/§6.2 一致 | +| T-09-18-07 | Information Disclosure | 修订范围外泄(处理 animation / images / sounds / bgms / fonts 或改动他处) | low | mitigate | prohibitions 与 `files_modified` 白名单明列边界;只读守卫覆盖 | +| T-09-18-08 | Repudiation | 未跑静态门禁即收口 / 未记录只读确认 | low | mitigate | Task 1/2 各有自动化门禁 + `09-18-SUMMARY.md` 只读确认;未通过不得收口 | +| T-09-18-SC | Tampering | npm/pip/cargo installs | low | accept | 无第三方包安装、无依赖安装(纯只读台账修订,不改 `package.json`) | + + + +- **Task 0**:预执行状态(`09-DATA-MIGRATION-AUDIT.md` 存在、`09-18-SUMMARY.md` 不存在、类型 / 落点参考文件存在);人工关卡(用户明确回复「可以执行」)。 +- **Task 1**:只读基线由 `` 步骤 (0) 一次性采集(仓库外),`` 断言**幂等**(不覆写);文档含 `## 0.` / `## 1.` 与 `TILE/MATERIAL/SPLIT-COUNT`(值不变);含指令 1 标记(`animates` / `terrains` / `不设独立`,落在 §0)、指令 3 标记(`可全部重新分配` / `id 全清单` / `num 分配策略候选`)、指令 2 标记(`只记录、不移植`)与「待用户报备批准」;锚点有效;只读。 +- **Task 2**:文档含 `## 0.`..`## 9.` 与 `### 8.1` / `### 8.2`;含七项计数(值不变);含指令 1 标记(`animates` / `terrains` / `不设独立`,且在 §2 及之后强化)、指令 2 标记(`只记录、不移植` / `待定`)、指令 4 标记(`interface ` / `type ` / `ITextureListConfig` / `ITextureListGridEntry`)与「待用户报备批准」;`09-18-SUMMARY.md` 存在;全部锚点有效;只读。 +- **只读守卫**:执行期无**新增** `.ts`/`.tsx`/`public/project/**`/`src/content/**` 改动(与**执行前**基线对比,基线仅由 Task 1 `` 步骤 (0) 写入、`` 幂等不改写);`files_modified` 外无越界生产文件或数据改动;用户既有并发改动(D-56)不计。 +- **计数一致性**:`TILE-COUNT` 235 / `MATERIAL-COUNT` 17 / `SPLIT-COUNT` 42 / `ENEMY-COUNT` 81 / `ITEM-COUNT` 62 / `NPC-COUNT` 24 / `MAP-COUNT` 5(修订预期不变)。 +- **无全仓门禁**:全程不使用 `pnpm check:type` / `pnpm build` / 全仓 TS 诊断数。 +- **人工复核**:结论写入 `09-18-SUMMARY.md`(含四条指示落实位置、命名候选清单、七项计数、只读确认、OI-1..OI-4)。 + + + +- **指令 1:** `animates` 并入 `terrains`(D-72③)在 §0 / §2 / §5 / §9 及 §1.4 以固定表述强化到无歧义(「不设独立 `animates` 素材组」),并区分逻辑类型映射(`TileType.Animate`)与素材分组。 +- **指令 2:** 「只记录、不移植」严格限定为「目标接口无对应字段」;§1.2 / §3.1 / §4.2 / §6.2 逐字段显式标注三类;§8 重构为 §8.1 / §8.2 / §8.3 且与各表一致;**无**「有明确目标字段却被标只记录」的误标。 +- **指令 3:** §1 明确「`num` 可全部重新分配,不沿用旧引擎顺序,唯一约束是 `id` ↔ `num` 一致」;给出 235 条 `id` 全清单与 ≥3 个 `num` 分配策略候选(标「待用户报备批准」);旧 `num` 仅作参考。 +- **指令 4:** §7 给出显式 TypeScript 类型声明(四个 entry 接口 + 联合别名 + 顶层配置接口),字段与 `addGrid` / `addRowAnimate` / `addAutotile` / `addTileset` 一致,命名标「待用户报备批准」。 +- **只读与一致性:** 唯一写入为 `09-DATA-MIGRATION-AUDIT.md`(原地修订)与 `09-18-SUMMARY.md`(新增);零 `.ts`/`.tsx`、零 `public/project/**`、零 `src/content/**`、零 PNG;七项计数与既有正确内容保留;锚点全部有效;疑似问题只入 §8、未登记为缺陷。 +- 产出经 Task 0 获批;静态门禁(文档结构 / 标记 / 锚点 / 只读)全绿。 + + + +Create `.planning/phases/09-minimum-browser-runnable/09-18-SUMMARY.md` when done + + +## Artifacts this phase produces + +| 符号 / 路径 | 类型 | 说明 | +|---|---|---| +| `.planning/phases/09-minimum-browser-runnable/09-DATA-MIGRATION-AUDIT.md`(原地修订) | 只读盘点台账 | §0(`animates` 并入 `terrains` 强化)+ §1(`num` 可全部重排 + `id` 全清单 + 新 `num` 分配策略候选 + 字段分类)+ §2(`animates` 强化)+ §3/§4/§6(字段分类)+ §5(交叉引用)+ §7(显式 TypeScript 类型声明)+ §8(8.1 只记录 / 8.2 待定 / 8.3 其他)+ §9(计数与命名说明) | +| `.planning/phases/09-minimum-browser-runnable/09-18-SUMMARY.md`(新增) | 执行摘要 | 四条指示落实位置、命名候选清单(`num` 策略 + §7 类型名)、七项计数(不变)、只读确认、OI-1..OI-4 | + +**命名说明:** 本计划**不引入任何源码 / 数据命名**;修订中提出的所有候选命名(`num` 分配策略、`texture/.list.jsonc` 字段名与 TypeScript 类型名)**只作台账记录并标注「待用户报备批准」,不落地使用**。 + +## Source Coverage Audit + +| 来源 | 项 | 处置 | +|------|----|------| +| GOAL | Phase 9(ROADMAP)「在已完成的数据端之上完成渲染端框架,产出可在浏览器中运行并验证的最小版本」 | COVERED(增量·任务 8 后续子切片:按用户裁定收敛只读迁移台账) | +| REQ | Phase 9 ROADMAP「Requirements: TBD」,无需求 ID | N/A(`requirements: []` 如实标注) | +| RESEARCH | 无 RESEARCH.md(本阶段为增量派发,无独立研究产物) | N/A | +| CONTEXT | D-70(落点 `src/content`;消费接线由用户后续提供,本次不实现) | COVERED(§7 落点侧参考;prohibitions) | +| CONTEXT | D-71(六类迁移范围) | COVERED(修订只动六类对应的既有台账章节,不扩范围) | +| CONTEXT | D-72(五条合并 / 分图规则)+ D-72③(`animates` 并入 `terrains`) | COVERED(指令 1:§0 / §1.4 / §2 / §5 / §9 强化) | +| CONTEXT | D-73(命名须先报备;schema 反向设计) | COVERED(指令 4:§7 显式类型声明,命名标「待用户报备批准」) | +| CONTEXT | D-74(本次规划单元 = 只读盘点;产出 `09-DATA-MIGRATION-AUDIT.md`) | COVERED(原地修订该文档;只读守卫) | +| CONTEXT | D-75(素材迁移阶段由 AI 脚本生成 PNG;本盘点不生成) | COVERED(prohibitions + 只读守卫覆盖 `public/project`/`src/content`) | +| CONTEXT | D-03 / D-04 / D-06(执行前获批 / 命名获批 / 不扩范围) | COVERED(Task 0 + prohibitions) | +| 用户指示 | 指令 1(`animates` 合并至 `terrains`,确认 D-72③) | COVERED(§0 / §1.4 / §2 / §5 / §9;门禁 `不设独立`) | +| 用户指示 | 指令 2(严格界定「只记录、不移植」;§8 重构;§1/§3/§4/§6 标注) | COVERED(§1.2 / §3.1 / §4.2 / §6.2 分类列 + §8.1 / §8.2 / §8.3;门禁 `只记录、不移植` / `待定` / `### 8.1` / `### 8.2`) | +| 用户指示 | 指令 3(`num` 可全部重新排序;`id` 全清单;新 `num` 分配策略候选) | COVERED(§1 + §1.5;门禁 `可全部重新分配` / `id 全清单` / `num 分配策略候选`) | +| 用户指示 | 指令 4(§7 `texture/.list.jsonc` schema 显式 TypeScript 类型声明) | COVERED(§7 类型声明区块;门禁 `interface ` / `type ` / `ITextureListConfig` / `ITextureListGridEntry`) | +| 缺口 | 无(四条指示已逐条落到 Task 1 / Task 2 与门禁) | CLOSED | + diff --git a/.planning/phases/09-minimum-browser-runnable/09-18-SUMMARY.md b/.planning/phases/09-minimum-browser-runnable/09-18-SUMMARY.md new file mode 100644 index 0000000..8100e15 --- /dev/null +++ b/.planning/phases/09-minimum-browser-runnable/09-18-SUMMARY.md @@ -0,0 +1,149 @@ +--- +phase: 09-minimum-browser-runnable +plan: 18 +subsystem: docs +tags: [data-migration, audit, read-only, texture-list, schema, tile-num, animates, terrains] + +# Dependency graph +requires: + - phase: 09-minimum-browser-runnable + provides: "09-17 产出的只读盘点台账 09-DATA-MIGRATION-AUDIT.md(本次原地修订对象)" +provides: + - "按用户 2026-10-13 四条指示修订后的 09-DATA-MIGRATION-AUDIT.md(只读盘点台账,未实施迁移)" + - "四条指示的逐条落实位置、命名候选清单(num 分配策略 + texture/.list.jsonc 类型声明)、七项计数(不变)" +affects: [09-19 or later 旧引擎数据迁移计划, texture/.list.jsonc schema 落地] + +# Actuals (#2632) — chars/4 over the revised artifact + summary (docs-only plan; no source diff) +actuals: + tokens: 15000 + tasks: 2 + commits: 0 + +# Tech tracking +tech-stack: + added: [] + patterns: ["只读盘点台账的原地修订(增改不删既有正确内容)", "字段级三分标注:映射候选 / 待定 / 只记录不移植"] + +key-files: + created: + - .planning/phases/09-minimum-browser-runnable/09-18-SUMMARY.md + modified: + - .planning/phases/09-minimum-browser-runnable/09-DATA-MIGRATION-AUDIT.md + +key-decisions: + - "指令 1:animates 并入 terrains(素材分组),与逻辑类型映射 animates → TileType.Animate 显式区分" + - "指令 2:只记录、不移植 严格限定为目标接口无对应字段;§8 重构为 8.1/8.2/8.3" + - "指令 3:图块 num 可全部重排(唯一约束 id↔num 一致);新增 §1.5(id 全清单 + 4 个 num 分配策略候选)" + - "指令 4:§7.1 补显式 TypeScript 类型声明(四个 entry 接口 + 联合别名 + 顶层配置接口)" + - "autotile 条目 AutotileType 参数与判别字段 type 同名冲突,暂以候选名 autotileType 消歧(§8.3 U-19)" + +patterns-established: + - "字段分类三分法:映射候选(可确定) / 待定(有目标字段但映射不确定) / 只记录、不移植(目标接口无对应字段)" + +requirements-completed: [] + +coverage: + - id: D1 + description: "指令 1 —— animates 并入 terrains(强化到无歧义,并区分逻辑类型映射与素材分组)已落到 §0.5.1 / §1.1 / §1.4 / §2.1 / §2.4 / §2.5 / §5 / §9" + verification: + - kind: other + ref: "node gate REV-AUDIT-A / REV-AUDIT-B: token '不设独立' 落在 §0 且在 §2 及之后强化" + status: pass + human_judgment: true + rationale: "文档表述的『无歧义』程度需用户确认(OI-4)" + - id: D2 + description: "指令 2 —— 只记录、不移植 严格二分 + §1.2/§3.1/§4.2/§6.2 分类列 + §8 重构为 8.1/8.2/8.3" + verification: + - kind: other + ref: "node gate: '只记录、不移植' / '待定' / '### 8.1' / '### 8.2' / '### 8.3' 存在" + status: pass + human_judgment: true + rationale: "字段分类的判定依据(是否存在对应字段)需用户复核" + - id: D3 + description: "指令 3 —— §1 明确 num 可全部重排 + §1.5 id 全清单(235 条)+ 4 个 num 分配策略候选(待报备)" + verification: + - kind: other + ref: "node gate: '可全部重新分配' / 'id 全清单' / 'num 分配策略候选' 存在;§1.5.1 合计 235" + status: pass + human_judgment: true + rationale: "num 分配策略采用哪种待用户裁定(OI-1)" + - id: D4 + description: "指令 4 —— §7.1 显式 TypeScript 类型声明(四个 entry 接口 + 联合别名 + 顶层配置接口)" + verification: + - kind: other + ref: "node gate: 'interface ' / 'type ' / 'ITextureListConfig' / 'ITextureListGridEntry' 存在" + status: pass + human_judgment: true + rationale: "所有类型名 / 字段名为命名候选,待用户报备批准(OI-2)" + +# Metrics +duration: ~20min +completed: 2026-10-13 +status: complete +--- + +# Phase 9 Plan 18: 按用户四条指示修订旧引擎数据迁移只读盘点台账 Summary + +**按用户 2026-10-13 四条指示原地修订 `09-DATA-MIGRATION-AUDIT.md`:`animates` 并入 `terrains` 强化到无歧义、字段级三分标注并重构 §8 为 8.1/8.2/8.3、`num` 可全部重排并补 `id` 全清单与策略候选、§7 补显式 TypeScript 类型声明 —— 全程只读,零源码 / 零数据 / 零 PNG 改动。** + +## Performance + +- **Duration:** ~20 min +- **Started:** 2026-10-13T08:20Z(约) +- **Completed:** 2026-10-13T08:37Z +- **Tasks:** 2(Task 0 汇报关卡已由用户「可以执行」满足) +- **Files modified:** 1(`09-DATA-MIGRATION-AUDIT.md` 原地修订)+ 1 新增(本 SUMMARY) + +## Accomplishments + +- **指令 1(`animates` 并入 `terrains`)**:新增 §0.5.1 固定表述「**`animates` 并入 `terrains`,不设独立 `animates` 素材组**」,并明确区分**素材分组**(并入 `terrains`)与**逻辑类型映射**(§1.1 `animates` → `TileType.Animate` 保留);§1.1 / §1.4 / §2.1 / §2.4 / §2.5 / §5(交叉引用)/ §9 同步强化。 +- **指令 2(严格界定「只记录、不移植」)**:`ITileRawData` / `ITilePassData` / `IEnemyRawData` / `IItemRawData` / `IMapRawData` 为目标判定基准;§1.2 / §3.1 / §4.2 / §6.2 各表**新增「分类」列**逐字段标注三类;`trigger` / map `events` 等**有目标字段项标「待定」而非「只记录」**;§8 **重构**为 §8.1(无目标字段 → 只记录、不移植)/ §8.2(有目标字段但映射不确定 → 待定)/ §8.3(其他待确认观察),并与各表逐条一致。 +- **指令 3(`num` 可全部重排)**:§1 加入固定表述「图块 `num` 可全部重新分配,不沿用旧引擎顺序,唯一约束是 `id` ↔ `num` 一致」;新增 §1.5:**235 条 `id` 全清单**(按 `cls` 分组,合计 235)+ **4 个 `num` 分配策略候选**(A 按 `TileType` 分段 / B 沿用旧 `num` / C 按目标贴图分组 / D 按 `id` 次序),均「待用户报备批准」。 +- **指令 4(§7 显式类型声明)**:新增 §7.1,给出 `ITextureListGridEntry` / `ITextureListRowAnimateEntry` / `ITextureListAutotileEntry` / `ITextureListTilesetEntry` 四个 entry 接口、`TextureListEntry` 联合别名、`ITextureListConfig` 顶层配置接口,字段对齐 `addGrid` / `addRowAnimate` / `addAutotile` / `addTileset`;全部命名标注「待用户报备批准」。 +- **计数不变**:`TILE-COUNT: 235` / `MATERIAL-COUNT: 17` / `SPLIT-COUNT: 42` / `ENEMY-COUNT: 81` / `ITEM-COUNT: 62` / `NPC-COUNT: 24` / `MAP-COUNT: 5` 七项值均未变。 +- **只读确认**:仅写 `09-DATA-MIGRATION-AUDIT.md`(原地)与本 SUMMARY;未改任何 `.ts`/`.tsx`、`public/project/**`、`src/content/**`;未生成 `.png`;未改 09-17 计划 / 摘要、`ROADMAP.md`、`STATE.md`、`09-CONTEXT.md`。 + +## Task Commits + +本计划**未提交**(经用户指示:产出留在工作树供审阅,不得 `git add` / `git commit`)。 + +1. **Task 1(指令 1 §0 + 指令 2 §1 + 指令 3)** - 未提交(工作树内修订) +2. **Task 2(指令 1 §2/§5/§9 + 指令 2 §3/§4/§6 + §8 重构 + 指令 4 §7 + SUMMARY)** - 未提交(工作树内修订) + +_Note:仅本 SUMMARY 为新增文件;`09-DATA-MIGRATION-AUDIT.md` 为原地修订(09-17 产出,原为工作树未跟踪文件)。_ + +## Files Created/Modified + +- `.planning/phases/09-minimum-browser-runnable/09-DATA-MIGRATION-AUDIT.md` - 原地修订:§0.5.1(新增强化)、§1(`num` 重排说明)、§1.1(消歧)、§1.2(分类列)、§1.4(强化)、§1.5(新增)、§2.1/§2.4/§2.5(强化)、§3.1/§4.2/§6.2(分类列)、§5(交叉引用)、§7.1(新增显式 TS 类型声明)、§8(重构为 8.1/8.2/8.3)、§9(命名说明) +- `.planning/phases/09-minimum-browser-runnable/09-18-SUMMARY.md` - 本执行摘要(新增) + +## Decisions Made + +- **`animates` 归属**:明确「素材 / 分图分组并入 `terrains`」与「逻辑类型映射 `animates` → `TileType.Animate` 保留」为两件事(消歧,OI-4)。 +- **分类三分法**:以目标接口是否存在对应字段为唯一判定基准;`trigger`(`ITileRawData.events`/`eventPass`)、map `events`(`IMapRawData.events`)确认为「待定」而非「只记录」。 +- **`num` 策略**:只记录 4 个候选,不选型、不落地(OI-1)。 +- **autotile `type` 同名冲突**:§7.1 暂以候选字段名 `autotileType` 消歧,作为 §8.3 U-19 观察(不登记为缺陷),最终命名待用户裁定(OI-2)。 + +## Deviations from Plan + +None - 计划按用户四条指示执行。计划中允许的「仓库外只读基线」按 Task 1 `` 步骤 (0) 写入 `%TEMP%\opencode\gsd-09-18-baseline.txt`(基线为空 = 执行前无生产文件 / 数据改动);`` 幂等未覆写。 + +> 说明(非偏差):用户指示「§5 相关处」中,本文档 §5 为 NPC 节、与 `animates`/`terrains` 无关,故按计划 assumptions 以「交叉引用」方式落实(OI-3)。 + +## Issues Encountered + +None。 + +## User Setup Required + +None - no external service configuration required. + +## Next Phase Readiness + +- 修订后的 `09-DATA-MIGRATION-AUDIT.md` 已就位,供用户审阅后派发后续「旧引擎数据迁移计划」。 +- 待用户裁定 / 报备项:**OI-1**(`num` 分配策略)、**OI-2**(`texture/.list.jsonc` 类型 / 字段命名)、**OI-3**(§5 定位处理方式)、**OI-4**(`animates` 消歧方式);另有 §8.3 U-19(autotile `type` 同名冲突命名)。 +- 未提交:产出留在工作树,用户确认后再决定后续流程。 + +--- +*Phase: 09-minimum-browser-runnable* +*Completed: 2026-10-13* diff --git a/.planning/phases/09-minimum-browser-runnable/09-DATA-MIGRATION-AUDIT.md b/.planning/phases/09-minimum-browser-runnable/09-DATA-MIGRATION-AUDIT.md new file mode 100644 index 0000000..e63b1f6 --- /dev/null +++ b/.planning/phases/09-minimum-browser-runnable/09-DATA-MIGRATION-AUDIT.md @@ -0,0 +1,984 @@ +# Phase 9 · 任务 8:旧引擎数据迁移只读盘点台账(09-DATA-MIGRATION-AUDIT) + +> 本文件是 Phase 9 任务 8 的**只读盘点产物**(`09-17-PLAN.md`,D-74)。**全程只读**:未修改 / 删除 / 新增任何生产 `.ts` / `.tsx` 源码,未改动任何 `public/project/**` 与 `src/content/**` 数据文件,未生成任何 `.png` 素材(D-75)。本文件唯一用途是为后续「旧引擎数据迁移」计划提供可消费的映射台账。所有 `file:line` 锚点取自执行日实测,指向真实存在且行号在范围内的行;无法确定者一律收入 §8,不猜测编造。文档使用裸 LF 行尾(随仓库既有 phase 文档约定)。 + +## 0. 盘点口径与范围 + +### 0.1 盘点范围(D-71) + +本次只读盘点覆盖**六类**: + +1. **图块定义 tile** —— `public/project/maps.js` 的 `blocksInfo`(运行时名,见 §0.3)。 +2. **地图 maps** —— `public/project/floors/*.js`。 +3. **贴图素材 textures** —— 显式含 `public/project/materials/*.png` **+** `public/project/autotiles/*.png` **+** `public/project/tilesets/*.png`(2026-10-13 用户裁定,D-71 补充)。 +4. **怪物 enemy** —— `public/project/enemys.js`。 +5. **道具 items** —— `public/project/items.js`。 +6. **NPC** —— `maps.js` 的 `npcs` / `npc48` 图块。 + +**不在本次范围:** `animation` 素材(暂不处理,不盘点 / 不挪动 `public/project/animates/*.animate`);`images` / `sounds` / `bgms` / `fonts`。 + +### 0.2 落点与接线边界(D-70) + +迁移目标落点为 `src/content`(jsonc 数据 + 素材文件)。把配置写入 store、把贴图列表写入 `TextureManager` 的**消费接线由用户后续单独提供,本次不实现**。本盘点对应参考的落点侧文件:`src/content/tile.jsonc:1`、`src/content/enemy.jsonc:1`、`src/content/item.jsonc:1`、`src/content/maps/MT0.jsonc:1`、`src/content/texture/.list.jsonc:1`、`src/content/client.jsonc:9`(`textureListDir: texture/.list.jsonc`)、`src/content/defaults/tile.jsonc:1`、`src/content/defaults/enemy.jsonc:1`、`src/content/defaults/item.jsonc:1`、`src/content/defaults/map.jsonc:1`、`src/content/defaults/texture.jsonc:1`。 + +### 0.3 名词说明(`blocksInfo`) + +`blocksInfo` 是**运行时名称**(`core.maps.blocksInfo`),**并非** `public/project/maps.js` 中的字面键名。该文件的字面形态为顶层 `var maps_ = { "1": {…}, … }` 的图块定义对象(`public/project/maps.js:1` 为 `var maps_90f36752_8815_4be8_b32b_d7fad1d0542e =`,`:3` 为第一个图块条目 `"1": {"cls":"animates",…}`)。本文档凡称 `blocksInfo` 一律指代该顶层对象,**检索时请勿按字面串 `blocksInfo` 在文件中匹配**。同理,`enemys.js` 顶层为 `var enemys_`(`public/project/enemys.js:1`),`items.js` 顶层为 `var items_`(`public/project/items.js:1`),`floors/*.js` 顶层为 `main.floors. = {…}`(`public/project/floors/MT0.js:1`)。 + +### 0.4 扫描方法论 + +- 用 `node` 在 `vm` 沙箱中只读求值 `public/project` 的 JS 数据文件,逐项实测枚举其键与字段,不臆造。 +- **PNG 像素尺寸**用 `node` 读取 PNG `IHDR`(宽 = 偏移 16 起 4 字节大端,高 = 偏移 20 起 4 字节大端)获得,无需任何依赖。 +- 映射目标接口以 `packages-user/data-common/src/store/types.ts` 与 `packages-user/client-base/src/material/types.ts` 的既有定义为锚点。 +- 所有计数为执行日实测;行号以执行日实测为准。 + +### 0.5 D-72 五条拆分规则原文(含 tilesets / autotiles 例外) + +> **D-72(合并 / 分图规则):** ① `npc` 与 `npc48`、`enemy` 与 `enemy48` 均**合并**(不再有 48 变体);② `terrains` / `items` / `enemy` / `npc` **分别存储**,不得混入同一张图片;③ `animates` **合并入 `terrains`**;④ **不同帧数**的素材不得放入同一张图片;⑤ 单帧素材按 **4×4** 规格拆分,多帧素材按「**帧数列 × 4 行**」拆分。 + +**例外(不套用 D-72⑤):** + +- `public/project/tilesets/*.png` → 走 `addTileset`(`source` / `identifier` / `cellWidth` / `cellHeight`,见 `packages-user/client-base/src/material/types.ts:302`),**不**做 4×4 / 帧数列×4 行拆分。 +- `public/project/autotiles/*.png` → 走 `addAutotile`(`source` / `identifier` / `type: AutotileType`,见 `packages-user/client-base/src/material/types.ts:289`;`AutotileType` 定义于同文件 `:12`,含 2×3 `Small2x3` 与 3×4 `Big3x4`),**不**做 4×4 / 帧数列×4 行拆分。 + +#### 0.5.1 指令落地强化(D-72③:`animates` 并入 `terrains`) + +> **固定表述:** **`animates` 并入 `terrains`,不设独立 `animates` 素材组。** + +上句特指**素材 / 分图分组**层面的合并 —— `animates` 各图块的贴图与 `terrain` 归入**同一** `terrains` 素材组(受规则④约束:`animates` 为多帧,与单帧 `terrains` 分开成图,见 §2.4)。 + +**不得**与**逻辑类型映射**混淆:§1.1 中旧 `cls: animates` → `TileType.Animate` 的映射**保留不变**(`TileType` 枚举中 `Terrain` 与 `Animate` 并存,见 `packages-user/data-common/src/store/types.ts:6`)。即「逻辑类型仍区分 `Terrain` / `Animate`」与「素材 / 分图分组并入 `terrains`」是**两件独立的事**(消歧见 §1.1 与 OI-4)。 + +### 0.6 命名待报备说明(D-73) + +本盘点中提出的任何新增 / 修改命名(拆分后贴图文件名、`addTileset` / `addAutotile` 的 `identifier` 与参数名、`texture/.list.jsonc` 字段名)**只作为「命名候选」写入台账并逐条标注「待用户报备批准」**,**不落地使用**、**不写入任何 `src/content/**` 文件**(AGENTS.md:公共 / 受保护 / 私有方法、字段命名须先报备获批)。 + +### 0.7 只读声明(D-74 / D-75) + +本文件是本次任务**唯一产物之一**(另一为 `09-17-SUMMARY.md`)。全程未改动任何 `.ts` / `.tsx`、任何 `public/project/**` 与 `src/content/**` 数据文件、任何既有注释或任何既有规划文件;未生成任何 `.png`。根据 D-75,迁移阶段的 PNG 素材将由后续脚本生成,**本盘点阶段不生成任何素材**。 + +## 1. 图块定义 tile + +数据来源:`public/project/maps.js:3` 起的顶层 `var maps_ = { "num": {…} }` 对象(运行时名 `blocksInfo`)。实测共 **235** 个图块定义。 + +**`num` 可全部重新分配(指令 3):** **图块 `num` 可全部重新分配,不沿用旧引擎顺序,唯一约束是 `id` ↔ `num` 一致**(即 `addTile` 的 `num` 与 `id` 均不冲突即可,见 `packages-user/data-common/src/store/types.ts:88`)。§1.3 的旧 `num` 列**仅作参考对照**;新 `num` 分配策略候选见 §1.5,均标注「待用户报备批准」。 + +### 1.1 `cls` → `TileType` 映射候选 + +目标枚举 `TileType`(`packages-user/data-common/src/store/types.ts:6`):`Unknown` / `None` / `Terrain` / `Animate` / `Item` / `Enemy` / `Npc` / `Autotile` / `Tileset`。 + +| 旧 `cls` | 实测条数 | 目标 `TileType`(候选) | 依据 | +| --- | --- | --- | --- | +| `animates` | 29 | `Animate` | 动画类图块(`public/project/maps.js:3` 首项 `cls:"animates"`) | +| `terrains` | 35 | `Terrain` | 地形类图块 | +| `autotile` | 4 | `Autotile` | 自动元件(`public/project/maps.js:19` `"20": {"cls":"autotile","id":"autotile"}`) | +| `items` | 62 | `Item` | 道具类图块(`public/project/maps.js:227` `"331": {"cls":"items","id":"I331"}`) | +| `enemys` | 73 | `Enemy` | 怪物类图块(`public/project/maps.js:121` `"201": {"cls":"enemys","id":"greenSlime"}`) | +| `enemy48` | 8 | `Enemy`(合并,D-72①) | 高 48px 怪物图块(`public/project/maps.js:181` `"261": {"cls":"enemy48","id":"angel"}`) | +| `npcs` | 14 | `Npc` | NPC 图块(`public/project/maps.js:90` `"121": {"cls":"npcs","id":"man"}`) | +| `npc48` | 10 | `Npc`(合并,D-72①) | 高 48px NPC 图块(`public/project/maps.js:102` `"133": {"cls":"npc48","id":"npc0",…}`) | + +合计 29 + 35 + 4 + 62 + 73 + 8 + 14 + 10 = **235**。`cls` 实测集合 = `animates, terrains, autotile, items, npcs, npc48, enemys, enemy48`(8 种);无 `None` / `Tileset` 图块定义。 + +> **消歧(指令 1 / OI-4):** 本表是**逻辑类型映射**(`cls` → `TileType`)—— `animates` → `TileType.Animate` 与 `terrains` → `TileType.Terrain` **各自保留**。这与 §0.5.1 / §2.4 的**素材 / 分图分组**合并(`animates` 并入 `terrains`,D-72③)**不是同一件事**:前者管「图块逻辑类型」,后者管「贴图分图归属」。 + +### 1.2 字段映射候选 → `ITileRawData` + +目标接口 `ITileRawData`(`packages-user/data-common/src/store/types.ts:47`):`num: number` / `id: string` / `events: Record` / `type: TileType` / `pass: ITilePassData` / `eventPass: boolean`。通行性子对象 `ITilePassData`(同文件 `:38`):`onlyEvents: boolean` / `outPass: number` / `inPass: number`。 + +旧图块字段实测频次:`cls`(235)、`id`(235)、`canBreak`(4)、`animate`(21)、`doorInfo`(17)、`name`(32)、`canPass`(19)、`trigger`(20)、`script`(5)、`faceIds`(4)、`autotileConnection`(2)、`cannotOut`(18)、`cannotIn`(18)。 + +**字段分类(指令 2,判定基准 = 目标接口是否存在对应字段):** + +- **映射候选(可确定)**:目标接口有对应字段,且映射关系可直接确定。 +- **待定(有目标字段但映射不确定)**:目标接口有对应字段,但取值 / 展开关系无法由静态解析唯一确定(汇总见 §8.2)。 +- **只记录、不移植(目标接口无对应字段)**:`ITileRawData` / `ITilePassData` 中**不存在**对应字段,旧字段只作盘点记录,不进入迁移(汇总见 §8.1)。 + +| 旧字段 | 实测条数 | 目标落点(候选) | 分类 | 说明 | +| --- | --- | --- | --- | --- | +| `id` | 235 | `ITileRawData.id` | 映射候选(可确定) | 直接对应图块字符串 id | +| `cls` | 235 | `ITileRawData.type` | 映射候选(可确定) | 经 §1.1 映射为 `TileType` | +| `trigger` | 20 | `ITileRawData.events`(事件 id)/ `eventPass` | 待定(有目标字段但映射不确定) | 旧触发名(`null` / `openDoor` / `ski` / `pushBox`);**确有对应字段**,仅事件 id 与事件可通行性的具体对应未定(§8.2 U-2) | +| `canPass` | 19 | `ITilePassData`(`inPass` / `outPass` 位运算) | 待定(有目标字段但映射不确定) | 旧布尔「可通行」→ 新四方向通行位(`PassBit`,`packages-user/data-common/src/store/types.ts:27`);**确有对应字段**,仅具体方向展开未定(§8.2 U-3) | +| `cannotOut` / `cannotIn` | 18 / 18 | `ITilePassData.outPass` / `inPass` | 映射候选(可确定) | 旧方向数组(`up` / `down` / `left` / `right`)→ 方向位掩码(可确定取反:`outPass = 全方向位 & ~cannotOut`) | +| `doorInfo` | 17 | 无(目标接口无对应字段) | 只记录、不移植(目标接口无对应字段) | 含 `time` / `openSound` / `closeSound` / `keys`;`ITileRawData` / `ITilePassData` 无字段承载门机制参数(§8.1 U-4) | +| `animate` | 21 | 无(仅用于 §2 素材帧数推导) | 只记录、不移植(目标接口无对应字段) | 旧值多为 `1`;`ITileRawData` 无帧数字段,帧数由素材(`addRowAnimate`)承载(§8.1 U-4) | +| `canBreak` | 4 | 无(目标接口无对应字段) | 只记录、不移植(目标接口无对应字段) | 出现在 `animates` 墙类(`yellowWall` 等,`public/project/maps.js:3`);无字段承载「可破坏」语义(§8.1 U-4) | +| `script` | 5 | 无(目标接口无对应字段) | 只记录、不移植(目标接口无对应字段) | 旧内联脚本(如 `lavaNet` / `light` / 血网等);`events` 仅存事件 id,不承载脚本正文(§8.1 U-4) | +| `faceIds` | 4 | 无(目标接口无对应字段) | 只记录、不移植(目标接口无对应字段) | 仅 `npc48` 的 `npc0..npc3`(`public/project/maps.js:102`)与敌人 `bear` 四面;无字段承载多面机制(§8.1 U-5) | +| `autotileConnection` | 2 | 无(目标接口无对应字段;对应 `IAutotileProcessor.setConnection`,非 `ITileRawData` 字段) | 只记录、不移植(目标接口无对应字段) | 仅 `autotile1` / `autotile3`(`public/project/maps.js:108`,值 `["yellowDoor"]`)(§8.1 U-5) | +| `name` | 32 | 无(目标接口无对应字段) | 只记录、不移植(目标接口无对应字段) | 旧显示名(如 `血网` / `黄门` / `薄墙-上`);`ITileRawData` 无对应字段(§8.1 U-5) | + +### 1.3 `num` / `id` 全清单(235 条) + +| num | id | cls | 关键字段 | +| --- | --- | --- | --- | +| 1 | yellowWall | animates | canBreak, animate, doorInfo | +| 2 | whiteWall | animates | canBreak, animate, doorInfo | +| 3 | blueWall | animates | canBreak, animate, doorInfo | +| 4 | star | animates | name | +| 5 | lava | animates | name | +| 6 | ice | animates | doorInfo, animate | +| 7 | blueShopLeft | terrains | - | +| 8 | blueShopRight | terrains | - | +| 9 | pinkShopLeft | terrains | - | +| 10 | pinkShopRight | terrains | - | +| 11 | lavaNet | animates | canPass, trigger, script, name | +| 12 | poisonNet | animates | canPass, trigger, script, name | +| 13 | weakNet | animates | canPass, trigger, script, name | +| 14 | curseNet | animates | canPass, trigger, script, name | +| 15 | blueLava | animates | - | +| 16 | water | animates | - | +| 20 | autotile | autotile | - | +| 21 | yellowKey | items | - | +| 22 | blueKey | items | - | +| 23 | redKey | items | - | +| 24 | greenKey | items | - | +| 25 | steelKey | items | - | +| 26 | bigKey | items | - | +| 27 | redGem | items | - | +| 28 | blueGem | items | - | +| 29 | greenGem | items | - | +| 30 | yellowGem | items | - | +| 31 | redPotion | items | - | +| 32 | bluePotion | items | - | +| 33 | greenPotion | items | - | +| 34 | yellowPotion | items | - | +| 35 | sword1 | items | - | +| 36 | shield1 | items | - | +| 37 | sword2 | items | - | +| 38 | shield2 | items | - | +| 39 | sword3 | items | - | +| 40 | shield3 | items | - | +| 41 | sword4 | items | - | +| 42 | shield4 | items | - | +| 43 | sword5 | items | - | +| 44 | shield5 | items | - | +| 45 | book | items | - | +| 46 | fly | items | - | +| 47 | pickaxe | items | - | +| 48 | icePickaxe | items | - | +| 49 | bomb | items | - | +| 50 | centerFly | items | - | +| 51 | upFly | items | - | +| 52 | downFly | items | - | +| 53 | coin | items | - | +| 54 | freezeBadge | items | - | +| 55 | cross | items | - | +| 56 | superPotion | items | - | +| 57 | earthquake | items | - | +| 58 | poisonWine | items | - | +| 59 | weakWine | items | - | +| 60 | curseWine | items | - | +| 61 | superWine | items | - | +| 62 | dagger | items | - | +| 63 | pack | items | - | +| 64 | amulet | items | - | +| 65 | hammer | items | - | +| 68 | lifeWand | items | - | +| 69 | jumpShoes | items | - | +| 70 | sword0 | items | - | +| 71 | shield0 | items | - | +| 72 | skill1 | items | - | +| 73 | wand | items | - | +| 81 | yellowDoor | animates | trigger, animate, doorInfo, name | +| 82 | blueDoor | animates | trigger, animate, doorInfo, name | +| 83 | redDoor | animates | trigger, animate, doorInfo, name | +| 84 | greenDoor | animates | trigger, animate, doorInfo, name | +| 85 | specialDoor | animates | trigger, animate, doorInfo, name | +| 86 | steelDoor | animates | trigger, animate, doorInfo, name | +| 87 | upFloor | terrains | canPass | +| 88 | downFloor | terrains | canPass | +| 89 | portal | animates | canPass | +| 90 | starPortal | animates | canPass | +| 91 | upPortal | animates | canPass | +| 92 | leftPortal | animates | canPass | +| 93 | downPortal | animates | canPass | +| 94 | rightPortal | animates | canPass | +| 101 | crystalUp | animates | - | +| 102 | crystalBottom | animates | - | +| 103 | fire | animates | - | +| 104 | switch | animates | - | +| 109 | magentaWall | animates | canBreak, animate, doorInfo | +| 121 | man | npcs | - | +| 122 | trader | npcs | - | +| 123 | thief | npcs | - | +| 124 | fairy | npcs | - | +| 125 | wizard | npcs | - | +| 126 | recluse | npcs | - | +| 127 | king | npcs | - | +| 128 | youngMan | npcs | - | +| 129 | sign | npcs | - | +| 130 | expShop | npcs | - | +| 131 | moneyShop | npcs | - | +| 132 | princess | npcs | - | +| 133 | npc0 | npc48 | faceIds, animate | +| 134 | npc1 | npc48 | faceIds, animate | +| 135 | npc2 | npc48 | faceIds, animate | +| 136 | npc3 | npc48 | faceIds, animate | +| 137 | greenMan | npcs | - | +| 138 | blueTrader | npcs | - | +| 151 | autotile1 | autotile | autotileConnection | +| 152 | autotile2 | autotile | - | +| 153 | autotile3 | autotile | autotileConnection | +| 161 | arrowUp | terrains | canPass, cannotOut, cannotIn | +| 162 | arrowDown | terrains | canPass, cannotOut, cannotIn | +| 163 | arrowLeft | terrains | canPass, cannotOut, cannotIn | +| 164 | arrowRight | terrains | canPass, cannotOut, cannotIn | +| 165 | light | terrains | trigger, canPass, script | +| 166 | darkLight | terrains | - | +| 167 | ski | terrains | trigger, canPass | +| 168 | flower | terrains | canPass | +| 169 | box | terrains | trigger | +| 170 | boxed | terrains | trigger | +| 201 | greenSlime | enemys | - | +| 202 | redSlime | enemys | - | +| 203 | blackSlime | enemys | - | +| 204 | slimelord | enemys | - | +| 205 | bat | enemys | - | +| 206 | bigBat | enemys | - | +| 207 | redBat | enemys | - | +| 208 | vampire | enemys | - | +| 209 | skeleton | enemys | - | +| 210 | skeletonWarrior | enemys | - | +| 211 | skeletonCaptain | enemys | - | +| 212 | ghostSoldier | enemys | - | +| 213 | zombie | enemys | - | +| 214 | zombieKnight | enemys | - | +| 215 | rock | enemys | - | +| 216 | slimeman | enemys | - | +| 217 | bluePriest | enemys | - | +| 218 | redPriest | enemys | - | +| 219 | brownWizard | enemys | - | +| 220 | redWizard | enemys | - | +| 221 | yellowGateKeeper | enemys | - | +| 222 | blueGateKeeper | enemys | - | +| 223 | redGateKeeper | enemys | - | +| 224 | swordsman | enemys | - | +| 225 | soldier | enemys | - | +| 226 | yellowKnight | enemys | - | +| 227 | redKnight | enemys | - | +| 228 | darkKnight | enemys | - | +| 229 | blackKing | enemys | - | +| 230 | yellowKing | enemys | - | +| 231 | greenKing | enemys | - | +| 232 | blueKnight | enemys | - | +| 233 | goldSlime | enemys | - | +| 234 | poisonSkeleton | enemys | - | +| 235 | poisonBat | enemys | - | +| 236 | ironRock | enemys | - | +| 237 | skeletonPriest | enemys | - | +| 238 | skeletonKing | enemys | - | +| 239 | skeletonPresbyter | enemys | - | +| 240 | skeletonKnight | enemys | - | +| 241 | evilHero | enemys | - | +| 242 | devilWarrior | enemys | - | +| 243 | demonPriest | enemys | - | +| 244 | goldHornSlime | enemys | - | +| 245 | redKing | enemys | - | +| 246 | blueKing | enemys | - | +| 247 | magicMaster | enemys | - | +| 248 | silverSlime | enemys | - | +| 249 | blademaster | enemys | - | +| 250 | whiteHornSlime | enemys | - | +| 251 | evilPrincess | enemys | - | +| 252 | evilFairy | enemys | - | +| 253 | yellowPriest | enemys | - | +| 254 | redSwordsman | enemys | - | +| 255 | whiteSlimeman | enemys | - | +| 256 | poisonZombie | enemys | - | +| 257 | dragon | enemys | - | +| 258 | octopus | enemys | - | +| 259 | fairyEnemy | enemys | - | +| 260 | princessEnemy | enemys | - | +| 261 | angel | enemy48 | - | +| 262 | elemental | enemy48 | - | +| 263 | steelGuard | enemy48 | - | +| 264 | evilBat | enemy48 | - | +| 265 | silverSlimelord | enemys | - | +| 266 | goldSlimelord | enemys | - | +| 267 | grayRock | enemys | - | +| 270 | greenKnight | enemys | - | +| 271 | bowman | enemys | - | +| 272 | purpleBowman | enemys | - | +| 275 | watcherSlime | enemys | - | +| 277 | frostBat | enemys | - | +| 278 | devilKnight | enemys | - | +| 279 | grayPriest | enemys | - | +| 280 | greenGateKeeper | enemys | - | +| 300 | ground | terrains | - | +| 301 | sWallT | terrains | name, cannotOut, cannotIn | +| 302 | sWallL | terrains | name, cannotOut, cannotIn | +| 303 | sWallR | terrains | name, cannotOut, cannotIn | +| 304 | sWallB | terrains | name, cannotOut, cannotIn | +| 305 | grass | terrains | - | +| 306 | sWallTL | terrains | name, cannotOut, cannotIn | +| 307 | sWallBR | terrains | name, cannotOut, cannotIn | +| 308 | grass2 | terrains | - | +| 309 | sWallTR | terrains | name, cannotOut, cannotIn | +| 310 | sWallBL | terrains | name, cannotOut, cannotIn | +| 311 | ground2 | terrains | - | +| 312 | sWallTB | terrains | name, cannotOut, cannotIn | +| 313 | ground3 | terrains | - | +| 314 | sWallLR | terrains | name, cannotOut, cannotIn | +| 315 | sWallBLR | terrains | name, cannotOut, cannotIn | +| 316 | sWallTLR | terrains | name, cannotOut, cannotIn | +| 317 | sWallTBR | terrains | name, cannotOut, cannotIn | +| 318 | sWallTBL | terrains | name, cannotOut, cannotIn | +| 319 | tallYellowDoor | npc48 | trigger, name, animate, doorInfo | +| 320 | tallBlueDoor | npc48 | trigger, name, animate, doorInfo | +| 321 | tallRedDoor | npc48 | trigger, name, animate, doorInfo | +| 322 | tallGreenDoor | npc48 | trigger, name, animate, doorInfo | +| 323 | tallSpecialDoor | npc48 | trigger, name, animate, doorInfo | +| 324 | tallSteelDoor | npc48 | trigger, name, animate, doorInfo | +| 325 | keiskeiFairy | enemys | - | +| 326 | tulipFairy | enemys | - | +| 327 | bearDown | enemy48 | - | +| 328 | bearLeft | enemy48 | - | +| 329 | bearRight | enemy48 | - | +| 330 | bearUp | enemy48 | - | +| 331 | I331 | items | - | +| 332 | I332 | items | - | +| 333 | I333 | items | - | +| 334 | I334 | items | - | +| 335 | I335 | items | - | +| 336 | I336 | items | - | +| 337 | I337 | items | - | +| 338 | I338 | items | - | +| 339 | I339 | items | - | +| 340 | I340 | items | - | +| 341 | I341 | items | - | + +**TILE-COUNT: 235** + +### 1.4 图块 → 贴图归属候选 + +按 D-72①③与 §2 的目标拆分,图块 id 归属候选如下(贴图文件名均为 §2.5 的命名候选,**待用户报备批准**): + +| `cls` | 图块 num 段 | 目标贴图归属候选 | +| --- | --- | --- | +| `terrains` | 7–10, 87–88, 161–170, 300–318 | `terrains` 组(单帧) | +| `animates` | 1–6, 11–16, 81–86, 89–94, 101–104, 109 | `terrains` 组(多帧)—— **`animates` 并入 `terrains`,不设独立 `animates` 素材组**(D-72③) | +| `items` | 21–73, 331–341 | `items` 组 | +| `enemys` + `enemy48` | 201–280, 325–330 | `enemy` 组(合并,D-72①) | +| `npcs` + `npc48` | 121–138, 133–136, 319–324 | `npc` 组(合并,D-72①) | +| `autotile` | 20, 151–153 | 对应 §2.2 的 autotile 素材(例外,非材料拆分) | + +> **固定表述(指令 1):** **`animates` 并入 `terrains`,不设独立 `animates` 素材组**(D-72③,与 §0.5.1 一致)。此为**贴图归属 / 素材分组**层面的合并(受规则④:多帧 `animates` 与单帧 `terrains` 分开成图);**不影响** §1.1 的**逻辑类型映射**(`animates` → `TileType.Animate` 仍保留)。 + +### 1.5 id 全清单与新 num 分配策略候选 + +本节汇总 **id 全清单**(235 条,复用 §1.3 的 `id` 列,按 `cls` 分组,**完整覆盖、不遗漏**)与 **num 分配策略候选**(≥3 个,均标注「待用户报备批准」)。**本盘点只记录候选,不落地任何 `num` 分配**(D-73 / OI-1)。 + +#### 1.5.1 id 全清单(235 条,按 `cls` 分组) + +| `cls` | 条数 | 图块 id 全清单 | +| --- | --- | --- | +| `animates` | 29 | yellowWall, whiteWall, blueWall, star, lava, ice, lavaNet, poisonNet, weakNet, curseNet, blueLava, water, yellowDoor, blueDoor, redDoor, greenDoor, specialDoor, steelDoor, portal, starPortal, upPortal, leftPortal, downPortal, rightPortal, crystalUp, crystalBottom, fire, switch, magentaWall | +| `terrains` | 35 | blueShopLeft, blueShopRight, pinkShopLeft, pinkShopRight, upFloor, downFloor, arrowUp, arrowDown, arrowLeft, arrowRight, light, darkLight, ski, flower, box, boxed, ground, sWallT, sWallL, sWallR, sWallB, grass, sWallTL, sWallBR, grass2, sWallTR, sWallBL, ground2, sWallTB, ground3, sWallLR, sWallBLR, sWallTLR, sWallTBR, sWallTBL | +| `autotile` | 4 | autotile, autotile1, autotile2, autotile3 | +| `items` | 62 | yellowKey, blueKey, redKey, greenKey, steelKey, bigKey, redGem, blueGem, greenGem, yellowGem, redPotion, bluePotion, greenPotion, yellowPotion, sword1, shield1, sword2, shield2, sword3, shield3, sword4, shield4, sword5, shield5, book, fly, pickaxe, icePickaxe, bomb, centerFly, upFly, downFly, coin, freezeBadge, cross, superPotion, earthquake, poisonWine, weakWine, curseWine, superWine, dagger, pack, amulet, hammer, lifeWand, jumpShoes, sword0, shield0, skill1, wand, I331, I332, I333, I334, I335, I336, I337, I338, I339, I340, I341 | +| `enemys` | 73 | greenSlime, redSlime, blackSlime, slimelord, bat, bigBat, redBat, vampire, skeleton, skeletonWarrior, skeletonCaptain, ghostSoldier, zombie, zombieKnight, rock, slimeman, bluePriest, redPriest, brownWizard, redWizard, yellowGateKeeper, blueGateKeeper, redGateKeeper, swordsman, soldier, yellowKnight, redKnight, darkKnight, blackKing, yellowKing, greenKing, blueKnight, goldSlime, poisonSkeleton, poisonBat, ironRock, skeletonPriest, skeletonKing, skeletonPresbyter, skeletonKnight, evilHero, devilWarrior, demonPriest, goldHornSlime, redKing, blueKing, magicMaster, silverSlime, blademaster, whiteHornSlime, evilPrincess, evilFairy, yellowPriest, redSwordsman, whiteSlimeman, poisonZombie, dragon, octopus, fairyEnemy, princessEnemy, silverSlimelord, goldSlimelord, grayRock, greenKnight, bowman, purpleBowman, watcherSlime, frostBat, devilKnight, grayPriest, greenGateKeeper, keiskeiFairy, tulipFairy | +| `enemy48` | 8 | angel, elemental, steelGuard, evilBat, bearDown, bearLeft, bearRight, bearUp | +| `npcs` | 14 | man, trader, thief, fairy, wizard, recluse, king, youngMan, sign, expShop, moneyShop, princess, greenMan, blueTrader | +| `npc48` | 10 | npc0, npc1, npc2, npc3, tallYellowDoor, tallBlueDoor, tallRedDoor, tallGreenDoor, tallSpecialDoor, tallSteelDoor | +| **合计** | **235** | —— | + +> 分组条数校验:29 + 35 + 4 + 62 + 73 + 8 + 14 + 10 = **235**(与 §1.1 / §1.3 一致)。以上 id 与 §1.3 的 `id` 列**逐条一致**(§1.3 含旧 `num`,本节只列 `id`,便于重排 `num` 时按 `id` 唯一对应)。 + +#### 1.5.2 新 num 分配策略候选(均「待用户报备批准」) + +以下策略**只作候选记录**,用于后续「迁移计划」确定 `num` 分配口径;**本盘点不选型、不落地**(OI-1)。 + +| 策略(候选) | 策略要点 | 优点 | 缺点 | 与旧 `num` 的对照 | +| --- | --- | --- | --- | --- | +| **A. 按 `TileType` 分段** | 按逻辑类型分成连续段:`Terrain` / `Animate` / `Item` / `Enemy` / `Npc` / `Autotile` / `Tileset` 各占一段,段内按 `id` 或旧 `num` 排序 | 段位语义清晰,便于按类型批量分配 / 查错 | 需整体重排,旧 `num` 全部失效;跨类共享 `id` 的图(如 door 类)归属需先定 | 完全打乱旧 `num`,仅以 `id` 对应 | +| **B. 沿用旧 `num`** | 保持 §1.3 的 `num` 不变,仅校验 `id` ↔ `num` 唯一一致 | 迁移最省,对照成本最低,旧资料回查方便 | 未利用「可全部重排」的灵活性;`enemy48`/`npc48` 合并后旧 `num` 段可能零散、留空洞 | 与旧 `num` 完全一致 | +| **C. 按目标贴图(拆分后图)分组顺序重排** | 以 §2.4 / §2.5 的目标图(A..E 组及拆分序)顺序为纲,同图内图块 `num` 连续分配 | 与素材拆分结果对齐,`num` 局部性贴近贴图,利于按图缓存 / 定位 | 依赖 §2.4 拆分方案最终定稿;拆分调整会牵动 `num` | 与旧 `num` 无关,按目标图顺序重排 | +| **D. 按 `id` 次序分配** | 对 §1.5.1 的 id 全清单按 `id` 字典序(或分组后组内字典序)依次赋 `num` | 规则最简单、可完全脚本化、无歧义 | `num` 与语义 / 贴图均无关,调试可读性最差 | 与旧 `num` 无关,按 `id` 次序重排 | + +> 以上任一策略均满足唯一硬约束:**`id` ↔ `num` 一致**(`addTile` 的 `num` 与 `id` 均不冲突,见 `packages-user/data-common/src/store/types.ts:88`)。候选取用哪一种**待用户报备批准 / 裁定**(OI-1)。 + +## 2. 贴图素材 textures + +本维度显式含三类素材:`materials/*.png`、`autotiles/*.png`(例外)、`tilesets/*.png`(例外)。 + +### 2.1 `materials/*.png` 登记 + +像素尺寸由 `node` 读取 PNG `IHDR` 实测(锚点指向对应 PNG 文件,`:1` 表示该文件)。 + +| 素材 | 像素尺寸 | 承载图块类(候选) | +| --- | --- | --- | +| `public/project/materials/terrains.png:1` | 32×1120 | `terrains`(35 单帧图块,1 列 × 35 行) | +| `public/project/materials/items.png:1` | 32×1984 | `items`(62 单帧图块,1 列 × 62 行) | +| `public/project/materials/enemys.png:1` | 64×2336 | `enemys`(73 怪物,2 列 × 73 行,每怪 2 帧) | +| `public/project/materials/enemy48.png:1` | 128×384 | `enemy48`(8 怪物,2 列 × 8 行,每怪 2 帧) | +| `public/project/materials/npcs.png:1` | 64×448 | `npcs`(14 NPC,2 列 × 14 行,每 NPC 2 帧) | +| `public/project/materials/npc48.png:1` | 128×480 | `npc48`(10 NPC,2 列 × 10 行,每 NPC 2 帧) | +| `public/project/materials/animates.png:1` | 128×928 | `animates`(29 动画图块,4 帧列 × 29 行)—— **素材分组并入 `terrains`,不设独立 `animates` 素材组**(D-72③,见 §0.5.1) | +| `public/project/materials/ground.png:1` | 32×32 | 单帧素材(归类**待确认**,§8) | +| `public/project/materials/airwall.png:1` | 32×32 | 单帧素材(归类**待确认**,§8) | +| `public/project/materials/keyboard.png:1` | 416×416 | 13×13 单帧网格(用途归类**待确认**,§8) | +| `public/project/materials/icons.png:1` | 32×1120 | 1 列 × 35 行(用途归类**待确认**,§8) | +| `public/project/materials/icons_old.png:1` | 32×1120 | 1 列 × 35 行(用途归类**待确认**,§8) | + +**MATERIAL-COUNT: 17**(= 12 materials + 4 autotiles + 1 tileset) + +### 2.2 `autotiles/*.png` 登记(例外,不套用 D-72⑤) + +走 `addAutotile(source, identifier, type)`(`packages-user/client-base/src/material/types.ts:289`);`type` 取 `AutotileType`(同文件 `:12`):2×3 = `Small2x3`、3×4 = `Big3x4`。 + +| 素材 | 像素尺寸 | `type` 候选 | `identifier` 候选 | 判定依据 | +| --- | --- | --- | --- | --- | +| `public/project/autotiles/autotile.png:1` | 96×128 | `Big3x4` | 20 | 96/32 = 3 列、128/32 = 4 行 → 3×4 | +| `public/project/autotiles/autotile2.png:1` | 96×128 | `Big3x4` | 152 | 同上 | +| `public/project/autotiles/autotile3.png:1` | 96×128 | `Big3x4` | 153 | 同上 | +| `public/project/autotiles/autotile1.png:1` | 384×128 | **待确认**(§8) | 151 | 384/32 = 12;无法由尺寸唯一判定 2×3 / 3×4 | + +`identifier` 候选值取自 §1 中 `autotile` 类图块的 num(20 / 151 / 152 / 153,见 `public/project/maps.js:19`、`public/project/maps.js:108`)。**明确:`autotiles/*.png` 不套用 D-72⑤ 的 4×4 / 帧数列×4 行拆分。** + +### 2.3 `tilesets/*.png` 登记(例外,不套用 D-72⑤) + +走 `addTileset(source, identifier, cellWidth, cellHeight)`(`packages-user/client-base/src/material/types.ts:302`);`identifier` 为 `IIndexedIdentifier`(`{ index: number; alias: string }`,同文件 `:50`)。 + +| 素材 | 像素尺寸 | `cellWidth` / `cellHeight` 候选 | `identifier` 候选 | 判定依据 | +| --- | --- | --- | --- | --- | +| `public/project/tilesets/magictower.png:1` | 256×1216 | 32 / 32(**待确认**,§8) | `{ index: ?, alias: "magictower" }` | 256/32 = 8 列、1216/32 = 38 行;切格尺寸为**待确认**(§8) | + +**明确:`tilesets/*.png` 不套用 D-72⑤ 的 4×4 / 帧数列×4 行拆分。** + +### 2.4 D-72 目标拆分方案(materials) + +按 D-72 五规则逐组推导(本盘点读法:**单帧目标图 = 4×4 网格,即每图 16 个单帧图块,对应 `addGrid`;多帧目标图 = 「帧数列 × 4 行」,即每图 4 个多帧图块,对应 `addRowAnimate`**。该读法如与用户原意不符,请以用户裁定为准,§8 已记录)。 + +| 组 | 来源 PNG | 类别 | 帧数 | 图块数 | 目标图(候选)+ 行列布局 | 目标图数 | +| --- | --- | --- | --- | --- | --- | --- | +| A | `terrains.png` | `terrains` 单帧 | 1 | 35 | `4×4`(16 格/图,`addGrid`) | 3 | +| B | `animates.png` | 并入 `terrains`(D-72③)多帧 | 4 | 29 | `帧数列(4) × 4 行`(4 块/图,`addRowAnimate`) | 8 | +| C | `items.png` | `items` 单帧 | 1 | 62 | `4×4`(16 格/图,`addGrid`) | 4 | +| D | `enemys.png` + `enemy48.png` | `enemy` 合并(D-72①)多帧 | 2 | 81 | `帧数列(2) × 4 行`(4 块/图,`addRowAnimate`) | 21 | +| E | `npcs.png` + `npc48.png` | `npc` 合并(D-72①)多帧 | 2 | 24 | `帧数列(2) × 4 行`(4 块/图,`addRowAnimate`) | 6 | + +- 落实规则②:`terrains` / `items` / `enemy` / `npc` 各自成图,不混入同一张。 +- 落实规则③:**`animates` 并入 `terrains`,不设独立 `animates` 素材组**(因帧数不同,按规则④与 A 组分开成图,D-72③)。 +- 落实规则④:单帧(A/C)与多帧(B/D/E)分图;B 为 4 帧、D/E 为 2 帧,亦分图。 +- 落实规则⑤:A/C 为单帧 → 4×4;B/D/E 为多帧 → 帧数列 × 4 行。 +- `materials` 中 `ground` / `airwall` / `keyboard` / `icons` / `icons_old` 的 D-72 归类**待确认**(§8),未计入本表。 +- 每图「帧数 × 4 行」按 `addRowAnimate`「每一行的帧数一致」的能力(`packages-user/client-base/src/material/types.ts:275`)设计。 + +**SPLIT-COUNT: 42**(= A 3 + B 8 + C 4 + D 21 + E 6;即按 D-72 五规则推导的 materials 目标图数量,仅计 `terrains` / `items` / `enemy` / `npc` 四类) + +### 2.5 命名候选(待报备) + +下表所有命名**均为候选、标注「待用户报备批准」,本计划不落地使用**(D-73 / OI-3)。 + +| 候选类别 | 命名候选 | 说明 | 状态 | +| --- | --- | --- | --- | +| materials 拆分图(`terrains` 单帧) | `terrains0.png` / `terrains1.png` / `terrains2.png` | A 组 3 图 | 待用户报备批准 | +| materials 拆分图(`animates` 并入 `terrains`) | `terrainsAnim0.png` … `terrainsAnim7.png` | B 组 8 图;**不设独立 `animates` 素材组**(D-72③) | 待用户报备批准 | +| materials 拆分图(`items`) | `items0.png` … `items3.png` | C 组 4 图 | 待用户报备批准 | +| materials 拆分图(`enemy`) | `enemy0.png` … `enemy20.png` | D 组 21 图 | 待用户报备批准 | +| materials 拆分图(`npc`) | `npc0.png` … `npc5.png` | E 组 6 图 | 待用户报备批准 | +| autotile `identifier` 参数 | `20` / `151` / `152` / `153` | 对应 §2.2,取自 §1 图块 num | 待用户报备批准 | +| autotile `type` 参数 | `AutoTileType.Big3x4` | `autotile.png` / `autotile2.png` / `autotile3.png` | 待用户报备批准 | +| tileset `identifier.alias` | `magictower` | 对应 §2.3 | 待用户报备批准 | +| tileset `cellWidth` / `cellHeight` | `32` / `32` | 待确认(§8) | 待用户报备批准 | +| `.list.jsonc` 承载字段名 | `grid` / `rowAnimate` / `autotile` / `tileset` | 见 §7 草案 | 待用户报备批准 | + +## 3. 怪物 enemy + +数据来源:`public/project/enemys.js:3` 起的顶层 `var enemys_ = { "": {…} }` 对象,实测 **81** 条,与 §1 的 `enemys`(73) + `enemy48`(8) = 81 图块一一对应(均由 id 关联)。 + +目标接口 `IEnemyRawData`(`packages-user/data-common/src/store/types.ts:319`):`num: number` / `attribute: Readonly` / `special: Record`。落点默认骨架见 `src/content/defaults/enemy.jsonc:1`(`attribute` 含 `name` / `hp` / `atk` / `def` / `money` / `exp` / `point`)。 + +### 3.1 字段映射候选 + +旧字段实测频次:`name`(81)、`hp`(81)、`atk`(81)、`def`(81)、`money`(81)、`exp`(81)、`point`(81)、`special`(81)、`value`(6)、`n`(1)、`range`(1)、`zoneSquare`(1)、`bigImage`(3)、`atkValue`(1)、`defValue`(1)、`add`(1)、`notBomb`(2)、`faceIds`(4)。 + +分类口径同 §1.2(映射候选(可确定) / 待定(有目标字段但映射不确定) / 只记录、不移植(目标接口无对应字段));判定基准 = `IEnemyRawData`(`packages-user/data-common/src/store/types.ts:319`)。 + +| 旧字段 | 目标落点(候选) | 分类 | 说明 | +| --- | --- | --- | --- | +| `num`(由 §1 图块数字得来) | `IEnemyRawData.num` | 映射候选(可确定) | 由 `maps.js` 中 `enemys` / `enemy48` 图块 num 关联(如 `greenSlime` ↔ 201,`public/project/maps.js:121`) | +| `name` / `hp` / `atk` / `def` / `money` / `exp` / `point` | `IEnemyRawData.attribute.{…}` | 映射候选(可确定) | 与 `src/content/defaults/enemy.jsonc:1` 的 `attribute` 骨架字段一一对应 | +| `special`(数字数组,如 `[16,18]`) | `IEnemyRawData.special: Record` | 映射候选(可确定) | 旧为「特殊属性数字」数组 → 新为「数字键 → 值」映射;键即数组中的数字 | +| `value` / `range` / `zoneSquare` / `add` / `n` | `IEnemyRawData.special[<键>]`(候选) | 待定(有目标字段但映射不确定) | 旧伴随 `special` 的修饰量;**有目标字段**(`special`),其与具体特殊属性数字键的绑定关系未定(§8.2 U-10) | +| `bigImage` | `IEnemyRawData.special[<键>]`(候选) | 待定(有目标字段但映射不确定) | 仅 3 条含(`octopus` / `dragon` / `bearDown`,值为 `null`);**有目标字段**(`special`),键位未定(§8.2 U-10) | +| `notBomb` | `IEnemyRawData.special[<键>]`(候选) | 待定(有目标字段但映射不确定) | 2 条(`magicMaster` / `blackKing`);**有目标字段**(`special`),键位未定(§8.2 U-10) | +| `atkValue` / `defValue` | `attribute` 派生(候选) | 待定(有目标字段但映射不确定) | 1 条(`slimeman`);**有目标字段**(`attribute` / `special`),派生方式未定(§8.2 U-10) | +| `faceIds` | 无(目标接口无对应字段) | 只记录、不移植(目标接口无对应字段) | 4 条(`bearDown` / `bearLeft` / `bearRight` / `bearUp`);无字段承载多面机制(`enemy48` 变体合并入 `enemy`,D-72①)(§8.1 U-10b) | + +### 3.2 `id` / `num` / `attribute` / `special` 全清单(81 条) + +| num | id | name | 附加字段 | special | +| --- | --- | --- | --- | --- | +| 201 | greenSlime | 绿头怪 | - | special=[] | +| 202 | redSlime | 红头怪 | value | special=[16,18] | +| 203 | blackSlime | 青头怪 | - | special=[] | +| 204 | slimelord | 怪王 | - | special=[1,9] | +| 205 | bat | 小蝙蝠 | - | special=[1,27] | +| 206 | bigBat | 大蝙蝠 | - | special=[] | +| 207 | redBat | 红蝙蝠 | - | special=[] | +| 208 | vampire | 冥灵魔王 | n | special=[6] | +| 209 | skeleton | 骷髅人 | - | special=[] | +| 210 | skeletonWarrior | 骷髅士兵 | - | special=[] | +| 211 | skeletonCaptain | 骷髅队长 | - | special=[] | +| 212 | ghostSoldier | 冥队长 | - | special=[8] | +| 213 | zombie | 兽人 | - | special=[] | +| 214 | zombieKnight | 兽人武士 | - | special=[] | +| 215 | rock | 石头人 | - | special=[3] | +| 216 | slimeman | 影子战士 | atkValue, defValue | special=[9] | +| 217 | bluePriest | 初级法师 | - | special=[9] | +| 218 | redPriest | 高级法师 | - | special=[] | +| 219 | brownWizard | 初级巫师 | value, range | special=[15] | +| 220 | redWizard | 高级巫师 | value, zoneSquare | special=[15] | +| 221 | yellowGateKeeper | 初级卫兵 | - | special=[] | +| 222 | blueGateKeeper | 中级卫兵 | - | special=[] | +| 223 | redGateKeeper | 高级卫兵 | - | special=[] | +| 224 | swordsman | 双手剑士 | - | special=[4] | +| 225 | soldier | 冥战士 | - | special=[] | +| 226 | yellowKnight | 金骑士 | - | special=[] | +| 227 | redKnight | 红骑士 | - | special=[7] | +| 228 | darkKnight | 黑骑士 | - | special=[] | +| 229 | blackKing | 黑衣魔王 | notBomb | special=[] | +| 230 | yellowKing | 黄衣魔王 | - | special=[] | +| 231 | greenKing | 青衣武士 | - | special=[] | +| 232 | blueKnight | 蓝骑士 | - | special=[8] | +| 233 | goldSlime | 黄头怪 | - | special=[] | +| 234 | poisonSkeleton | 紫骷髅 | - | special=[13] | +| 235 | poisonBat | 紫蝙蝠 | - | special=[13] | +| 236 | ironRock | 铁面人 | - | special=[] | +| 237 | skeletonPriest | 骷髅法师 | value | special=[18] | +| 238 | skeletonKing | 骷髅王 | - | special=[] | +| 239 | skeletonPresbyter | 骷髅巫师 | - | special=[] | +| 240 | skeletonKnight | 骷髅武士 | - | special=[] | +| 241 | evilHero | 迷失勇者 | - | special=[] | +| 242 | devilWarrior | 魔神武士 | - | special=[] | +| 243 | demonPriest | 魔神法师 | - | special=[] | +| 244 | goldHornSlime | 金角怪 | - | special=[] | +| 245 | redKing | 红衣魔王 | - | special=[] | +| 246 | blueKing | 白衣武士 | - | special=[16] | +| 247 | magicMaster | 黑暗大法师 | value, add, notBomb | special=[11] | +| 248 | silverSlime | 银头怪 | - | special=[14] | +| 249 | blademaster | 剑圣 | - | special=[] | +| 250 | whiteHornSlime | 尖角怪 | - | special=[] | +| 251 | evilPrincess | 痛苦魔女 | - | special=[10] | +| 252 | evilFairy | 黑暗仙子 | - | special=[] | +| 253 | yellowPriest | 中级法师 | - | special=[] | +| 254 | redSwordsman | 剑王 | - | special=[] | +| 255 | whiteSlimeman | 水银战士 | - | special=[] | +| 256 | poisonZombie | 绿兽人 | - | special=[12] | +| 257 | dragon | 魔龙 | bigImage | special=[] | +| 258 | octopus | 血影 | bigImage | special=[] | +| 259 | fairyEnemy | 仙子 | - | special=[] | +| 260 | princessEnemy | 假公主 | - | special=[] | +| 261 | angel | 天使 | - | special=[] | +| 262 | elemental | 元素生物 | - | special=[] | +| 263 | steelGuard | 铁守卫 | value | special=[18] | +| 264 | evilBat | 邪恶蝙蝠 | - | special=[] | +| 265 | silverSlimelord | 银怪王 | - | special=[] | +| 266 | goldSlimelord | 金怪王 | - | special=[] | +| 267 | grayRock | 灰色石头人 | - | special=[] | +| 270 | greenKnight | 强盾骑士 | - | special=[] | +| 271 | bowman | 初级弓兵 | - | special=[] | +| 272 | purpleBowman | 高级弓兵 | - | special=[] | +| 275 | watcherSlime | 邪眼怪 | - | special=[] | +| 277 | frostBat | 寒蝙蝠 | - | special=[] | +| 278 | devilKnight | 恶灵骑士 | - | special=[1,5,7,8] | +| 279 | grayPriest | 混沌法师 | - | special=[] | +| 280 | greenGateKeeper | 卫兵队长 | - | special=[] | +| 325 | keiskeiFairy | 铃兰花妖 | - | special=[] | +| 326 | tulipFairy | 郁金香花妖 | - | special=[] | +| 327 | bearDown | 熊出没 | faceIds, bigImage | special=[] | +| 328 | bearLeft | 熊出没 | faceIds | special=[] | +| 329 | bearRight | 熊出没 | faceIds | special=[] | +| 330 | bearUp | 熊出没 | faceIds | special=[] | + +**ENEMY-COUNT: 81** + +## 4. 道具 items + +数据来源:`public/project/items.js:3` 起的顶层 `var items_ = { "": {…} }` 对象,实测 **62** 条。目标接口 `IItemRawData`(`packages-user/data-common/src/store/types.ts:176`):`num` / `category: ItemCategory` / `name` / `text` / `hideInToolbox` / `effect: IItemEffect` / `equip: IItemEquipData`。 + +### 4.1 `cls` → `ItemCategory` 映射候选 + +目标枚举 `ItemCategory`(`packages-user/data-common/src/store/types.ts:125`):`Unknown` / `Constant`(永久道具)/ `Consumable`(一次性道具)/ `Pick`(即捡即用)/ `Equipment`(装备)。 + +| 旧 `cls` | 实测条数 | `ItemCategory`(候选) | 依据 | +| --- | --- | --- | --- | +| `tools` | 20 | `Consumable`(候选) | 多为可多次使用的钥匙 / 器械;`canUseItemEffect` 普遍存在 | +| `items` | 32 | `Consumable`(候选) | 含宝石 / 血瓶 / 剑盾(带 `equip`);部分语义接近即拾即用(`yellowGem` 带 `useItemEvent`),细分**待确认**(§8) | +| `equips` | 2 | `Equipment` | 仅 `sword0` / `shield0`(`public/project/items.js:132`),带 `equip` | +| `constants` | 8 | `Constant` | 永久道具(`book` / `fly` / `coin` 等,`public/project/items.js:304`) | + +说明:`cls` 为 `items` 的剑盾(`sword1..5` / `shield1..5`)也带 `equip`,其 `category` 究竟应取 `Consumable` 还是 `Equipment`**待确认**(§8)。 + +### 4.2 效果脚本与装备映射候选 + +旧字段实测频次:`cls`(62)、`name`(62)、`text`(49)、`hideInToolbox`(5)、`itemEffect`(23)、`itemEffectTip`(23)、`useItemEffect`(25)、`canUseItemEffect`(38)、`useItemEvent`(2)、`equip`(12)、`hideInReplay`(2)。 + +分类口径同 §1.2;判定基准 = `IItemRawData` / `IItemEffect` / `IItemEquipData`(`packages-user/data-common/src/store/types.ts:176` / `:138` / `:161`)。 + +| 旧字段 | 目标落点(候选) | 分类 | 说明 | +| --- | --- | --- | --- | +| `name` / `text` / `hideInToolbox` | `IItemRawData.{name,text,hideInToolbox}` | 映射候选(可确定) | 直接对应 | +| `itemEffect` | `IItemEffect.effect` 派生(候选) | 待定(有目标字段但映射不确定) | 旧为字符串 / 函数(如 `红宝石` 的 `itemEffect`);**有目标字段**(`IItemEffect`),派生方式未定(§8.2 U-12) | +| `itemEffectTip` | `IItemEffect.effect` 派生(候选) | 待定(有目标字段但映射不确定) | 效果提示串(含 `${…}` 模板);**有目标字段**(`IItemEffect`),派生方式未定(§8.2 U-12) | +| `useItemEffect` | `IItemEffect.useEffect?`(候选) | 待定(有目标字段但映射不确定) | 旧为字符串脚本(部分为函数体,如 `freezeBadge` 852 字符);**有目标字段**(`IItemEffect.useEffect`),映为 `useEffect(item, state)` 方法的改写方式未定(§8.2 U-12) | +| `useItemEvent` | `IItemEffect.useEvent?`(候选) | 待定(有目标字段但映射不确定) | 旧为事件数组(`yellowGem` / `lifeWand`);新 `useEvent?: string` 承载事件 id,事件库由落点侧提供(`src/content/item.ts:7` 的 `item200` 为示例)(§8.2 U-12) | +| `canUseItemEffect` | `IItemEffect.canUse?`(候选) | 待定(有目标字段但映射不确定) | 旧为布尔或函数串;新为 `canUse(item, state): boolean`(§8.2 U-12) | +| `equip` | `IItemEquipData`(`packages-user/data-common/src/store/types.ts:161`) | 映射候选(可确定) | 字段级有对应目标接口;旧 `equip` = `{ type: number; animate?: string; value: Record }` → 新 `{ slots: (number\|string)[]; animate: AnimationIds; value; percentage; loadEvent; unloadEvent }`(子字段映射见 §4.3 / §8.2 U-14) | +| `hideInReplay` | 无(目标接口无对应字段) | 只记录、不移植(目标接口无对应字段) | 2 条(`fly` / `skill1`);`IItemRawData` 无对应字段(§8.1 U-13) | + +### 4.3 `equip` → `IItemEquipData` 映射候选 + +旧 `equip` 实测 12 条,形态为 `{ type: number; animate?: string; value: Record }`。 + +| 旧 `equip` 字段 | 目标落点(候选) | 说明 | +| --- | --- | --- | +| `type`(0 / 1) | `IItemEquipData.slots: (number\|string)[]` | 旧为单个装备孔索引(0 = 武器,1 = 盾);新为数组,是否包成 `[type]` **待确认**(§8) | +| `animate`(如 `"sword"`) | `IItemEquipData.animate: AnimationIds` | 仅剑类含(`sword0..5`);旧动画 id 字符串 → 新 `AnimationIds` 的取值**待确认**(§8) | +| `value`(`{ atk: 10 }` / `{ def: 10 }`) | `IItemEquipData.value: Record, number>` | 旧属性名(`atk` / `def` / `mdef`)→ 新属性键的对应**待确认**(§8) | +| —(旧无) | `IItemEquipData.percentage` / `loadEvent` / `unloadEvent` | 旧数据无对应;`defaults/item.jsonc:1` 给出空骨架 | + +### 4.4 `id` / `num` / `cls` 全清单(62 条) + +| num | id | cls | 关键字段 | +| --- | --- | --- | --- | +| 21 | yellowKey | tools | hideInToolbox | +| 22 | blueKey | tools | hideInToolbox | +| 23 | redKey | tools | hideInToolbox | +| 24 | greenKey | tools | - | +| 25 | steelKey | tools | - | +| 26 | bigKey | tools | itemEffect, itemEffectTip, useItemEffect, canUseItemEffect | +| 27 | redGem | items | itemEffect, itemEffectTip, useItemEffect, canUseItemEffect | +| 28 | blueGem | items | itemEffect, itemEffectTip, useItemEffect, canUseItemEffect | +| 29 | greenGem | items | itemEffect, itemEffectTip, useItemEffect, canUseItemEffect | +| 30 | yellowGem | items | itemEffect, itemEffectTip, useItemEvent, canUseItemEffect | +| 31 | redPotion | items | itemEffect, itemEffectTip, useItemEffect, canUseItemEffect | +| 32 | bluePotion | items | itemEffect, itemEffectTip, useItemEffect, canUseItemEffect | +| 33 | greenPotion | items | itemEffect, itemEffectTip, useItemEffect, canUseItemEffect | +| 34 | yellowPotion | items | itemEffect, itemEffectTip, useItemEffect, canUseItemEffect | +| 35 | sword1 | items | equip, itemEffect, itemEffectTip | +| 36 | shield1 | items | equip, itemEffect, itemEffectTip | +| 37 | sword2 | items | equip, itemEffect, itemEffectTip | +| 38 | shield2 | items | equip, itemEffect, itemEffectTip | +| 39 | sword3 | items | equip, itemEffect, itemEffectTip | +| 40 | shield3 | items | equip, itemEffect, itemEffectTip | +| 41 | sword4 | items | equip, itemEffect, itemEffectTip | +| 42 | shield4 | items | equip, itemEffect, itemEffectTip | +| 43 | sword5 | items | equip, itemEffect, itemEffectTip | +| 44 | shield5 | items | equip, itemEffect, itemEffectTip | +| 45 | book | constants | hideInToolbox, useItemEffect, canUseItemEffect | +| 46 | fly | constants | hideInReplay, hideInToolbox, useItemEffect, canUseItemEffect | +| 47 | pickaxe | tools | useItemEffect, canUseItemEffect | +| 48 | icePickaxe | tools | useItemEffect, canUseItemEffect | +| 49 | bomb | tools | useItemEffect, canUseItemEffect | +| 50 | centerFly | tools | useItemEffect, canUseItemEffect | +| 51 | upFly | tools | useItemEffect, canUseItemEffect | +| 52 | downFly | tools | useItemEffect, canUseItemEffect | +| 53 | coin | constants | - | +| 54 | freezeBadge | constants | useItemEffect, canUseItemEffect | +| 55 | cross | constants | - | +| 56 | superPotion | items | itemEffect, itemEffectTip, useItemEffect, canUseItemEffect | +| 57 | earthquake | tools | useItemEffect, canUseItemEffect | +| 58 | poisonWine | tools | useItemEffect, canUseItemEffect | +| 59 | weakWine | tools | useItemEffect, canUseItemEffect | +| 60 | curseWine | tools | useItemEffect, canUseItemEffect | +| 61 | superWine | tools | useItemEffect, canUseItemEffect | +| 62 | dagger | constants | - | +| 63 | pack | items | itemEffect, itemEffectTip | +| 64 | amulet | constants | - | +| 65 | hammer | tools | - | +| 68 | lifeWand | tools | useItemEvent, canUseItemEffect | +| 69 | jumpShoes | tools | useItemEffect, canUseItemEffect | +| 70 | sword0 | equips | equip, itemEffect, itemEffectTip | +| 71 | shield0 | equips | equip, itemEffect, itemEffectTip | +| 72 | skill1 | constants | hideInReplay, useItemEffect, canUseItemEffect | +| 73 | wand | items | - | +| 331 | I331 | items | canUseItemEffect | +| 332 | I332 | items | canUseItemEffect | +| 333 | I333 | items | canUseItemEffect | +| 334 | I334 | items | canUseItemEffect | +| 335 | I335 | items | canUseItemEffect | +| 336 | I336 | items | canUseItemEffect | +| 337 | I337 | items | canUseItemEffect | +| 338 | I338 | items | canUseItemEffect | +| 339 | I339 | items | canUseItemEffect | +| 340 | I340 | items | canUseItemEffect | +| 341 | I341 | items | canUseItemEffect | + +**ITEM-COUNT: 62** + +## 5. NPC + +依 D-72①,`npcs`(14) 与 `npc48`(10) **合并**为 `npc`(不再有 48 变体)。数据来源为 §1 的图块集合(`public/project/maps.js:90` 起)。 + +> **交叉引用(D-72③,指令 1):** D-72③ 的 **`animates` 并入 `terrains`,不设独立 `animates` 素材组** 作用于**图块素材分组**层面,与本节的 NPC 分组(依 D-72① 并入 `npc`)**互不冲突、互不影响**:NPC 图块仍归 `npc` 组,`animates` 图块归 `terrains` 组。本节不涉及 `animates` 内容。 + +### 5.1 图块映射候选(合并为 `npc`) + +| 旧 `cls` | 旧 num | 旧 id | 目标 | 说明 | +| --- | --- | --- | --- | --- | +| `npcs` | 121 | `man` | `npc` | 普通 NPC(32px) | +| `npcs` | 122 | `trader` | `npc` | | +| `npcs` | 123 | `thief` | `npc` | | +| `npcs` | 124 | `fairy` | `npc` | | +| `npcs` | 125 | `wizard` | `npc` | | +| `npcs` | 126 | `recluse` | `npc` | | +| `npcs` | 127 | `king` | `npc` | | +| `npcs` | 128 | `youngMan` | `npc` | | +| `npcs` | 129 | `sign` | `npc` | | +| `npcs` | 130 | `expShop` | `npc` | | +| `npcs` | 131 | `moneyShop` | `npc` | | +| `npcs` | 132 | `princess` | `npc` | | +| `npcs` | 137 | `greenMan` | `npc` | | +| `npcs` | 138 | `blueTrader` | `npc` | | +| `npc48` | 133 | `npc0` | `npc` | 高 48px;含 `faceIds`(四面) | +| `npc48` | 134 | `npc1` | `npc` | 高 48px;含 `faceIds` | +| `npc48` | 135 | `npc2` | `npc` | 高 48px;含 `faceIds` | +| `npc48` | 136 | `npc3` | `npc` | 高 48px;含 `faceIds` | +| `npc48` | 319 | `tallYellowDoor` | `npc` | 高门(`trigger: openDoor` + `doorInfo`) | +| `npc48` | 320 | `tallBlueDoor` | `npc` | 高门 | +| `npc48` | 321 | `tallRedDoor` | `npc` | 高门 | +| `npc48` | 322 | `tallGreenDoor` | `npc` | 高门 | +| `npc48` | 323 | `tallSpecialDoor` | `npc` | 高门 | +| `npc48` | 324 | `tallSteelDoor` | `npc` | 高门 | + +### 5.2 贴图归属候选 + +`public/project/materials/npcs.png:1`(64×448,14 NPC × 2 帧)与 `public/project/materials/npc48.png:1`(128×480,10 NPC × 2 帧)按 D-72① **合并**为一张 `npc`(多帧,2 帧),归属 §2.4 的 E 组(目标图 6 张,`npc0.png` … `npc5.png`)。**注意:** `npc48` 中的 6 个高门(319–324)虽归 `npc` 图块类,但其 48px 高度与 32px NPC 的帧尺寸不同——是否与 32px NPC 混入同一目标图,受 D-72④「不同帧数不得同图」约束需进一步确认(§8)。 + +**NPC-COUNT: 24**(= `npcs` 14 + `npc48` 10) + +## 6. 地图 maps + +数据来源:`public/project/floors/*.js`(`main.floors. = {…}`,`public/project/floors/MT0.js:1`)。实测 **5** 个楼层文件。目标接口 `IMapRawData`(`packages-user/data-common/src/store/types.ts:260`):`floorId` / `width` / `map: Record` / `layerAlias: Record` / `events: Record>>`。 + +### 6.1 楼层登记 + +| 文件 | 楼层 id | `width` × `height` | `map` 形状 | `events` 条目数 | 备注字段 | +| --- | --- | --- | --- | --- | --- | +| `public/project/floors/empty.js:1` | `empty` | 13 × 13 | 二维 13×13 | 0 | `bgm` | +| `public/project/floors/MT0.js:1` | `MT0` | 13 × 13 | 二维 13×13 | 0 | — | +| `public/project/floors/sample0.js:1` | `sample0` | 13 × 13 | 二维 13×13 | 7 | `fgmap` 13×13、`bgm` | +| `public/project/floors/sample1.js:1` | `sample1` | 13 × 13 | 二维 13×13 | 15 | `weather` | +| `public/project/floors/sample2.js:1` | `sample2` | 26 × 26 | 二维 26×26 | 3 | `upFloor` | + +各文件字段集合(实测):`floorId` / `title` / `name` / `width` / `height` / `canFlyTo` / `canFlyFrom` / `canUseQuickShop` / `cannotViewMap` / `defaultGround` / `images` / `bgm` / `ratio` / `weather` / `map` / `firstArrive` / `eachArrive` / `parallelDo` / `events` / `changeFloor` / `afterBattle` / `afterGetItem` / `afterOpenDoor` / `cannotMove` / `cannotMoveIn` / `beforeBattle` / `autoEvent` / `upFloor` / `bgmap` / `fgmap` / `bg2map` / `fg2map`。 + +### 6.2 字段映射候选 → `IMapRawData` + +分类口径同 §1.2;判定基准 = `IMapRawData`(`packages-user/data-common/src/store/types.ts:260`)。 + +| 旧字段 | 目标落点(候选) | 分类 | 说明 | +| --- | --- | --- | --- | +| `floorId` | `IMapRawData.floorId` | 映射候选(可确定) | 直接对应(如 `MT0`,`public/project/floors/MT0.js:1`) | +| `width` | `IMapRawData.width` | 映射候选(可确定) | 直接对应(13 / 26) | +| `map`(二维数组 `[row][col]`) | `IMapRawData.map: Record` | 映射候选(可确定) | 旧二维数组 → 新「层键 → 一维数组」;层键即行索引(`0`…`height-1`)为候选,见 `src/content/maps/MT0.jsonc:6`(`"0"` / `"10"` 两键) | +| `layerAlias`(旧无) | `IMapRawData.layerAlias` | 映射候选(可确定) | 旧数据无该字段;默认空对象(`src/content/defaults/map.jsonc:1` 给出 `{}`) | +| `events`(`"x,y"` → 事件数据) | `IMapRawData.events: Record>>` | 待定(有目标字段但映射不确定) | **确有对应目标字段** `IMapRawData.events`;仅「层 → 位置索引 → 优先级 → 事件 id」三段嵌套由 `"x,y"` 换算的算法未定(§8.2 U-15) | +| `title` / `name` | 无(目标接口无对应字段) | 只记录、不移植(目标接口无对应字段) | 楼层显示名(如 `主塔 0 层` / `样板 0 层`);`IMapRawData` 无对应字段(§8.1 U-16) | +| `height` | 无(目标接口无对应字段) | 只记录、不移植(目标接口无对应字段) | `IMapRawData` 无 `height`;由 `map` 层数隐含(§8.1 U-16) | +| `firstArrive` / `eachArrive` / `parallelDo` | 无(目标接口无对应字段) | 只记录、不移植(目标接口无对应字段) | 楼层到达 / 并行事件;无专用字段承载(§8.1 U-16) | +| `changeFloor` | 无(目标接口无对应字段) | 只记录、不移植(目标接口无对应字段) | 换层定义(含 `floorId` / `stair` / `loc` / `direction`);无专用字段(§8.1 U-16) | +| `afterBattle` / `afterGetItem` / `afterOpenDoor` / `beforeBattle` / `cannotMove` / `cannotMoveIn` / `autoEvent` | 无(目标接口无对应字段) | 只记录、不移植(目标接口无对应字段) | 各类钩子事件;无专用字段(§8.1 U-16) | +| `bgmap` / `fgmap` / `bg2map` / `fg2map` | 图层机制(候选) | 待定(有目标字段但映射不确定) | 旧额外图层二维数组(如 `sample0` 的 `fgmap` 13×13,含 10169 等数字);**可能**落入 `IMapRawData.map` / `layerAlias`,但对应关系无法唯一确定(§8.2 U-16b) | +| `images` / `bgm` / `weather` / `ratio` / `defaultGround` / `canFlyTo` / `canFlyFrom` / `canUseQuickShop` / `cannotViewMap` / `upFloor` | 无(目标接口无对应字段) | 只记录、不移植(目标接口无对应字段) | 渲染 / 规则属性;`IMapRawData` 无对应字段(§8.1 U-16) | + +**MAP-COUNT: 5** + +## 7. 贴图列表 schema 草案(texture/.list.jsonc) + +依 `ITextureManager` 的能力(`packages-user/client-base/src/material/types.ts`)**反向设计** `src/content/texture/.list.jsonc`(落点由 `src/content/client.jsonc:9` 的 `textureListDir: texture/.list.jsonc` 指定;现状为 `{}`,`src/content/texture/.list.jsonc:1`)。**仅草案**;所有字段命名均为候选、标注「待用户报备批准」,**不落地写入任何文件**(D-73 / OI-3 / OI-5)。 + +能力依据: + +- `addGrid(source, map, width, height)`(`:261`)—— 单帧网格贴图(先左到右、再上到下)。 +- `addRowAnimate(source, map, width, height)`(`:275`)—— 行动画贴图(每行帧数一致,逐行映射)。 +- `addAutotile(source, identifier, type)`(`:289`)—— 自动元件(`type: AutotileType`)。 +- `addTileset(source, identifier, cellWidth, cellHeight)`(`:302`)—— tileset 切片(`identifier: IIndexedIdentifier`)。 + +字段结构草案(与 §2 登记逐条一致): + +| 条目 `type`(候选) | 承载来源(§2) | 字段(候选) | 对应方法 | +| --- | --- | --- | --- | +| `grid` | §2.4 A 组(`terrains` 单帧)、C 组(`items` 单帧) | `source`(PNG 路径)/ `map`(图块数字数组)/ `width` / `height`(单帧像素尺寸,候选 32) | `addGrid` | +| `rowAnimate` | §2.4 B 组(animates 并入 terrains)、D 组(enemy)、E 组(npc) | `source` / `map` / `width` / `height`(每帧像素尺寸) | `addRowAnimate` | +| `autotile` | §2.2(`autotiles/*.png`) | `source` / `identifier`(图块数字)/ `type`(`AutotileType`) | `addAutotile` | +| `tileset` | §2.3(`tilesets/*.png`) | `source` / `identifier`(`{ index, alias }`)/ `cellWidth` / `cellHeight` | `addTileset` | + +字段结构草案(示意,**非落地**): + +``` +{ + "textures": [ + { "type": "grid", "source": "", "map": [ /* nums */ ], "width": 32, "height": 32 }, + { "type": "rowAnimate", "source": "", "map": [ /* nums */ ], "width": 32, "height": 32 }, + { "type": "grid", "source": "", "map": [ /* nums */ ], "width": 32, "height": 32 }, + { "type": "rowAnimate", "source": "", "map": [ /* nums */ ], "width": 32, "height": 32 }, + { "type": "rowAnimate", "source": "", "map": [ /* nums */ ], "width": 32, "height": 32 }, + { "type": "autotile", "source": "", "identifier": 20, "type": 1 }, + { "type": "tileset", "source": "", "identifier": { "index": 0, "alias": "magictower" }, "cellWidth": 32, "cellHeight": 32 } + ] +} +``` + +一致性核对:materials 图 → `grid`(单帧)/ `rowAnimate`(多帧);`autotiles/*.png` → `autotile`;`tilesets/*.png` → `tileset`。与 §2.1–§2.4 的登记逐条对应。 + +### 7.1 显式 TypeScript 类型声明(候选,均「待用户报备批准」) + +> 本节为 §7 schema 的**显式类型声明**(`interface` 与 `type` 别名形式),供后续「迁移计划」落地参考。**所有新增类型名 / 字段名均为「命名候选」,标注「待用户报备批准」,本盘点不落地写入任何文件**(D-73 / OI-2)。 + +```ts +/** 图像源路径(候选命名,待用户报备批准) */ +type TextureListSource = string; + +/** grid 条目,对应 `addGrid`(候选命名,待用户报备批准) */ +interface ITextureListGridEntry { + /** 条目判别字段 */ + type: 'grid'; + /** 图像源 */ + source: TextureListSource; + /** 贴图的图块数字映射,按先从左到右、再从上到下顺序映射(对应 `addGrid` 的 `map`) */ + map: number[]; + /** 每个贴图的像素宽度(对应 `addGrid` 的 `width`) */ + width: number; + /** 每个贴图的像素高度(对应 `addGrid` 的 `height`) */ + height: number; +} + +/** rowAnimate 条目,对应 `addRowAnimate`(候选命名,待用户报备批准) */ +interface ITextureListRowAnimateEntry { + /** 条目判别字段 */ + type: 'rowAnimate'; + /** 图像源 */ + source: TextureListSource; + /** 贴图的图块数字映射,按从上到下顺序映射(对应 `addRowAnimate` 的 `map`) */ + map: number[]; + /** 每一帧的像素宽度(对应 `addRowAnimate` 的 `width`) */ + width: number; + /** 每一帧 / 每一行的像素高度(对应 `addRowAnimate` 的 `height`) */ + height: number; +} + +/** autotile 条目,对应 `addAutotile`(候选命名,待用户报备批准) */ +interface ITextureListAutotileEntry { + /** 条目判别字段 */ + type: 'autotile'; + /** 图像源 */ + source: TextureListSource; + /** 自动元件的图块数字(对应 `addAutotile` 的 `identifier`) */ + identifier: number; + /** + * 自动元件类型,取 `AutotileType`(对应 `addAutotile` 的 `type` 参数)。 + * 为避免与判别字段 `type` 同名冲突,此参数以候选字段名 `autotileType` 承载(待用户报备批准,见 §8.3 U-19)。 + */ + autotileType: AutotileType; +} + +/** tileset 条目,对应 `addTileset`(候选命名,待用户报备批准) */ +interface ITextureListTilesetEntry { + /** 条目判别字段 */ + type: 'tileset'; + /** 图像源 */ + source: TextureListSource; + /** tileset 标识符对象,取 `IIndexedIdentifier`(对应 `addTileset` 的 `identifier`) */ + identifier: IIndexedIdentifier; + /** Tileset 中每个 tile 的宽度(对应 `addTileset` 的 `cellWidth`) */ + cellWidth: number; + /** Tileset 中每个 tile 的高度(对应 `addTileset` 的 `cellHeight`) */ + cellHeight: number; +} + +/** 贴图列表条目联合类型:四类 entry 之一(候选命名,待用户报备批准) */ +type TextureListEntry = + | ITextureListGridEntry + | ITextureListRowAnimateEntry + | ITextureListAutotileEntry + | ITextureListTilesetEntry; + +/** `texture/.list.jsonc` 顶层配置接口(候选命名,待用户报备批准) */ +interface ITextureListConfig { + /** 贴图条目列表,元素为 `TextureListEntry` */ + textures: TextureListEntry[]; +} +``` + +**类型声明与能力接口的对应:** `ITextureListGridEntry` ↔ `addGrid`(`packages-user/client-base/src/material/types.ts:261`);`ITextureListRowAnimateEntry` ↔ `addRowAnimate`(`:275`);`ITextureListAutotileEntry` ↔ `addAutotile`(`:289`,`type` 取 `AutotileType`,定义于 `:12`);`ITextureListTilesetEntry` ↔ `addTileset`(`:302`,`identifier` 取 `IIndexedIdentifier`,定义于 `:50`)。`ITextureListConfig.textures` 的元素即 `TextureListEntry`。 + +**待报备命名清单(OI-2):** 类型名 `TextureListSource` / `ITextureListGridEntry` / `ITextureListRowAnimateEntry` / `ITextureListAutotileEntry` / `ITextureListTilesetEntry` / `TextureListEntry` / `ITextureListConfig`,以及字段名 `type` / `source` / `map` / `width` / `height` / `identifier` / `autotileType` / `cellWidth` / `cellHeight` / `textures`;**均「待用户报备批准」**。 + +## 8. 未确定项 / 待确认观察 + +以下内容**无法从阅读 / 静态解析唯一确定**,或属**疑似不一致的观察**。依 prohibitions,**均不登记为缺陷**,仅作待确认观察,供用户裁定。 + +**分区依据(指令 2):** 按「新接口(`ITileRawData` / `ITilePassData` / `IEnemyRawData` / `IItemRawData` / `IMapRawData`)是否存在对应字段」二分 —— **§8.1** 为**无目标字段 → 只记录、不移植**(字段级);**§8.2** 为**有目标字段但映射不确定 → 待定**(字段级);**§8.3** 保留**非字段级**的其他待确认观察。§8.1 / §8.2 与 §1.2 / §3.1 / §4.2 / §6.2 的「分类」列**逐条一致**。(旧编号 U-1..U-18 全部保留;含跨类内容的项拆为 `U-10b` / `U-16b`。) + +### 8.1 无目标字段 → 只记录、不移植(字段级) + +判定基准:目标接口中**不存在**对应字段;旧字段只作盘点记录,不进入迁移。 + +| 编号 | 观察 / 未确定项 | 所查接口与缺失字段(执行日实测) | +| --- | --- | --- | +| U-4 | 图块 `canBreak`(4) / `script`(5) / `doorInfo`(17) / `animate`(21)(§1.2) | 查 `ITileRawData`(`packages-user/data-common/src/store/types.ts:47`)与 `ITilePassData`(`:38`):无承载「可破坏 / 内联脚本正文 / 门机制参数(`time`/`keys`/`openSound`/`closeSound`)/ 帧数」的字段 | +| U-5 | 图块 `name`(32) / `faceIds`(4) / `autotileConnection`(2)(§1.2) | 查 `ITileRawData`(`:47`)/ `ITilePassData`(`:38`):无显示名 / 多面 / 自动元件连接字段(`autotileConnection` 对应 `IAutotileProcessor.setConnection`,非图块原始数据字段) | +| U-10b | 怪物 `faceIds`(4)(§3.1) | 查 `IEnemyRawData`(`:319`):无多面怪物字段 | +| U-13 | 道具 `hideInReplay`(2)(§4.2) | 查 `IItemRawData`(`:176`):无对应字段 | +| U-16 | 地图 `title` / `name` / `height` / `firstArrive` / `eachArrive` / `parallelDo` / `changeFloor` / `afterBattle` / `afterGetItem` / `afterOpenDoor` / `beforeBattle` / `cannotMove` / `cannotMoveIn` / `autoEvent` / `images` / `bgm` / `weather` / `ratio` / `defaultGround` / `canFlyTo` / `canFlyFrom` / `canUseQuickShop` / `cannotViewMap` / `upFloor`(§6.2) | 查 `IMapRawData`(`:260`):无上述楼层属性的对应字段 | + +### 8.2 有目标字段但映射不确定 → 待定(字段级) + +判定基准:目标接口**确有**对应字段,但取值 / 展开 / 嵌套关系无法由静态解析唯一确定。 + +| 编号 | 观察 / 未确定项 | 已确认存在的目标字段(执行日实测) | 缺什么才能确定 | +| --- | --- | --- | --- | +| U-2 | 图块 `trigger`(`null` / `openDoor` / `ski` / `pushBox`)的具体对应(§1.2,共 20 条) | `ITileRawData.events`(`:53`)/ `eventPass`(`:62`) | 新引擎事件 id 命名与事件可通行性约定 | +| U-3 | 图块 `canPass: true` → 四方向通行位(`PassBit`)的具体展开(§1.2,共 19 条) | `ITilePassData.inPass` / `outPass`(`:38`,位掩码见 `PassBit` `:27`) | 新引擎通行位的方向展开规则 | +| U-10 | 怪物 `value` / `range` / `zoneSquare` / `add` / `n` / `bigImage` / `notBomb` / `atkValue` / `defValue` 与 `special` 数字键的绑定关系(§3.1) | `IEnemyRawData.special`(`:325`)/ `attribute`(`:323`) | 新引擎特殊属性数字字典 | +| U-12 | 道具 `itemEffect` / `itemEffectTip` / `useItemEffect` / `useItemEvent` / `canUseItemEffect` 的目标落点(§4.2) | `IItemEffect`(`:138`,含 `useEvent` / `useEffect` / `canUse`) | 新引擎道具效果接口对上述字段的承载方式 | +| U-14 | `equip.type`(单值)→ `IItemEquipData.slots`(数组)的包装;`animate` 字符串 → `AnimationIds`;`value` 属性名(`atk` / `def` / `mdef`)→ 新属性键(§4.3) | `IItemEquipData`(`:161`) | 新引擎装备槽索引、动画 id 与属性键的取值 | +| U-15 | 地图 `events` 旧键 `"x,y"` → `IMapRawData.events` 三段嵌套(层 / 位置索引 / 优先级)的算法(§6.2) | `IMapRawData.events`(`:270`) | 新引擎事件位置索引与优先级约定 | +| U-16b | 地图 `bgmap` / `fgmap` / `bg2map` / `fg2map` 是否并入地图图层(§6.2) | `IMapRawData.map`(`:266`)/ `layerAlias`(`:268`) | 额外图层是否编入 `map` / `layerAlias` 及对应关系 | + +### 8.3 其他待确认观察(非字段级) + +| 编号 | 观察 / 未确定项 | 缺什么才能确定 | +| --- | --- | --- | +| U-1 | D-72⑤ 读法:本盘点将「单帧按 4×4 / 多帧按帧数列 × 4 行」理解为「单帧目标图 = 4×4 网格(16 格);多帧目标图 = 帧数列 × 4 行(4 块)」,并据此得 SPLIT-COUNT = 42(§2.4)。 | 用户确认该读法,或给出目标图每图格数的确切约定 | +| U-6 | `ground.png` / `airwall.png` / `keyboard.png` / `icons.png` / `icons_old.png` 是否属于 D-72 四类拆分范围及其归属(§2.1)。 | 用户确认其是否纳入 tile 素材及归入哪一类 | +| U-7 | `autotile1.png`(384×128)的自动元件类型无法由尺寸唯一判定为 2×3 `Small2x3` 或 3×4 `Big3x4`(§2.2)。 | 该素材的原始自动元件规格 | +| U-8 | `magictower.png`(256×1216)的 `cellWidth` / `cellHeight` 切格尺寸与 `identifier.index`(§2.3)。 | 该 tileset 的切格尺寸与索引约定 | +| U-9 | `enemy48.png` / `npc48.png` 的每格像素与帧布局:本盘点按「2 帧列、128px 宽」推导(§2.1),但 48px 高度下的单元格尺寸未由数据唯一确定。 | 高 48px 素材的单元格尺寸与帧布局 | +| U-11 | 道具 `category` 细分:`cls: items` 且带 `equip` 的剑盾(`sword1..5` / `shield1..5`)应取 `Consumable` 还是 `Equipment`(§4.1)。 | 用户确认其分类口径 | +| U-17 | `npc48` 中的 6 个高门(319–324,48px 高)与 32px NPC 是否可混入同一目标图(受 D-72④「不同帧数不得同图」约束,§5.2)。 | 用户确认高门是否单独成图 | +| U-18 | `blocksInfo` 中无 `None` / `Tileset` 类图块定义(§1.1);空图块(num 0)在 `map` 中大量出现但不在 `blocksInfo`。 | 新引擎对空图块 / tileset 图块的定义方式 | +| U-19 | §7.1 中 `autotile` 条目的 `AutotileType` 参数与条目判别字段 `type` **同名**,无法同时以 `type` 承载二者(§7.1)。 | 用户确认该参数字段的最终命名(候选名 `autotileType`,待用户报备批准)(OI-2 的一部分) | + +> **U-19 说明:** 此为**命名层面的观察**(不登记为缺陷)—— 判别字段 `type`(取 `'grid'` / `'rowAnimate'` / `'autotile'` / `'tileset'`)与 `addAutotile` 的 `type: AutotileType` 参数冲突;§7.1 暂以候选字段名 `autotileType` 消歧,最终命名待用户裁定。 + +## 9. 汇总 + +| 维度 | 计数 | 说明 | +| --- | --- | --- | +| 图块 tile | 235 | `public/project/maps.js` 的 `blocksInfo`(8 个 `cls`) | +| 贴图素材 textures | 17 | materials 12 + autotiles 4 + tilesets 1 | +| 素材拆分目标图 split | 42 | D-72 四类推导(terrains/items/enemy/npc);**`animates` 并入 `terrains`,不设独立 `animates` 素材组**(D-72③) | +| 怪物 enemy | 81 | `public/project/enemys.js`(与 `enemys` 73 + `enemy48` 8 图块一一对应) | +| 道具 items | 62 | `public/project/items.js`(`tools` 20 / `items` 32 / `equips` 2 / `constants` 8) | +| NPC | 24 | `npcs` 14 + `npc48` 10(合并为 `npc`) | +| 地图 maps | 5 | `public/project/floors/*.js` | + +机器可读计数行: + +``` +TILE-COUNT: 235 +MATERIAL-COUNT: 17 +SPLIT-COUNT: 42 +ENEMY-COUNT: 81 +ITEM-COUNT: 62 +NPC-COUNT: 24 +MAP-COUNT: 5 +``` + +> **命名说明:** 本盘点中提出的所有候选命名 —— 拆分后贴图文件名 `terrains*/items*/enemy*/npc*`、`addTileset` / `addAutotile` 的 `identifier` 与参数名、`texture/.list.jsonc` 字段名 `grid` / `rowAnimate` / `autotile` / `tileset`、**§1.5 的新 `num` 分配策略候选(策略 A / B / C / D)**、**§7.1 的显式 TypeScript 类型名(`TextureListSource` / `ITextureListGridEntry` / `ITextureListRowAnimateEntry` / `ITextureListAutotileEntry` / `ITextureListTilesetEntry` / `TextureListEntry` / `ITextureListConfig` 及其字段)** —— **均为「命名候选」,标注「待用户报备批准」,本计划不落地使用**(D-73 / OI-1 / OI-2)。**`animates` 并入 `terrains`,不设独立 `animates` 素材组**(D-72③)。§8 的未确定项均**未登记为缺陷**,仅作待确认观察,待用户裁定后方可另立计划。