From aee8627e9532d5be4b07917c95458b802115a80b Mon Sep 17 00:00:00 2001
From: unanmed <1319491857@qq.com>
Date: Mon, 26 Oct 2026 20:36:00 +0800
Subject: [PATCH] =?UTF-8?q?docs(10-33):=20=E9=AB=98=E7=BA=A7=20I=20?=
=?UTF-8?q?=E7=A7=BB=E5=85=A5=20i/=20=E5=B9=B6=E9=87=8D=E5=BB=BA=E6=80=BB?=
=?UTF-8?q?=E5=AF=BC=E8=A7=88=E4=B8=8E=E4=BE=A7=E8=BE=B9=E6=A0=8F?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 九个高级 I 页面 git mv 至 docs/advanced/i/
- docs/advanced/index.md 重建为高级教程总导览(III 暂不提供链接)
- 侧边栏新增总导览入口,I 组九项改指 /advanced/i/
- 修正 I 页与高级 II 入链的跨层相对路径
---
docs/.vitepress/config.ts | 19 +--
docs/advanced/{ => i}/data-stores.md | 2 +-
.../{ => i}/declarative-vs-imperative.md | 6 +-
docs/advanced/{ => i}/face-handler.md | 2 +-
docs/advanced/{ => i}/hooks-in-depth.md | 2 +-
docs/advanced/i/index.md | 106 ++++++++++++++++
docs/advanced/{ => i}/mover.md | 6 +-
docs/advanced/{ => i}/plugin-system.md | 2 +-
docs/advanced/{ => i}/replay-consistency.md | 4 +-
docs/advanced/{ => i}/saveable-content.md | 4 +-
docs/advanced/ii/custom-replay-command.md | 4 +-
docs/advanced/ii/index.md | 12 +-
docs/advanced/ii/static-vs-dynamic-tiles.md | 2 +-
docs/advanced/index.md | 113 ++++--------------
14 files changed, 163 insertions(+), 121 deletions(-)
rename docs/advanced/{ => i}/data-stores.md (97%)
rename docs/advanced/{ => i}/declarative-vs-imperative.md (98%)
rename docs/advanced/{ => i}/face-handler.md (99%)
rename docs/advanced/{ => i}/hooks-in-depth.md (97%)
create mode 100644 docs/advanced/i/index.md
rename docs/advanced/{ => i}/mover.md (99%)
rename docs/advanced/{ => i}/plugin-system.md (98%)
rename docs/advanced/{ => i}/replay-consistency.md (96%)
rename docs/advanced/{ => i}/saveable-content.md (98%)
diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts
index c51d483..8c57a55 100644
--- a/docs/.vitepress/config.ts
+++ b/docs/.vitepress/config.ts
@@ -152,39 +152,40 @@ export default defineConfig({
}
],
'/advanced/': [
+ { text: '高级教程总导览', link: '/advanced/' },
{
text: '高级 I · 扩展基础',
collapsed: false,
items: [
- { text: '高级 I 导览', link: '/advanced/' },
+ { text: '高级 I 导览', link: '/advanced/i/' },
{
text: '声明式 vs 命令式接口',
- link: '/advanced/declarative-vs-imperative'
+ link: '/advanced/i/declarative-vs-imperative'
},
{
text: '插件系统',
- link: '/advanced/plugin-system'
+ link: '/advanced/i/plugin-system'
},
{
text: '可存档内容',
- link: '/advanced/saveable-content'
+ link: '/advanced/i/saveable-content'
},
{
text: '录像一致性',
- link: '/advanced/replay-consistency'
+ link: '/advanced/i/replay-consistency'
},
{
text: 'data-common 的 Store',
- link: '/advanced/data-stores'
+ link: '/advanced/i/data-stores'
},
{
text: '钩子深入',
- link: '/advanced/hooks-in-depth'
+ link: '/advanced/i/hooks-in-depth'
},
- { text: '移动 Mover', link: '/advanced/mover' },
+ { text: '移动 Mover', link: '/advanced/i/mover' },
{
text: 'FaceHandler 系列',
- link: '/advanced/face-handler'
+ link: '/advanced/i/face-handler'
}
]
},
diff --git a/docs/advanced/data-stores.md b/docs/advanced/i/data-stores.md
similarity index 97%
rename from docs/advanced/data-stores.md
rename to docs/advanced/i/data-stores.md
index 93279eb..dd68afa 100644
--- a/docs/advanced/data-stores.md
+++ b/docs/advanced/i/data-stores.md
@@ -2,7 +2,7 @@
`@user/data-common` 是数据端的 Layer 0 公共层,上面挂着五个内容注册表:`tileStore`、`itemStore`、`mapStore`、`enemyStore`、`eventStore`。它们看起来像「数据表」,实际是内容层原始定义的注册表——图块、道具、地图、怪物的定义登记在这里,游戏运行时从这里查出来,再按定义构造自己的对象;事件则直接以实例登记。
-中级教程的[数据端总览](../intermediate/data-state-overview.md)列过这些入口,[图块存储](../intermediate/tile-store.md)讲了 `tileStore` 的日常用法。这一篇逐个讲每个 Store 存的是什么、有哪些常用成员、它自己能做什么,以及为什么运行期只该读。
+中级教程的[数据端总览](../../intermediate/data-state-overview.md)列过这些入口,[图块存储](../../intermediate/tile-store.md)讲了 `tileStore` 的日常用法。这一篇逐个讲每个 Store 存的是什么、有哪些常用成员、它自己能做什么,以及为什么运行期只该读。
阅读前需要会基础语法:`class`、`new`、`Map`、`import`。
diff --git a/docs/advanced/declarative-vs-imperative.md b/docs/advanced/i/declarative-vs-imperative.md
similarity index 98%
rename from docs/advanced/declarative-vs-imperative.md
rename to docs/advanced/i/declarative-vs-imperative.md
index c2a4b14..5db0c6d 100644
--- a/docs/advanced/declarative-vs-imperative.md
+++ b/docs/advanced/i/declarative-vs-imperative.md
@@ -41,7 +41,7 @@ function healHero(hero: IHeroState): void {
- 只在执行到的那一瞬间生效。脚本没被触发,效果就不存在;触发第二次,效果就再来一次。
- 想重新计算只能重跑。怪物移动、周围条件变化之后,旧数值不会自动更新;再跑一遍脚本,又容易把加成叠加两次。
- 每条路径都要自己同步。存档、读档、录像回放、重新进入楼层,这些路径都可能绕开你写的那段脚本,于是状态对不上。
-- 没法凭空给怪物加特殊属性。直接改 `atk` 只是改了一个数字,不能让怪物获得一条领域属性;特殊属性本身的写法见[特殊属性](../intermediate/enemy-specials.md)。
+- 没法凭空给怪物加特殊属性。直接改 `atk` 只是改了一个数字,不能让怪物获得一条领域属性;特殊属性本身的写法见[特殊属性](../../intermediate/enemy-specials.md)。
这不是说命令式一无是处。玩家点一下、走一步、用一次道具,本来就是「现在立刻发生」的动作,用命令式接口最直接。问题只出在:把持续生效的规则也写成了一次性命令。
@@ -90,7 +90,7 @@ export class MyDamageCalculator implements IDamageCalculator<
为什么这比命令式更稳:你只描述 `f(enemy, hero)` 这一条规则,所有调用点共用同一份结果。不需要在「显示」和「结算」两处各写一遍公式,也不会出现显示一个数、实际打另一个数。代价是计算器必须无副作用,下面会单独解释。
-改公式与注册的完整做法见[伤害计算](../intermediate/enemy-calculator.md)。
+改公式与注册的完整做法见[伤害计算](../../intermediate/enemy-calculator.md)。
### 特殊属性与光环
@@ -98,7 +98,7 @@ export class MyDamageCalculator implements IDamageCalculator<
引擎在哪个时机执行:构建「计算后怪物」时。`state.enemyContext.buildup()` 按固定顺序重建每只怪物,把特殊属性、查询与最终效果写进属性。
-为什么这比命令式更稳:每次重建都从模板的原始值出发,反复重建不会让加成越叠越多;你改的是注册表里的一条定义,所有读取计算后怪物的场景都会自动看到新结果;存档也只需要保存模板。更细的构建流程与特殊属性写法见[战斗总览](../intermediate/combat-overview.md)与[特殊属性](../intermediate/enemy-specials.md)。
+为什么这比命令式更稳:每次重建都从模板的原始值出发,反复重建不会让加成越叠越多;你改的是注册表里的一条定义,所有读取计算后怪物的场景都会自动看到新结果;存档也只需要保存模板。更细的构建流程与特殊属性写法见[战斗总览](../../intermediate/combat-overview.md)与[特殊属性](../../intermediate/enemy-specials.md)。
### 道具效果
diff --git a/docs/advanced/face-handler.md b/docs/advanced/i/face-handler.md
similarity index 99%
rename from docs/advanced/face-handler.md
rename to docs/advanced/i/face-handler.md
index 3efe3f6..456bf04 100644
--- a/docs/advanced/face-handler.md
+++ b/docs/advanced/i/face-handler.md
@@ -109,7 +109,7 @@ function targetOf(
移动器构造时保存一个处理器,每一步用它把 `moveDirection` 换成落点偏移。内置的 `DynamicTile` 构造自己的 `DynamicTileMover` 时,从 `state.faceManager` 按 `DYNAMIC_MOVER_FACE`(即 `Dir8`)取处理器;自己写移动器时,处理器要作为构造参数传进去(见[移动 Mover](./mover.md#实现自己的移动器))。处理器必须与对象的方向体系一致:四方向对象配八方向处理器,或反过来,偏移与朝向都会对不上。
-寻路复用同一套方向规则。构建有向图时,它按 `mapMovement()` 的条目向相邻格扩展,所以处理器直接决定「能往哪几个方向走」:四方向处理器只扩展上下左右,八方向处理器会扩展到斜向。注入方式是 `finder.useFaceHandler(handler)`;没有注入时构图会失败(找不到图层、谓词或处理器都会失败),详见[自动寻路](./ii/pathfinding.md)。
+寻路复用同一套方向规则。构建有向图时,它按 `mapMovement()` 的条目向相邻格扩展,所以处理器直接决定「能往哪几个方向走」:四方向处理器只扩展上下左右,八方向处理器会扩展到斜向。注入方式是 `finder.useFaceHandler(handler)`;没有注入时构图会失败(找不到图层、谓词或处理器都会失败),详见[自动寻路](../ii/pathfinding.md)。
因为「方向即偏移」被多处共用,改处理器的行为会同时影响移动与寻路:调整四方向处理器的斜向归并,移动器的斜向输入和寻路的斜向分支会一起改变。只希望其中一处变化时,不要直接换掉共享的处理器,而是给那处单独注入一个处理器实例。
diff --git a/docs/advanced/hooks-in-depth.md b/docs/advanced/i/hooks-in-depth.md
similarity index 97%
rename from docs/advanced/hooks-in-depth.md
rename to docs/advanced/i/hooks-in-depth.md
index 6bbec20..73fb22a 100644
--- a/docs/advanced/hooks-in-depth.md
+++ b/docs/advanced/i/hooks-in-depth.md
@@ -1,6 +1,6 @@
# 钩子深入
-中级教程的[钩子](../intermediate/hooks.md)回答的是「什么需求该用哪个钩子」;这一篇讲另一件事:怎么把钩子限定到你关心的那个对象上。做法很简单——建议一个对象一个钩子实例。共享实例是允许的,但不建议;除非你明确了解钩子的内部实现与后果,否则不要这样做。
+中级教程的[钩子](../../intermediate/hooks.md)回答的是「什么需求该用哪个钩子」;这一篇讲另一件事:怎么把钩子限定到你关心的那个对象上。做法很简单——建议一个对象一个钩子实例。共享实例是允许的,但不建议;除非你明确了解钩子的内部实现与后果,否则不要这样做。
钩子接口本身不难,容易出错的是「挂上去了,触发的却不只是你想管的那张地图、那个图层」。下面先讲实例与对象的关系,再给一个完整示例,最后是注册、移除与异步的接口事实。
diff --git a/docs/advanced/i/index.md b/docs/advanced/i/index.md
new file mode 100644
index 0000000..506d5d4
--- /dev/null
+++ b/docs/advanced/i/index.md
@@ -0,0 +1,106 @@
+# 高级 I 导览
+
+高级教程写给已经能独立使用中级接口、现在想按自己的规则扩展数据端的人。到了这一层,问题不再是「这个接口怎么调」,而是「这个需求该放在哪一层、该由谁在什么时机执行」。弄错这一层,效果可能当下能跑,却会在存档、读档或录像回放时对不上。
+
+这一页说明高级教程分几层、高级 I 包含哪些篇目、先读什么、以及一条贯穿全层的取舍原则:新增系统优先用插件,少改内置。
+
+## 高级教程是给谁的
+
+高级教程面向有高度定制需求的作者:
+
+- 你要新增一套有自己状态、生命周期和存档内容的系统,而不只是改一个数值;
+- 你要理解扩展机制本身,知道声明式接口、插件、存档与录像之间怎么配合;
+- 你愿意读一点源码,但不需要成为引擎的编写者。
+
+它不要求你改造引擎内部。高级教程的目标是同时覆盖数据端与渲染端;渲染端尚未完工,当前只写数据端内容,渲染端部分只用占位标记指出归属。所以高级 I 的例子都落在数据端:你写的内容、你注册的对象、你声明的规则。
+
+## 高级教程的三层
+
+高级教程按难度与主题分成三层。层与顶层的中级教程不是同一套划分:中级讲「怎么用」,高级讲「怎么扩展」。
+
+| 层级 | 定位 | 主要主题 |
+| ---------------------------- | -------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
+| 高级 I · 扩展基础 | 以插件系统为核心,后续内容都是它的拓展 | 声明式接口、插件系统、可存档内容、录像一致性、data-common 的 Store、钩子深入、移动 Mover、FaceHandler 系列 |
+| [高级 II · 深入定制](../ii/) | 深入定制 | 范围、图块、装备、录像指令、寻路、勇士与怪物属性、战斗上下文 |
+| 高级 III · 原理与架构 | 原理与架构 | 双端分离约束、数据端 L0–L3、编辑器识别协议、录像深层原理、性能与调试 |
+
+高级 II 已经可阅读,入口见[高级 II 导览](../ii/);本层收录范围、图块、装备、录像指令、寻路、属性与战斗上下文。高级 III 仍用 `高级 III · <专题>` 这样的占位标记指出归属,暂不提供页面入口。
+
+## 高级 I 包含什么
+
+高级 I 一共九篇,包含本导览与八篇正文。
+
+| 序 | 篇目 | 它能让你做什么 |
+| --- | -------------------- | ------------------------------------------------------------ |
+| 1 | 高级 I 导览 | 看清分层、阅读路径与取舍原则,决定先读哪篇 |
+| 2 | 声明式 vs 命令式接口 | 分清两种思路,知道持续生效的规则与一次性动作各该写在哪 |
+| 3 | 插件系统 | 独立写出插件,分清数据端与渲染端的依赖和生命周期 |
+| 4 | 可存档内容 | 让自定义状态正确进入存档,并在读档后恢复到存档时的样子 |
+| 5 | 录像一致性 | 判断数据端哪些方法该登记、哪些路径不该记,以及检查能保证什么 |
+| 6 | data-common 的 Store | 知道五个 Store 各存什么、能做什么,以及为什么运行时只读 |
+| 7 | 钩子深入 | 把钩子限定到一个对象上,建议一个对象一个钩子实例 |
+| 8 | 移动 Mover | 能排出一次移动、等它走完,并在需要时自己实现移动器 |
+| 9 | FaceHandler 系列 | 能选对朝向处理器、算出目标格与反方向,并把朝向映射到图块数字 |
+
+推荐阅读顺序:声明式 vs 命令式 → 插件系统 → 可存档内容 → 录像一致性 → data-common 的 Store → 钩子深入。移动 Mover 与 FaceHandler 系列偏参考性质,等要写移动或朝向逻辑时再读;它们各自独立,不依赖前面的顺序。
+
+插件系统是这一层的核心,可存档内容、录像一致性、data-common 的 Store 与钩子深入都建立在它提供的扩展位置上。也可以按需求跳读,但建议至少先读前两篇:它们决定你是在「声明一条规则」,还是在「写一段一次性脚本」。
+
+## 阅读路径与前置
+
+高级 I 假设你已经读完中级教程,尤其是数据端与渲染端、代码基础、数据插件这三篇;数据端总览、钩子、录像指令也会在具体页面里被直接引用。前置篇目的链接放在本页末尾。
+
+按目标不同,可以走两条路径:
+
+- 做扩展系统:声明式 → 插件 → 可存档内容 → 录像一致性。适合新增任务、商店、计数器这类有自己状态与生命周期的功能。读完能独立装配一个插件,并让它的数据跟着存档走。
+- 改内容或机制:声明式 → data-common 的 Store → 钩子深入,按需接上移动 Mover 或 FaceHandler 系列。适合在现有机制上加行为、按接口读取内容定义。读完能分清哪些只需要读、哪些由数据端登记,也能把钩子限定到目标对象上;要改移动或朝向规则时,知道该换哪个扩展点。
+
+两条路径都建议先读声明式篇。它决定你后面是在「加一条规则」,还是「写一段一次性脚本」;这个判断错了,插件、存档与录像都会跟着别扭。
+
+## 优先用插件,少改内置
+
+新增系统优先通过插件系统实现;只有不得不修改内置系统时,才去改内部。理由很现实:版本迁移时 `src` 基本不会改,而内置包(`packages` / `packages-user`)有可能会改。直接改内置,升级时就要跟着差异走,维护成本更高;把改动留在自己的 `src` 里,升级时要对照的地方少得多。
+
+| 需求类型 | 优先做法 | 什么情况下才改内置 |
+| -------------------------------------------- | ------------------------------------------ | ---------------------------------------------------------------------------------------- |
+| 新增一套有自己状态、生命周期与存档内容的系统 | 写成插件,在插件里装配对象 | 现有插件已覆盖大部分能力,只差一个接口或默认值时 |
+| 给已有系统补一点行为 | 改内置类的方法体,或加一条分支 | 需要替换整套策略、或改动会破坏内置行为时,才独立成类 |
+| 读取内容定义(图块、道具、地图、怪物、事件) | 通过 `state.*Store` 的接口读,不新建 Store | 现有 Store 装不下,且确实需要新的系统级数据表时 |
+| 给会改状态的方法补录像标记 | 在内置方法上加一行装饰器 | 需要新的录像入口或自定义录像行为时(见[自定义录像指令](../ii/custom-replay-command.md)) |
+
+判断标准不是改动多少行,而是改动落在谁的代码里:能由插件或内容定义完成的,就留在 `src`;必须动内置包时,尽量把差异收敛在一个方法或一条分支上,方便升级时对照。
+
+## 本层不覆盖什么
+
+- 渲染端内容:渲染端尚未完工,高级教程当前只收录数据端内容,渲染端专题待完工后补上。
+- 高级 II 的深入定制内容:范围、图块、装备、录像指令、寻路、勇士与怪物属性、战斗上下文,见[高级 II 导览](../ii/)。
+- 高级 III 的原理与架构:双端分离约束、数据端 L0–L3、编辑器识别协议、录像深层原理、性能与调试。
+- API 参考:接口签名与类型清单不属于教程正文。
+
+## 读完这一层应该能做什么
+
+- 能用自己的话解释声明式与命令式的区别,并说出一个「仍然该用命令式」的场景。
+- 能拿到一个需求,判断它该做成插件,还是按现有接口读取即可。
+- 能说出一个可存档对象必须满足的三个约束:只存重建必需的数据、`saveState` 返回独立对象(引擎不替你深拷贝)、`loadState` 先清空再重建。
+- 能说明为什么建议一个对象一个钩子实例,并在回调拿到多个对象时用对象引用筛出目标。
+- 能排出一次移动、等到它结束,并在需要时实现自己的移动器或勇士顶层实现。
+- 能选对朝向处理器、算出目标格与反方向,并把朝向映射到图块数字。
+
+## 继续阅读
+
+高级 I 正文:
+
+- [声明式 vs 命令式接口](./declarative-vs-imperative.md)
+- [插件系统](./plugin-system.md)
+- [可存档内容](./saveable-content.md)
+- [录像一致性](./replay-consistency.md)
+- [data-common 的 Store](./data-stores.md)
+- [钩子深入](./hooks-in-depth.md)
+- [移动 Mover](./mover.md)
+- [FaceHandler 系列](./face-handler.md)
+
+前置回顾(中级教程):
+
+- [数据端与渲染端](../../intermediate/data-vs-render.md)
+- [代码基础](../../intermediate/code-basics.md)
+- [数据插件](../../intermediate/data-plugins.md)
diff --git a/docs/advanced/mover.md b/docs/advanced/i/mover.md
similarity index 99%
rename from docs/advanced/mover.md
rename to docs/advanced/i/mover.md
index 85c6cdc..1cb5f89 100644
--- a/docs/advanced/mover.md
+++ b/docs/advanced/i/mover.md
@@ -49,7 +49,7 @@ async function heroStepRight(state: ICoreState): Promise {
}
```
-勇士位置对象本身的接口(坐标、楼层、朝向、存档)见[勇士位置](../intermediate/hero-location.md)。
+勇士位置对象本身的接口(坐标、楼层、朝向、存档)见[勇士位置](../../intermediate/hero-location.md)。
### 动态图块
@@ -70,7 +70,7 @@ async function pushTileRight(layer: IMapLayer): Promise {
}
```
-`layer.createDynamic(num, x, y)` 直接新建动态图块,`layer.getDynamicTilesAt(x, y)` 与 `layer.iterateDynamicTiles()` 用来找已存在的动态图块。只是推一格这类简单场景,用 `tile.step(dir, count)` 更省事,它内部就是 `mover.step(...)` 加 `mover.start()`。创建、转换与移动的完整说明见[静态图块与动态图块](./ii/static-vs-dynamic-tiles.md)。
+`layer.createDynamic(num, x, y)` 直接新建动态图块,`layer.getDynamicTilesAt(x, y)` 与 `layer.iterateDynamicTiles()` 用来找已存在的动态图块。只是推一格这类简单场景,用 `tile.step(dir, count)` 更省事,它内部就是 `mover.step(...)` 加 `mover.start()`。创建、转换与移动的完整说明见[静态图块与动态图块](../ii/static-vs-dynamic-tiles.md)。
### 其他可移动对象
@@ -453,7 +453,7 @@ state.hero.location.mover.useTopImplementation(new NoLeftMoveTopImpl(state));
- `predicate()` 在每个方向步都会被调用,要返回构造时建好的稳定实例,不要在方法里新建对象(内置实现就是把 `DefaultPassPredicateImpl` 存在字段里)。
- `config({ ignoreTerrain: true })` 会让 `HeroMover` 跳过整个谓词判断、只剩边界检查,此时改谓词看不到效果。
-- 寻路用的是同一形状的谓词,但由 `state.pathfinding.finder.usePassPredicate(...)` 独立注入,不会自动跟随勇士的顶层实现。改了勇士的通行规则、又希望寻路一致时,要把同一个谓词再注入给 `finder`,详见[自动寻路](./ii/pathfinding.md)。
+- 寻路用的是同一形状的谓词,但由 `state.pathfinding.finder.usePassPredicate(...)` 独立注入,不会自动跟随勇士的顶层实现。改了勇士的通行规则、又希望寻路一致时,要把同一个谓词再注入给 `finder`,详见[自动寻路](../ii/pathfinding.md)。
## 边界与注意
diff --git a/docs/advanced/plugin-system.md b/docs/advanced/i/plugin-system.md
similarity index 98%
rename from docs/advanced/plugin-system.md
rename to docs/advanced/i/plugin-system.md
index fb80928..f9f5cf9 100644
--- a/docs/advanced/plugin-system.md
+++ b/docs/advanced/i/plugin-system.md
@@ -1,6 +1,6 @@
# 插件系统
-如果你已经用过中级教程里的[数据插件](../intermediate/data-plugins.md),你会知道插件能把一组功能打包、挂到 `state` 上,并按固定时机初始化。那一篇停在「会用」;这一篇讲怎么自己写一个插件、让插件彼此协作,以及怎么在插件之外调用它。
+如果你已经用过中级教程里的[数据插件](../../intermediate/data-plugins.md),你会知道插件能把一组功能打包、挂到 `state` 上,并按固定时机初始化。那一篇停在「会用」;这一篇讲怎么自己写一个插件、让插件彼此协作,以及怎么在插件之外调用它。
插件分数据端与渲染端两套,写法、注册方式与生命周期一一对应:数据端插件负责逻辑与状态,渲染端插件负责画面与交互。渲染端插件可以依赖数据端插件,反过来不行。下面两边并列讲,遇到不同的地方单独说明。
diff --git a/docs/advanced/replay-consistency.md b/docs/advanced/i/replay-consistency.md
similarity index 96%
rename from docs/advanced/replay-consistency.md
rename to docs/advanced/i/replay-consistency.md
index 3496433..d1ca126 100644
--- a/docs/advanced/replay-consistency.md
+++ b/docs/advanced/i/replay-consistency.md
@@ -2,9 +2,9 @@
录像验证的用法是:把玩家的操作录下来,之后只跑数据端、按顺序重放,看结果与原始游玩是否一致。要让这件事成立,前提是该记的行为一条都不能少。少记一条,重放时那一步就没有发生,之后每一步都建立在错误的状态上,而第一次出现差异的位置,往往离真正的原因很远。
-中级教程的[录像指令](../intermediate/replay-commands.md)讲了录像是什么、主动与被动的区别、以及怎么新增一条指令。这一篇往下一层:`@shouldReplay` 与 `@ignoreReplay` 这两个装饰器该写在什么方法上,它们能替你查出什么、查不出什么。
+中级教程的[录像指令](../../intermediate/replay-commands.md)讲了录像是什么、主动与被动的区别、以及怎么新增一条指令。这一篇往下一层:`@shouldReplay` 与 `@ignoreReplay` 这两个装饰器该写在什么方法上,它们能替你查出什么、查不出什么。
-阅读前需要会基础语法:`class`、`new`、`import`,以及装饰器写法 `@名字(...)`。装饰器的基础写法见[代码基础](../intermediate/code-basics.md)。
+阅读前需要会基础语法:`class`、`new`、`import`,以及装饰器写法 `@名字(...)`。装饰器的基础写法见[代码基础](../../intermediate/code-basics.md)。
## 什么该记
diff --git a/docs/advanced/saveable-content.md b/docs/advanced/i/saveable-content.md
similarity index 98%
rename from docs/advanced/saveable-content.md
rename to docs/advanced/i/saveable-content.md
index 0a623ee..48763d8 100644
--- a/docs/advanced/saveable-content.md
+++ b/docs/advanced/i/saveable-content.md
@@ -2,9 +2,9 @@
存档不是「把内存里的东西倒进文件」,而是游戏状态的一份快照。读档之后,游戏必须从这份快照继续,按同一套规则运行:玩家位置、地图改动、Flag、怪物状态、插件自己的计数,都要回到存档那一刻。只要有一处没回到该有的样子,后面就会逐渐偏掉,而且往往要到很久以后才表现出来。
-中级教程里的 [Flag 变量](../intermediate/flags.md) 已经替你存好了一类简单状态。这一篇讲更一般的情况:当你要自己定义一类需要进存档的数据时,怎么声明、怎么注册、怎么保证读档后的结果和存档时一致。
+中级教程里的 [Flag 变量](../../intermediate/flags.md) 已经替你存好了一类简单状态。这一篇讲更一般的情况:当你要自己定义一类需要进存档的数据时,怎么声明、怎么注册、怎么保证读档后的结果和存档时一致。
-阅读前需要会基础语法:`class`、`new`、`Map`、`import`。如果你还没写过插件,建议先看[数据插件](../intermediate/data-plugins.md)与[插件系统](./plugin-system.md);可存档内容通常就注册在插件的 `awake` 里。
+阅读前需要会基础语法:`class`、`new`、`Map`、`import`。如果你还没写过插件,建议先看[数据插件](../../intermediate/data-plugins.md)与[插件系统](./plugin-system.md);可存档内容通常就注册在插件的 `awake` 里。
## 什么该存,什么不该存
diff --git a/docs/advanced/ii/custom-replay-command.md b/docs/advanced/ii/custom-replay-command.md
index aac3fcc..d4a1ab1 100644
--- a/docs/advanced/ii/custom-replay-command.md
+++ b/docs/advanced/ii/custom-replay-command.md
@@ -1,8 +1,8 @@
# 自定义录像指令
-高级 I 的[录像一致性](../replay-consistency.md)讲的是「已有行为该不该记」,回答方式是给方法加 `@shouldReplay`。还有一种情况它覆盖不到:行为本身不在引擎里,录像里没有对应的指令码,回放时根本无从执行。这时需要新增一条录像指令。
+高级 I 的[录像一致性](../i/replay-consistency.md)讲的是「已有行为该不该记」,回答方式是给方法加 `@shouldReplay`。还有一种情况它覆盖不到:行为本身不在引擎里,录像里没有对应的指令码,回放时根本无从执行。这时需要新增一条录像指令。
-本篇讲一条自定义指令从指令码、指令对象、注册到记录的完整路径,并用技能给出完整示例。主动步与被动步的配合会先用选择框说明。新增指令码会修改 `packages-user` 下的引擎内容,升级时要预期合并成本;如果只是给已有行为补标记,回到[录像一致性](../replay-consistency.md)即可,不必新增指令。
+本篇讲一条自定义指令从指令码、指令对象、注册到记录的完整路径,并用技能给出完整示例。主动步与被动步的配合会先用选择框说明。新增指令码会修改 `packages-user` 下的引擎内容,升级时要预期合并成本;如果只是给已有行为补标记,回到[录像一致性](../i/replay-consistency.md)即可,不必新增指令。
## 什么时候需要自定义指令
diff --git a/docs/advanced/ii/index.md b/docs/advanced/ii/index.md
index a46bf12..6b658ea 100644
--- a/docs/advanced/ii/index.md
+++ b/docs/advanced/ii/index.md
@@ -32,12 +32,12 @@
高级 II 假设你已经读完高级 I,尤其是下面四篇:
-- [声明式 vs 命令式接口](../declarative-vs-imperative.md):决定你是在声明规则,还是写一次性脚本;
-- [插件系统](../plugin-system.md):新增系统优先放在插件里,而不是直接改内置;
-- [可存档内容](../saveable-content.md):新增状态要能进入存档,并在读档后恢复;
-- [录像一致性](../replay-consistency.md):会改变游戏进程的方法要考虑是否登记录像。
+- [声明式 vs 命令式接口](../i/declarative-vs-imperative.md):决定你是在声明规则,还是写一次性脚本;
+- [插件系统](../i/plugin-system.md):新增系统优先放在插件里,而不是直接改内置;
+- [可存档内容](../i/saveable-content.md):新增状态要能进入存档,并在读档后恢复;
+- [录像一致性](../i/replay-consistency.md):会改变游戏进程的方法要考虑是否登记录像。
-同层的 [data-common 的 Store](../data-stores.md) 与 [钩子深入](../hooks-in-depth.md) 也会被反复引用:前者决定你从哪里读内容定义,后者决定你在什么时机插入行为。
+同层的 [data-common 的 Store](../i/data-stores.md) 与 [钩子深入](../i/hooks-in-depth.md) 也会被反复引用:前者决定你从哪里读内容定义,后者决定你在什么时机插入行为。
中级教程里的相关篇目同样重要,按主题列在下面:
@@ -125,4 +125,4 @@
- [新增怪物属性](./adding-enemy-attribute.md)
- [怪物上下文与战斗深入](./enemy-context-and-combat.md)
-返回[高级 I 导览](../index.md)。
+返回[高级 I 导览](../i/index.md)。
diff --git a/docs/advanced/ii/static-vs-dynamic-tiles.md b/docs/advanced/ii/static-vs-dynamic-tiles.md
index fa98d68..26a321e 100644
--- a/docs/advanced/ii/static-vs-dynamic-tiles.md
+++ b/docs/advanced/ii/static-vs-dynamic-tiles.md
@@ -146,7 +146,7 @@ async function shiftRight(
### 移动器 mover
-`transferToDynamic` 得到的动态图块已经带有自己的 `mover`,之后的位置变更都由它发起;`step` 只是 mover 上的单步便捷入口。`mover` 的接口、速度与路径配置、`onEnd` 等待等细节属于高级 I 的内容,见[高级 I · 移动 Mover](../mover.md),本篇不重复。
+`transferToDynamic` 得到的动态图块已经带有自己的 `mover`,之后的位置变更都由它发起;`step` 只是 mover 上的单步便捷入口。`mover` 的接口、速度与路径配置、`onEnd` 等待等细节属于高级 I 的内容,见[高级 I · 移动 Mover](../i/mover.md),本篇不重复。
### moveBlock 简写
diff --git a/docs/advanced/index.md b/docs/advanced/index.md
index 4121460..92a33d5 100644
--- a/docs/advanced/index.md
+++ b/docs/advanced/index.md
@@ -1,106 +1,41 @@
-# 高级 I 导览
+# 高级教程总导览
-高级教程写给已经能独立使用中级接口、现在想按自己的规则扩展数据端的人。到了这一层,问题不再是「这个接口怎么调」,而是「这个需求该放在哪一层、该由谁在什么时机执行」。弄错这一层,效果可能当下能跑,却会在存档、读档或录像回放时对不上。
+高级教程写给有高度定制需求、并且愿意理解引擎设计的作者。到了这一层,问题不再是「这个接口怎么调」,而是「这个需求该放在哪一层、该由谁在什么时机执行」。弄错这一层,效果可能当下能跑,却会在存档、读档或录像回放时对不上。
-这一页说明高级教程分几层、高级 I 包含哪些篇目、先读什么、以及一条贯穿全层的取舍原则:新增系统优先用插件,少改内置。
+本层按难度分成 I / II / III 三层;分层是难度标签,不是领域分类,具体主题可能在层间调整。这一页说明三层的定位与主题、怎么选阅读路径、需要哪些前置,以及本层与中级教程的分工。
-## 高级教程是给谁的
-
-高级教程面向有高度定制需求的作者:
-
-- 你要新增一套有自己状态、生命周期和存档内容的系统,而不只是改一个数值;
-- 你要理解扩展机制本身,知道声明式接口、插件、存档与录像之间怎么配合;
-- 你愿意读一点源码,但不需要成为引擎的编写者。
-
-它不要求你改造引擎内部。高级教程的目标是同时覆盖数据端与渲染端;渲染端尚未完工,当前只写数据端内容,渲染端部分只用占位标记指出归属。所以高级 I 的例子都落在数据端:你写的内容、你注册的对象、你声明的规则。
-
-## 高级教程的三层
-
-高级教程按难度与主题分成三层。层与顶层的中级教程不是同一套划分:中级讲「怎么用」,高级讲「怎么扩展」。
+## 三层结构
| 层级 | 定位 | 主要主题 |
| --------------------------- | -------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
-| 高级 I · 扩展基础 | 以插件系统为核心,后续内容都是它的拓展 | 声明式接口、插件系统、可存档内容、录像一致性、data-common 的 Store、钩子深入、移动 Mover、FaceHandler 系列 |
-| [高级 II · 深入定制](./ii/) | 深入定制 | 范围、图块、装备、录像指令、寻路、勇士与怪物属性、战斗上下文 |
-| 高级 III · 原理与架构 | 原理与架构 | 双端分离约束、数据端 L0–L3、编辑器识别协议、录像深层原理、性能与调试 |
+| [高级 I · 扩展基础](./i/) | 较低门槛,核心是插件系统与基本扩展机制 | 声明式接口、插件系统、可存档内容、录像一致性、data-common 的 Store、钩子深入、移动 Mover、FaceHandler 系列 |
+| [高级 II · 深入定制](./ii/) | 中等门槛,需要理解并修改引擎的特定系统 | 范围对象、图块、装备、录像指令、寻路、勇士与怪物属性、战斗上下文 |
+| 高级 III · 原理与架构 | 最高门槛,系统讲解设计原理与架构 | 双端分离约束、数据端 L0–L3、编辑器识别协议、录像深层原理、性能与调试 |
-高级 II 已经可阅读,入口见[高级 II 导览](./ii/);本层收录范围、图块、装备、录像指令、寻路、属性与战斗上下文。高级 III 仍用 `高级 III · <专题>` 这样的占位标记指出归属,暂不提供页面入口。
+高级 I 是入口层,插件系统是这一层的核心,后面的可存档内容、录像一致性、data-common 的 Store 与钩子深入都建立在它提供的扩展位置上。高级 II 按主题深入单个系统,进入前应当先读完高级 I 的相关篇目。高级 III 讲的是支撑前面两层的原理与架构,等该层页面就绪后开放入口。
-## 高级 I 包含什么
+## 怎么选阅读路径
-高级 I 一共九篇,包含本导览与八篇正文。
+- 先 I 后 II、III 按需:默认路径是从高级 I 顺着读,把插件、存档、录像与钩子这四块打通;遇到需要改造某个具体系统时再进高级 II,想弄清背后设计时再进高级 III。这条路径适合第一次系统扩展数据端的作者。
+- 按目标跳读:已经有明确需求时,可以直接按主题跳到对应层。例如要新增一套带存档的系统,从高级 I 的插件系统与可存档内容读起;要新增录像指令或自定义寻路,直接进高级 II;要判断某个约束为什么存在,再查高级 III。跳读时如果对某个接口的日常用法不熟,先回中级教程补齐。
-| 序 | 篇目 | 它能让你做什么 |
-| --- | -------------------- | ------------------------------------------------------------ |
-| 1 | 高级 I 导览 | 看清分层、阅读路径与取舍原则,决定先读哪篇 |
-| 2 | 声明式 vs 命令式接口 | 分清两种思路,知道持续生效的规则与一次性动作各该写在哪 |
-| 3 | 插件系统 | 独立写出插件,分清数据端与渲染端的依赖和生命周期 |
-| 4 | 可存档内容 | 让自定义状态正确进入存档,并在读档后恢复到存档时的样子 |
-| 5 | 录像一致性 | 判断数据端哪些方法该登记、哪些路径不该记,以及检查能保证什么 |
-| 6 | data-common 的 Store | 知道五个 Store 各存什么、能做什么,以及为什么运行时只读 |
-| 7 | 钩子深入 | 把钩子限定到一个对象上,建议一个对象一个钩子实例 |
-| 8 | 移动 Mover | 能排出一次移动、等它走完,并在需要时自己实现移动器 |
-| 9 | FaceHandler 系列 | 能选对朝向处理器、算出目标格与反方向,并把朝向映射到图块数字 |
+## 前置
-推荐阅读顺序:声明式 vs 命令式 → 插件系统 → 可存档内容 → 录像一致性 → data-common 的 Store → 钩子深入。移动 Mover 与 FaceHandler 系列偏参考性质,等要写移动或朝向逻辑时再读;它们各自独立,不依赖前面的顺序。
+高级教程假设你已经读完中级教程,尤其是下面三篇:
-插件系统是这一层的核心,可存档内容、录像一致性、data-common 的 Store 与钩子深入都建立在它提供的扩展位置上。也可以按需求跳读,但建议至少先读前两篇:它们决定你是在「声明一条规则」,还是在「写一段一次性脚本」。
+- [数据端与渲染端](../intermediate/data-vs-render.md):理解「算」与「画」的分工,以及为什么录像验证只跑数据端;
+- [代码基础](../intermediate/code-basics.md):类、接口、模块与类型的基本写法;
+- [数据插件](../intermediate/data-plugins.md):插件是什么、怎么注册、生命周期大致如何。
-## 阅读路径与前置
+中级教程里的数据端总览、钩子、录像指令等篇目也会在具体页面里被直接引用;遇到不熟悉的接口时,回到对应中级篇目即可,本层不重复解释。
-高级 I 假设你已经读完中级教程,尤其是数据端与渲染端、代码基础、数据插件这三篇;数据端总览、钩子、录像指令也会在具体页面里被直接引用。前置篇目的链接放在本页末尾。
+## 与中级教程的分工
-按目标不同,可以走两条路径:
+中级教程讲「怎么用」:调用哪个接口、参数怎么填、常见的改法是什么。高级教程讲「怎么扩展」:新增的系统该放在哪一层、由谁在什么时机执行、怎样与存档和录像保持一致。同一件事在两层的深度不同,读高级时默认你已经会使用相关接口,因此这里只讲扩展点、取舍与边界,不重新罗列基础用法。
-- 做扩展系统:声明式 → 插件 → 可存档内容 → 录像一致性。适合新增任务、商店、计数器这类有自己状态与生命周期的功能。读完能独立装配一个插件,并让它的数据跟着存档走。
-- 改内容或机制:声明式 → data-common 的 Store → 钩子深入,按需接上移动 Mover 或 FaceHandler 系列。适合在现有机制上加行为、按接口读取内容定义。读完能分清哪些只需要读、哪些由数据端登记,也能把钩子限定到目标对象上;要改移动或朝向规则时,知道该换哪个扩展点。
+## 边界与注意
-两条路径都建议先读声明式篇。它决定你后面是在「加一条规则」,还是「写一段一次性脚本」;这个判断错了,插件、存档与录像都会跟着别扭。
-
-## 优先用插件,少改内置
-
-新增系统优先通过插件系统实现;只有不得不修改内置系统时,才去改内部。理由很现实:版本迁移时 `src` 基本不会改,而内置包(`packages` / `packages-user`)有可能会改。直接改内置,升级时就要跟着差异走,维护成本更高;把改动留在自己的 `src` 里,升级时要对照的地方少得多。
-
-| 需求类型 | 优先做法 | 什么情况下才改内置 |
-| -------------------------------------------- | ------------------------------------------ | --------------------------------------------------------------------------------------- |
-| 新增一套有自己状态、生命周期与存档内容的系统 | 写成插件,在插件里装配对象 | 现有插件已覆盖大部分能力,只差一个接口或默认值时 |
-| 给已有系统补一点行为 | 改内置类的方法体,或加一条分支 | 需要替换整套策略、或改动会破坏内置行为时,才独立成类 |
-| 读取内容定义(图块、道具、地图、怪物、事件) | 通过 `state.*Store` 的接口读,不新建 Store | 现有 Store 装不下,且确实需要新的系统级数据表时 |
-| 给会改状态的方法补录像标记 | 在内置方法上加一行装饰器 | 需要新的录像入口或自定义录像行为时(见[自定义录像指令](./ii/custom-replay-command.md)) |
-
-判断标准不是改动多少行,而是改动落在谁的代码里:能由插件或内容定义完成的,就留在 `src`;必须动内置包时,尽量把差异收敛在一个方法或一条分支上,方便升级时对照。
-
-## 本层不覆盖什么
-
-- 渲染端内容:渲染端尚未完工,高级教程当前只收录数据端内容,渲染端专题待完工后补上。
-- 高级 II 的深入定制内容:范围、图块、装备、录像指令、寻路、勇士与怪物属性、战斗上下文,见[高级 II 导览](./ii/)。
-- 高级 III 的原理与架构:双端分离约束、数据端 L0–L3、编辑器识别协议、录像深层原理、性能与调试。
-- API 参考:接口签名与类型清单不属于教程正文。
-
-## 读完这一层应该能做什么
-
-- 能用自己的话解释声明式与命令式的区别,并说出一个「仍然该用命令式」的场景。
-- 能拿到一个需求,判断它该做成插件,还是按现有接口读取即可。
-- 能说出一个可存档对象必须满足的三个约束:只存重建必需的数据、`saveState` 返回独立对象(引擎不替你深拷贝)、`loadState` 先清空再重建。
-- 能说明为什么建议一个对象一个钩子实例,并在回调拿到多个对象时用对象引用筛出目标。
-- 能排出一次移动、等到它结束,并在需要时实现自己的移动器或勇士顶层实现。
-- 能选对朝向处理器、算出目标格与反方向,并把朝向映射到图块数字。
-
-## 继续阅读
-
-高级 I 正文:
-
-- [声明式 vs 命令式接口](./declarative-vs-imperative.md)
-- [插件系统](./plugin-system.md)
-- [可存档内容](./saveable-content.md)
-- [录像一致性](./replay-consistency.md)
-- [data-common 的 Store](./data-stores.md)
-- [钩子深入](./hooks-in-depth.md)
-- [移动 Mover](./mover.md)
-- [FaceHandler 系列](./face-handler.md)
-
-前置回顾(中级教程):
-
-- [数据端与渲染端](../intermediate/data-vs-render.md)
-- [代码基础](../intermediate/code-basics.md)
-- [数据插件](../intermediate/data-plugins.md)
+- 渲染端内容:渲染端尚未完工,本层当前只收录数据端内容;渲染端专题只用占位标记指出归属,待完工后补上。
+- 编辑器内容:涉及编辑器与内容配置的部分只保留占位,不在本层展开。
+- 不重复中级已讲内容:接口的日常用法、参数清单与常见改法属于中级教程;本层只在需要说明扩展点时引用,不整段重写。
+- 高级 III 入口:该层页面尚未创建,本页与侧边栏都不提供链接;页面齐备后统一开放入口。