From 9efadc98064b9470ffc8de309c0c18eab0c5f96e Mon Sep 17 00:00:00 2001 From: unanmed <1319491857@qq.com> Date: Wed, 9 Sep 2026 16:57:17 +0800 Subject: [PATCH] fix(02-pathfinding): revise plans for user path/types.ts interface redesign --- .planning/phases/02-pathfinding/02-02-PLAN.md | 110 ++++++++-------- .planning/phases/02-pathfinding/02-03-PLAN.md | 117 +++++++++++------- 2 files changed, 134 insertions(+), 93 deletions(-) diff --git a/.planning/phases/02-pathfinding/02-02-PLAN.md b/.planning/phases/02-pathfinding/02-02-PLAN.md index 9b781d4..638b4f7 100644 --- a/.planning/phases/02-pathfinding/02-02-PLAN.md +++ b/.planning/phases/02-pathfinding/02-02-PLAN.md @@ -7,12 +7,12 @@ depends_on: ["02-01"] files_modified: - packages-user/data-common/src/common/mover.ts - packages-user/data-common/src/common/mover.test.ts - - packages-user/data-system/src/pathfinding/types.ts - - packages-user/data-system/src/pathfinding/graph.ts - - packages-user/data-system/src/pathfinding/system.ts - - packages-user/data-system/src/pathfinding/index.ts - - packages-user/data-system/src/pathfinding/graph.test.ts - - packages-user/data-system/src/pathfinding/system.test.ts + - packages-user/data-system/src/path/types.ts + - packages-user/data-system/src/path/graph.ts + - packages-user/data-system/src/path/system.ts + - packages-user/data-system/src/path/index.ts + - packages-user/data-system/src/path/graph.test.ts + - packages-user/data-system/src/path/system.test.ts - packages-user/data-system/src/index.ts - packages/common/src/logger.json autonomous: true @@ -25,40 +25,41 @@ estimate: must_haves: truths: - "mover.ts:651 坐标回写条件按用户拍板结果修复,L0 回归 4 用例(x 正交/y 正交/斜向/传送)全部翻绿 (D-07)" - - "有向图构建正确:边 = outPass(source) 含方向位且 inPass(dest) 含反向位,单向门 A→B 可行而 B→A 不可行有测试 (D-02)" - - "最小损失搜索满足 D-01:默认每格损失 1,可注入自定义损失函数,返回两点间损失最小路径 (D-01, D-02)" - - "仅取路径 API 存在且不移动任何对象 (D-06)" + - "path/types.ts 的 moveTo/teleportTo 返回类型已按用户授权修正为 `IPathfindingController | null`,除此之外该文件零改动(用户提交 7a011b2,user-owned)" + - "有向图构建正确:邻域方向由 `useDirGroup` 经 `IDirectionMapper.map(group)` 解析(默认 `InternalDirectionGroup.Dir4` 四正交),边可行性由注入的 `IPassPredicate.canPass(IPassCheckHandler)` 判定,单向门 A→B 可行而 B→A 不可行有测试 (D-02)" + - "最小损失搜索满足 D-01:默认每格损失 1(`PathCostFunction(block: ILayerLocation) => number`),可注入自定义损失函数,返回两点间损失最小的 `IPathfindingStep[]` 步骤序列(dir/from/to)(D-01, D-02)" + - "仅取路径 API `getPath(target): IPathfindingStep[]` 存在且不移动任何对象 (D-06)" - "不可达目标按 D-08 情形 2 返回空数组且不移动 (D-08)" - - "回退策略槽位存在:策略函数接收路径及每步到达位置并决策移动方式(瞬移或退为逐步)(D-04, D-05)" - - "绑定面为 IMapLayer 与 IObjectMovable 注入槽位,L2 未引用 L3 任何符号 (D-03)" + - "回退策略槽位存在:`PathFallbackPolicy(path) => boolean`(true = 必须逐步、禁止瞬移),注入 null 时默认必定回退为逐步寻路;「路径上存在事件即回退」的拍板默认策略由 02-03 在 L3 注入 (D-04, D-05)" + - "绑定面为 `useMapState`/`useMapLayer`/`useMovable` 注入槽位与 `usePassPredicate` 槽位(默认均不可通行),L2 未引用 L3 任何符号 (D-03)" artifacts: - "packages-user/data-common/src/common/mover.ts" - "packages-user/data-common/src/common/mover.test.ts" - - "packages-user/data-system/src/pathfinding/types.ts" - - "packages-user/data-system/src/pathfinding/graph.ts" - - "packages-user/data-system/src/pathfinding/system.ts" - - "packages-user/data-system/src/pathfinding/index.ts" - - "packages-user/data-system/src/pathfinding/graph.test.ts" - - "packages-user/data-system/src/pathfinding/system.test.ts" + - "packages-user/data-system/src/path/types.ts" + - "packages-user/data-system/src/path/graph.ts" + - "packages-user/data-system/src/path/system.ts" + - "packages-user/data-system/src/path/index.ts" + - "packages-user/data-system/src/path/graph.test.ts" + - "packages-user/data-system/src/path/system.test.ts" - "packages-user/data-system/src/index.ts" - "packages/common/src/logger.json" key_links: - - "graph.ts 消费注入的通行性谓词而非 import L3,谓词复刻 moverImpl canPass 的掩码语义(事件层恒参与,其余层仅 onlyEvents 参与)" - - "system.ts 的损失/回退/谓词槽位由 02-03 的 L3 接线注入" + - "graph.ts 消费注入的 IPassPredicate(canPass 判边可行性、shouldHit 分类终端/撞击节点)而非 import L3;掩码语义谓词由 02-03 Task 1 从 moverImpl 重构为 predicate() 后经 usePassPredicate 注入" + - "system.ts 的损失/回退/谓词槽位由 02-03 的 L3 接线填充(hero mover 的 predicate() 注入 usePassPredicate)" assumptions: - "PATH-01(flagged assumption,spec-less probe 未分类):本计划交付自动寻路的纯计算核心(图构建 + 最小损失搜索 + 仅取路径),Node 可验证" - "PATH-02(flagged assumption,spec-less probe 未分类):本计划提供的仅取路径 API 即渲染端 Phase 4 将调用的数据端入口之一,点击接线不在本阶段 (D-11)" prohibitions: - "不得为寻路引入任何新 npm 依赖(Dijkstra 自写,零新包)" - "不得把渲染端点击处理接进数据端代码 (D-11)" - - "接口签名必须与用户拍板后的草案逐字一致,不得自行更名或改层 (D-07)" + - "path/types.ts 为用户所有(commit 7a011b2),除用户授权的 moveTo/teleportTo 返回类型修正外不得重写、更名或改层 (D-07)" --- -按用户拍板结果修复 L0 mover.ts:651 坐标回写缺陷并翻绿回归测试,然后在 data-system(L2)落地寻路核心:有向图构建(注入谓词)、最小损失搜索(自定义损失、默认每格 1)、仅取路径 API、D-08 情形 2 空数组语义、回退策略槽位与移动方式决策。 +按用户拍板结果修复 L0 mover.ts:651 坐标回写缺陷并翻绿回归测试,然后在 data-system(L2)落地寻路核心:有向图构建(注入 IPassPredicate + useDirGroup 方向组)、最小损失搜索(`PathCostFunction` 自定义损失、默认每格 1)、`getPath` 仅取路径步骤序列、D-08 情形 2 空数组语义、`PathFallbackPolicy` 回退策略槽位与移动方式决策。接口签名以用户已提交的 `packages-user/data-system/src/path/types.ts`(commit 7a011b2)为唯一事实源——本计划仅消费该文件,唯一例外是用户授权的 moveTo/teleportTo 返回类型修正。 Purpose: 这是全部寻路行为的纯逻辑地基——不移动任何对象,可在 Node 下完整验证;L0 缺陷不修则 02-03 的多步逐步移动必然走崩(RESEARCH P1)。 -Output: 修复后的 mover.ts + 绿色 L0 回归;pathfinding/{types,graph,system,index}.ts 及 co-located 测试。 +Output: 修复后的 mover.ts + 绿色 L0 回归;path/{graph,system,index}.ts 及 co-located 测试;path/types.ts 的授权返回类型修正。 @@ -74,7 +75,14 @@ Output: 修复后的 mover.ts + 绿色 L0 回归;pathfinding/{types,graph,syst @.planning/phases/02-pathfinding/02-PATTERNS.md @.planning/phases/02-pathfinding/02-INTERFACE-DRAFT.md -# 草案与拍板记录是本计划的接口事实源 +# 接口事实源:用户已提交的 path/types.ts(草案与拍板记录为语义背景) +@packages-user/data-system/src/path/types.ts +@packages/common/src/utils/types.ts +@packages/common/src/utils/dir.ts + +# 草案与拍板记录 +@.planning/phases/02-pathfinding/02-INTERFACE-DRAFT.md + @packages-user/data-common/src/common/mover.ts @packages-user/data-common/src/store/types.ts @packages-user/data-base/src/map/types.ts @@ -105,40 +113,44 @@ Output: 修复后的 mover.ts + 绿色 L0 回归;pathfinding/{types,graph,syst - Task 2: 有向图构建(注入谓词 + 终端节点 + 边界守卫) - packages-user/data-system/src/pathfinding/graph.ts, packages-user/data-system/src/pathfinding/types.ts, packages-user/data-system/src/pathfinding/graph.test.ts - .planning/phases/02-pathfinding/02-INTERFACE-DRAFT.md, packages-user/data-state/src/hero/moverImpl.ts, packages-user/data-base/src/map/mapLayer.ts, packages-user/data-base/src/map/types.ts, packages-user/data-common/src/store/types.ts, packages-user/data-system/src/combat/mapDamage.ts, dev.md - 创建 types.ts 与 graph.ts(签名与命名以拍板草案为唯一事实源,D-07)。types.ts:损失函数、回退策略、通行性谓词的函数类型别名;图节点/邻接的 interface;全部 jsDoc 中文注释、对象类型单独 interface、禁 `import type`(dev.md)。graph.ts:把 `IMapLayer` 转换为有向图——节点为 inMap 内图块;从 A 向方向 d 的边成立当且仅当通行性谓词(source, d, dest) 为真(D-02 有向图要求,单向门由掩码不对称产生);谓词为 `useXxx()` 注入槽位(PATTERNS Shared Pattern 1 同款),L2 默认实现逐字复刻 moverImpl.ts:85-104 + 107-122 的掩码语义(事件层恒参与判定;其余层仅当 `pass.onlyEvents` 为真参与;方向→PassBit 映射照 moverImpl.ts:40-53 语义),L3 可注入复用 `DefaultHeroMoveTopImpl` 判定的谓词。终端节点:canPass 为真但 `eventPass` 为假的图块标记为仅可作路径终点、不可作中间节点(RESEARCH Pitfall 3)。边界守卫:构建与查询入口统一 `IMapLayer.inMap` + `isNil` 判空,非法输入 `logger.warn` 新码(warn ≥173,本任务在 packages/common/src/logger.json 登记首个本阶段码)后返回空图/空结果。graph.test.ts 用 eventDispatch.test.ts:105-179 的 fixture 模式(TileStore.addTile 位掩码 + FaceManager.register + MapState.fromRaw),必含用例:A→B 可行且 B→A 不可行的单向门(Pitfall 4)、eventPass=false 终端节点、越界坐标守卫告警。禁 import L3 任何符号(防循环依赖)。 + Task 2: 授权的 types.ts 返回类型修正 + 有向图构建(注入 IPassPredicate + useDirGroup + 终端节点 + 边界守卫) + packages-user/data-system/src/path/types.ts, packages-user/data-system/src/path/graph.ts, packages-user/data-system/src/path/graph.test.ts + packages-user/data-system/src/path/types.ts, packages/common/src/utils/types.ts, packages/common/src/utils/dir.ts, packages-user/data-state/src/hero/moverImpl.ts, packages-user/data-base/src/map/mapLayer.ts, packages-user/data-base/src/map/types.ts, packages-user/data-common/src/store/types.ts, packages-user/data-system/src/combat/mapDamage.ts, dev.md + 【授权修正,先行】`path/types.ts` 为用户所有(commit 7a011b2),本任务对它做且仅做一处用户逐字授权的修正:`IPathfindingSystem.moveTo` 与 `teleportTo` 的返回类型由 `IPathfindingController` 改为 `IPathfindingController | null`(jsdoc 已写明「失败返回 null」,与契约对齐)。不得改动该文件任何其他内容(D-07)。 +【graph.ts】新建 `packages-user/data-system/src/path/graph.ts`(不新建 types.ts——它已由用户提交,直接 `import` 其导出:`IPathfindingStep`、`PathCostFunction`、`PathFallbackPolicy`,并从 `@user/data-base` 引入 `IPassPredicate`/`IPassCheckHandler`/`ILayerLocation`,从 `@motajs/common` 引入 `InternalDirectionGroup`)。图构建:节点为 `IMapLayer.inMap` 内图块;邻域由 `useDirGroup(group: number)` 注入的方向组经 `IDirectionMapper.map(group)`(`@motajs/common` utils 的 `DirectionMapper` 实例,L0 合法引入)解析为 `IDirectionDescriptor` 坐标增量,默认组 `InternalDirectionGroup.Dir4`(四正交,per 拍板);从 A 向描述器 d 的边成立当且仅当注入的 `IPassPredicate.canPass({ currLoc, nextLoc, direction, floorId })` 为真(D-02 有向图要求,单向门由谓词判定不对称产生);谓词经 `usePassPredicate(predicate: IPassPredicate | null)` 注入,默认(null)均不可通行——per 用户 types.ts jsdoc,掩码语义谓词由 02-03 Task 1 从 moverImpl 重构提供。每条边的 `IPathfindingStep.dir` 取与描述器坐标增量对应的 `FaceDirection`(Dir4 四增量与 moverImpl.ts:40-53 directionToPassBit 的 Up/Right/Down/Left 一一对应;Dir8 斜向增量对应四个斜向 FaceDirection)。终端节点分类:canPass 为真但注入谓词 `shouldHit` 为真的图块标记为仅可作路径终点、不可作中间节点(RESEARCH Pitfall 3,D-08 语义:掩码允许到达且目标 no-pass 才构成撞击/OnTouch 情形,掩码导致的不可达永不触发)。边界守卫:构建与查询入口统一 `IMapLayer.inMap` + `isNil` 判空,非法输入 `logger.warn` 新码(warn ≥173,本任务在 packages/common/src/logger.json 登记首个本阶段码)后返回空图/空结果。graph.test.ts 用 eventDispatch.test.ts:105-179 的 fixture 模式(TileStore.addTile 位掩码 + FaceManager.register + MapState.fromRaw),测试内注入复刻 moverImpl 掩码语义的 fixture 谓词(事件层恒参与、其余层仅 `pass.onlyEvents` 参与),必含用例:A→B 可行且 B→A 不可行的单向门(Pitfall 4)、shouldHit=true 终端节点、越界坐标守卫告警。禁 import L3 任何符号(防循环依赖)。 - pnpm exec vitest run "packages-user/data-system/src/pathfinding/graph.test.ts" + pnpm exec vitest run "packages-user/data-system/src/path/graph.test.ts" vitest 输出出现 "failed" 字样或非零退出码——单向门、终端节点或边界守卫断言未通过 + - path/types.ts 的 git diff 仅含 moveTo/teleportTo 两处返回类型追加 `| null`,无其他变更(CLI 断言) - 单向门用例:同一条边从 A→B 判定可行、从 B→A 判定不可行(行为断言) - - eventPass=false 的可通行图块出现在路径终点时合法、出现在中间节点时被拒绝(行为断言) + - shouldHit=true 的可通行图块出现在路径终点时合法、出现在中间节点时被拒绝(行为断言) + - 未注入谓词(默认)时所有边不可行;注入谓词后邻域方向数与 `DirectionMapper.map(InternalDirectionGroup.Dir4)` 一致为 4(行为断言) - 越界/空数据输入触发 logger.warn 新码并得到空结果而非异常(行为断言) - - graph.ts 与 types.ts 中无任何来自 data-state 的引入:对两文件运行来源断言找不到 data-state 引用语句(源断言) + - graph.ts 中无任何来自 data-state 的引入:对文件运行来源断言找不到 data-state 引用语句(源断言) - 有向图构建在 Node 下全绿,边语义与 canPass 判定同源。 + 授权修正落地且 types.ts 其余零改动;有向图构建在 Node 下全绿,边语义与注入谓词同源。 - Task 3: 最小损失搜索 + 仅取路径 API + 回退策略槽位(L2 核心) - packages-user/data-system/src/pathfinding/system.ts, packages-user/data-system/src/pathfinding/types.ts, packages-user/data-system/src/pathfinding/index.ts, packages-user/data-system/src/pathfinding/system.test.ts - .planning/phases/02-pathfinding/02-INTERFACE-DRAFT.md, packages-user/data-system/src/pathfinding/graph.ts, packages-user/data-system/src/event/system.ts, packages-user/data-system/src/combat/mapDamage.ts, packages-user/data-common/src/common/mover.ts, dev.md - 创建 system.ts(`PathfindingSystem` 类,骨架照 GameEventSystem 形态:构造器持 state、可空成员显式类型、`useXxx()` 注入方法,per PATTERNS)与 index.ts barrel(照 event/index.ts 三行形态,并在 data-system/src/index.ts 追加 pathfinding 导出)。实现:(1) 自写 Dijkstra(O(V²),约 40 行,零依赖)在 Task 2 的有向图上求两点间损失最小路径,满足 D-01/D-02 语义;(2) 损失函数注入槽位,默认每格损失 1(D-01);损失守卫——函数返回非有限数或负数时 `logger.warn` 新码并按默认损失 1 处理(威胁 T-02-02);(3) 仅取路径方法:绑定地图与 `IObjectMovable` 后可调用,返回坐标路径数组,不调用任何移动方法(D-06,D-03 绑定槽位);(4) 不可达语义:目标不可达且不属于「no-pass 目标 + 可达相邻格」情形时返回空数组、不移动(D-08 情形 2;情形 1 的相邻格解析留给 02-03,本任务先暴露图上「目标四邻中存在可达格」的查询以便 L3 判定——具体 API 形态以拍板草案为准);(5) 回退策略槽位:策略函数入参为完整路径及每步到达位置,返回是否退回逐步方式(D-05);移动方式决策——策略判回退则走逐步,否则允许瞬移(D-04;瞬移的实际执行在 02-03,本任务只产出决策结果);每次寻路动态构建图、不缓存(CONTEXT 酌情项默认建议,数据端状态可变)。system.test.ts 覆盖:默认损失 1 的最短路径、自定义损失改变选路(构造「步数少但损失高」的岔路)、不可达返回空数组、回退策略被以(路径,逐步到达位置序列)调用并生效、未绑定地图/对象时 logger.warn + 空结果(mapDamage.ts:169-173 守卫形态)。 + Task 3: 最小损失搜索 + 仅取路径 + 回退策略槽位(L2 核心,签名照用户 types.ts) + packages-user/data-system/src/path/system.ts, packages-user/data-system/src/path/index.ts, packages-user/data-system/src/path/system.test.ts, packages-user/data-system/src/index.ts + packages-user/data-system/src/path/types.ts, packages-user/data-system/src/path/graph.ts, packages-user/data-system/src/event/system.ts, packages-user/data-system/src/combat/mapDamage.ts, packages-user/data-common/src/common/mover.ts, dev.md + 创建 system.ts(`PathfindingSystem` 类实现用户 types.ts 的 `IPathfindingSystem`,骨架照 GameEventSystem 形态:构造器持 state、可空成员显式类型、`useXxx()` 注入方法,per PATTERNS;公开 `readonly finder: IPathfinder`——finder 实现用户 types.ts 的 `IPathfinder`:`useMapState(IMapState|null)`、`useMapLayer(IMapLayer|null)`(通常绑事件层)、`useCostFunction(PathCostFunction|null)`、`usePassPredicate(IPassPredicate|null)`、`useDirGroup(group: number)`、`find(start, target): IPathfindingStep[]`)与 index.ts barrel(照 event/index.ts 三行形态,并在 data-system/src/index.ts 追加 path 导出)。实现:(1) 自写 Dijkstra(O(V²),约 40 行,零依赖)在 Task 2 的有向图上求两点间损失最小路径,输出 `IPathfindingStep[]` 步骤序列(每步 `{ dir, from, to }`,非坐标列表),满足 D-01/D-02 语义;(2) 损失函数注入槽位,签名 `PathCostFunction = (block: ILayerLocation) => number`,默认每格损失 1(D-01);损失守卫——函数返回非有限数或负数时 `logger.warn` 新码并按默认损失 1 处理(威胁 T-02-02);(3) 仅取路径 `getPath(target): IPathfindingStep[]`:绑定地图与 `IObjectMovable` 后可调用,返回步骤序列,不调用任何移动方法(D-06,D-03 绑定槽位 `useMovable(IObjectMovable|null)`);(4) 不可达语义:目标不可达时 `find`/`getPath` 返回空数组、不移动(D-08 情形 2);情形 1 的相邻格可达性由 02-03 直接以 `find(heroPos, 相邻格)` 判定,无需专用查询 API;(4) `moveTo`/`teleportTo(target): IPathfindingController | null`:按用户 types.ts 契约实现——绑定 movable 后经其 mover 取得 `IMoverController`,与计算所得路径步骤序列构成 `IPathfindingController { controller, path }` 返回;无法寻路、无路径或已有移动进行中时返回 `null`(授权修正后的 jsdoc 语义,D-04);移动的实际推进(逐步队列驱动/瞬移执行)在 02-03 的 L3 接线完成,本任务产出正确的控制器包装与决策结果;(5) 回退策略槽位 `useFallbackPolicy(policy: PathFallbackPolicy | null)`:`PathFallbackPolicy = (path: readonly IPathfindingStep[]) => boolean`,返回 true 表示不允许瞬移、必须逐步(D-05);注入 null 时默认必定回退为逐步寻路(per 用户 types.ts jsdoc)——「路径上存在事件即回退」的拍板默认策略由 02-03 在 L3 注入;移动方式决策——策略判回退则走逐步,否则允许瞬移(D-04);(6) `interrupt(): Promise` 占位实现(02-03 Task 4 接管语义);(7) 每次寻路动态构建图、不缓存(CONTEXT 酌情项默认建议,数据端状态可变)。system.test.ts 覆盖:默认损失 1 的最短路径步骤序列、自定义损失改变选路(构造「步数少但损失高」的岔路)、不可达返回空数组、moveTo/teleportTo 无路径或未绑定 movable 时返回 null(而非异常)、回退策略以路径步骤序列为入参被调用并生效、null 策略默认逐步、未绑定地图/对象时 logger.warn + 空结果(mapDamage.ts:169-173 守卫形态)。 - pnpm exec vitest run "packages-user/data-system/src/pathfinding/system.test.ts" - vitest 输出出现 "failed" 字样或非零退出码——最小损失、自定义损失、空数组语义或回退策略断言未通过 + pnpm exec vitest run "packages-user/data-system/src/path/system.test.ts" + vitest 输出出现 "failed" 字样或非零退出码——最小损失、自定义损失、空数组语义、null 契约或回退策略断言未通过 - - 默认损失下返回格数最少路径;注入自定义损失后选择损失更小的岔路(行为断言) - - 不可达目标返回空数组且移动对象坐标不变(行为断言) - - 回退策略收到路径与每步到达位置并改变决策结果(行为断言) + - 默认损失下返回格数最少的 IPathfindingStep 序列;注入自定义损失后选择损失更小的岔路(行为断言) + - 不可达目标 getPath 返回空数组且移动对象坐标不变(行为断言) + - moveTo/teleportTo 在无路径/未绑定 movable 时返回 null,有路径时返回含 controller 与 path 的 IPathfindingController(行为断言) + - PathFallbackPolicy 收到步骤序列并改变决策结果;注入 null 时决策为逐步(行为断言) - 未绑定依赖时 warn 码输出且返回空结果不抛异常(行为断言) - - `pnpm exec vitest run "packages-user/data-system/src/pathfinding"` 全目录绿(CLI 断言) + - `pnpm exec vitest run "packages-user/data-system/src/path"` 全目录绿(CLI 断言) - L2 寻路核心在 Node 下全绿:图构建、最小损失、仅取路径、空数组语义、回退决策齐备。 + L2 寻路核心在 Node 下全绿:图构建、最小损失、仅取路径步骤序列、空数组语义、控制器包装契约、回退决策齐备,签名与用户 types.ts 逐字一致。 @@ -146,9 +158,9 @@ Output: 修复后的 mover.ts + 绿色 L0 回归;pathfinding/{types,graph,syst ## Artifacts this phase produces -- 接口/类型(命名以拍板草案为唯一事实源):损失函数类型(默认每格 1)、回退策略函数类型(入参路径 + 每步到达位置)、通行性谓词函数类型、图节点/邻接 interface(`packages-user/data-system/src/pathfinding/types.ts`) -- 类:`PathfindingSystem`(L2,绑定 `IMapLayer`/`IObjectMovable` 注入槽位,D-03) -- 函数/方法:有向图构建(graph.ts)、自写 Dijkstra 最小损失搜索、仅取路径 API(D-06)、移动方式决策(D-04/D-05 槽位)、方向→PassBit 映射(谓词默认实现内) +- 接口/类型:用户已提交的 `packages-user/data-system/src/path/types.ts`(`IPathfinder`/`IPathfindingSystem`/`IPathfindingStep`/`IPathfindingController`/`PathCostFunction`/`PathFallbackPolicy`,commit 7a011b2)——本计划仅消费;唯一改动为用户授权的 moveTo/teleportTo 返回类型追加 `| null` +- 类:`PathfindingSystem` + `IPathfinder` 实现(L2,绑定 `useMapState`/`useMapLayer`/`useMovable` 与 `usePassPredicate`/`useCostFunction`/`useDirGroup` 注入槽位,D-03) +- 函数/方法:有向图构建(graph.ts,`IDirectionMapper.map` 邻域解析)、自写 Dijkstra 最小损失搜索、`getPath` 仅取路径步骤序列(D-06)、`IPathfindingController` 包装契约、移动方式决策(D-04/D-05 槽位) - 修改:`packages-user/data-common/src/common/mover.ts`(mover.ts:651 回写条件,按拍板)、`packages/common/src/logger.json`(新 warn 码 ≥173)、`packages-user/data-system/src/index.ts`(barrel 追加) - 测试:`mover.test.ts`(翻绿)、`graph.test.ts`、`system.test.ts` @@ -171,13 +183,13 @@ Output: 修复后的 mover.ts + 绿色 L0 回归;pathfinding/{types,graph,syst - `pnpm exec vitest run "packages-user/data-common/src/common/mover.test.ts"` 4/4 passed -- `pnpm exec vitest run "packages-user/data-system/src/pathfinding"` 全绿 -- `pnpm check:type 2>&1 | Select-String -Pattern "pathfinding|mover\.ts"` 无输出(Phase 01 同款过滤式类型门,仓库既有诊断不在本计划范围) -- `pnpm check:circular` 输出中不含 pathfinding 相关路径(eventStore 既有循环为 Phase 01 基线,维持不变) +- `pnpm exec vitest run "packages-user/data-system/src/path"` 全绿 +- `pnpm check:type 2>&1 | Select-String -Pattern "src[\\/]path[\\/]|mover\.ts"` 无输出(Phase 01 同款过滤式类型门,仓库既有诊断不在本计划范围) +- `pnpm check:circular` 输出中不含 path 相关路径(eventStore 既有循环为 Phase 01 基线,维持不变) -L0 缺陷按拍板修复且有回归护栏;L2 寻路核心全部行为(有向边、单向门、终端节点、最小损失、自定义损失、仅取路径、空数组、回退决策)在 Node 下测试验证通过。 +L0 缺陷按拍板修复且有回归护栏;path/types.ts 仅含授权的返回类型修正;L2 寻路核心全部行为(有向边、单向门、终端节点、useDirGroup/Dir4 邻域、最小损失、自定义损失、仅取路径步骤序列、空数组、控制器包装契约、回退决策)在 Node 下测试验证通过,签名与用户 types.ts 逐字一致。 diff --git a/.planning/phases/02-pathfinding/02-03-PLAN.md b/.planning/phases/02-pathfinding/02-03-PLAN.md index 2513cb0..fcf9e84 100644 --- a/.planning/phases/02-pathfinding/02-03-PLAN.md +++ b/.planning/phases/02-pathfinding/02-03-PLAN.md @@ -5,34 +5,40 @@ type: execute wave: 3 depends_on: ["02-01", "02-02"] files_modified: - - packages-user/data-state/src/pathfinding/heroPathfinding.ts - - packages-user/data-state/src/pathfinding/heroPathfinding.test.ts + - packages-user/data-state/src/path/heroPathfinding.ts + - packages-user/data-state/src/path/heroPathfinding.test.ts + - packages-user/data-state/src/hero/moverImpl.ts + - packages-user/data-base/src/hero/mover.ts - packages-user/data-state/src/core.ts - packages-user/data-state/src/index.ts - packages/common/src/logger.json autonomous: true requirements: [PATH-01, PATH-02] estimate: - tokens: 75000 - raw_tokens: 75000 - tasks: 3 + tokens: 85000 + raw_tokens: 85000 + tasks: 4 confidence: low must_haves: truths: - - "逐步自动寻路端到端可用:路径翻译为 Dir 步骤队列并复用 `DefaultHeroMoveTopImpl` 执行,每步走 enter/leave/hit hooks,途经事件自然触发 (D-09)" - - "瞬移执行可用:决策允许瞬移时经 `ObjectMoveType.Teleport` 到达目标;拍板的回退策略判定需回退时自动退为逐步寻路,默认策略为路径上存在事件即回退 (D-04, D-05)" + - "DefaultHeroMoveTopImpl 通行性判断已重构为 IPassPredicate:predicate() 返回谓词实现,hero mover onStepStart 经 predicate().canPass/shouldHit(IPassCheckHandler: currLoc/nextLoc/direction/floorId)判定;Phase 1 enter/leave/hit 事件派发行为不变(D-07, D-08, D-09)" + - "逐步自动寻路端到端可用:hero mover 的 predicate() 经 usePassPredicate 注入 L2,`find` 取得的 IPathfindingStep 步骤序列翻译为 Dir 步骤队列并复用 `DefaultHeroMoveTopImpl` 执行,每步走 enter/leave/hit hooks,途经事件自然触发 (D-09)" + - "瞬移执行可用:决策允许瞬移时经 `ObjectMoveType.Teleport` 到达目标;`PathFallbackPolicy(path) => boolean` 判定需回退(true = 必须逐步)时自动退为逐步寻路,拍板默认策略「路径上存在事件即回退」在 L3 注入 (D-04, D-05)" - "D-08 情形 1:no-pass 目标存在可达相邻格时,移动到该相邻格、按拍板方案派发目标 OnTouch、勇士面朝目标 (D-08)" - - "自动寻路途中被新寻路调用或显式打断入口打断后,新寻路从最新位置接管 (D-10)" + - "自动寻路途中被新寻路调用或显式打断入口(interrupt())打断后,新寻路从最新位置接管 (D-10)" - "数据端移动入口已在 data-state barrel 导出,Node 冒烟测试可调用;渲染端点击接线不在本阶段 (D-11)" - "CoreState 构造器完成寻路系统接线(照 useTopImplementation 同处接线模式)(D-09)" artifacts: - - "packages-user/data-state/src/pathfinding/heroPathfinding.ts" - - "packages-user/data-state/src/pathfinding/heroPathfinding.test.ts" + - "packages-user/data-state/src/path/heroPathfinding.ts" + - "packages-user/data-state/src/path/heroPathfinding.test.ts" + - "packages-user/data-state/src/hero/moverImpl.ts" + - "packages-user/data-base/src/hero/mover.ts" - "packages-user/data-state/src/core.ts" - "packages-user/data-state/src/index.ts" - "packages/common/src/logger.json" key_links: - - "heroPathfinding.ts 注入 02-02 的损失/回退/谓词槽位并绑定 hero mover(L3 → L2 合法方向,反向即循环依赖)" + - "heroPathfinding.ts 将 state.hero mover 的 predicate()(moverImpl 重构产物)注入 02-02 的 usePassPredicate,并注入损失/回退槽位、绑定 hero mover(L3 → L2 合法方向,反向即循环依赖)" + - "data-base/src/hero/mover.ts 的 onStepStart 判定改经 topImpl.predicate().canPass/shouldHit(IPassCheckHandler 构造),与 moverImpl 重构为同一接口契约的两端" - "D-08 OnTouch 派发走 Phase 1 source-aware 执行链(executor.execute,env 形态复刻 moverImpl commonTrigger)" - "打断接管经 `IMoverController.stop()`(先停旧再起新,mover.start 移动中返回 null)" assumptions: @@ -42,13 +48,14 @@ must_haves: - "不得为寻路引入任何新 npm 依赖(零依赖约束)" - "不得把渲染端点击处理接进数据端代码 (D-11)" - "D-08 OnTouch 派发必须按用户拍板方案实现,不得静默替换 (D-07, D-08)" + - "path/types.ts(用户已提交)不得重写;moverImpl 重构仅提取 predicate(),Phase 1 事件派发语义不得变更 (D-07, D-09)" --- -在 data-state(L3)完成寻路系统的移动执行接线:逐步寻路复用 hero mover 与 Phase 1 事件链、瞬移与默认回退策略、D-08 情形 1 相邻格 + OnTouch + 面朝目标、打断接管,以及 CoreState/barrel 装配、logger 码登记与阶段门禁。 +在 data-state(L3)完成寻路系统的移动执行接线:先把 moverImpl 通行性判断重构为用户新接口要求的 `IPassPredicate`(`predicate()`),再落地逐步寻路(复用 hero mover 与 Phase 1 事件链)、瞬移与拍板默认回退策略、D-08 情形 1 相邻格 + OnTouch + 面朝目标、打断接管,以及 CoreState/barrel 装配、logger 码登记与阶段门禁。接口签名以用户已提交的 `packages-user/data-system/src/path/types.ts`(commit 7a011b2)为唯一事实源。 Purpose: 让寻路从「算得出」变成「走得通」——勇士真实移动、事件真实触发、输入真实接管;这是 PATH-01 的最终形态与 PATH-02 的数据端入口。 -Output: heroPathfinding.ts + 接线与测试;更新 core.ts、双 barrel、logger.json;全阶段门禁通过。 +Output: moverImpl predicate 重构 + heroPathfinding.ts + 接线与测试;更新 core.ts、双 barrel、logger.json;全阶段门禁通过。 @@ -65,9 +72,10 @@ Output: heroPathfinding.ts + 接线与测试;更新 core.ts、双 barrel、log @.planning/phases/02-pathfinding/02-INTERFACE-DRAFT.md @.planning/phases/02-pathfinding/02-02-SUMMARY.md -# L2 寻路核心与 L3 既有接线点 -@packages-user/data-system/src/pathfinding/system.ts -@packages-user/data-system/src/pathfinding/types.ts +# L2 寻路核心(用户 types.ts 为接口事实源)与 L3 既有接线点 +@packages-user/data-system/src/path/system.ts +@packages-user/data-system/src/path/types.ts +@packages-user/data-base/src/map/types.ts @packages-user/data-state/src/hero/moverImpl.ts @packages-user/data-state/src/core.ts @packages-user/data-state/src/index.ts @@ -81,36 +89,55 @@ Output: heroPathfinding.ts + 接线与测试;更新 core.ts、双 barrel、log - - Task 1: 逐步自动寻路端到端接线(CoreState + 路径翻译 + hero mover 执行) - packages-user/data-state/src/pathfinding/heroPathfinding.ts, packages-user/data-state/src/pathfinding/heroPathfinding.test.ts, packages-user/data-state/src/core.ts, packages-user/data-state/src/index.ts - .planning/phases/02-pathfinding/02-INTERFACE-DRAFT.md, packages-user/data-state/src/hero/moverImpl.ts, packages-user/data-state/src/core.ts, packages-user/data-base/src/hero/mover.ts, packages-user/data-common/src/common/mover.ts, packages-user/data-system/src/event/eventDispatch.test.ts, dev.md - 创建 L3 `HeroPathfinding` 类(imports 骨架照 moverImpl.ts:1-32:从 `@user/data-system` 引入 L2 寻路系统——此方向合法)。职责:构造时绑定 `IObjectMovable`(hero mover)与 `IMapLayer`(event 层)到 02-02 的 `PathfindingSystem` 注入槽位(D-03);注入默认通行性谓词(复刻 `DefaultHeroMoveTopImpl` canPass 语义)与回退策略槽位。实现逐步执行:调用寻路系统取得最小损失路径后,将路径翻译为 `ObjectMoveType.Dir` 步骤队列(相邻坐标增量 → `FaceDirection`,每次一步保证 canPass 逐格判定,per RESEARCH Code Examples 语义),经 mover `push` + `start` 启动(D-09)。core.ts 在 `//#endregion 勇士顶层初始化` region 内、`useTopImplementation` 之后新增寻路接线(照 core.ts:235-237 模式);data-state/src/index.ts 在 `export * from './hero';` 之后追加寻路导出(dev.md:不转发导出)。data-system/src/index.ts 的 pathfinding 导出已由 02-02 Task 3 落地(该文件的唯一归属任务),本任务只验证其存在且可被 `@user/data-system` 引入,不重复写入该行。heroPathfinding.test.ts 用 eventDispatch.test.ts:105-179 fixture 模式:勇士在 fixture 地图上自动寻路走两步,断言终点坐标、leave/enter 事件按 Phase 1 source-aware 顺序触发、途经带事件的图块事件被执行。签名与命名以拍板草案为唯一事实源(D-07)。 + + Task 1: moverImpl 通行性判断重构为 IPassPredicate(predicate())+ hero mover 调用点改接 + packages-user/data-state/src/hero/moverImpl.ts, packages-user/data-base/src/hero/mover.ts + packages-user/data-base/src/hero/types.ts, packages-user/data-base/src/map/types.ts, packages-user/data-base/src/hero/mover.ts, packages-user/data-state/src/hero/moverImpl.ts, packages-user/data-state/src/coreEventLayer.test.ts, packages-user/data-common/src/common/mover.ts, dev.md + 用户的接口重构(commit 7a011b2)将 `IHeroMoveTopImpl` 的 `canPass(handler)/shouldHit(handler)` 移除、新增 `predicate(): IPassPredicate`(`@user/data-base` map/types.ts §通行检测:`IPassPredicate.canPass/shouldHit(IPassCheckHandler)`,handler 为 `{ currLoc, nextLoc, direction, floorId }`),现有 moverImpl.ts 不再满足接口。本任务做行为保持型重构:(1) 在 `DefaultHeroMoveTopImpl` 内把现 `canPass`(moverImpl.ts:63-125,含四角朝向直通、事件层掩码、其余层 onlyEvents 掩码)与 `shouldHit`(moverImpl.ts:127-141,eventPass 判定)的判定逻辑提取为一个 `IPassPredicate` 实现类;谓词不再依赖 `IHeroMoveTopHandler.face`(`IPassCheckHandler` 无 face 字段)——反向掩码位由 `FaceDirection` 的反向映射直接得出(语义与 moverImpl.ts:85-87 经 face.opposite 等价);实现 `predicate(): IPassPredicate` 返回该实例;类内原 `canPass/shouldHit` 公有方法移除。(2) 改接 `packages-user/data-base/src/hero/mover.ts` 的调用点:`onStepStart` 内 mover.ts:183 `this.topImpl.canPass(handler)` 与 mover.ts:185 `this.topImpl.shouldHit(handler)` 改为从 `this.topImpl.predicate()` 取谓词,并以 `IPassCheckHandler` 形态(currLoc/nextLoc/direction/floorId,从现有 handler 字段提取)调用 `canPass/shouldHit`;HeroMoveCode 分支结构(Step/Hit/CannotMove)不变。(3) 保持 Phase 1 事件派发行为完整不变:enter/leave/hit → OnEnter/OnLeave/OnTouch(D-08 语义 per 拍板记录——hit/OnTouch 仅在掩码允许到达且目标 no-pass 时触发,掩码导致的不可达走 CannotMove 永不触发 OnTouch)。D-08 拍板语义映射:`canPass=true` 且 `shouldHit=true` → HeroMoveCode.Hit → OnTouch;`canPass=false` → CannotMove(不触发)。全部 jsDoc 中文注释、禁 `as`、禁 `import type`(dev.md)。回归护栏:coreEventLayer.test.ts 与全量既有测试必须全绿(行为保持证明)。 - pnpm exec vitest run "packages-user/data-state/src/pathfinding/heroPathfinding.test.ts" + pnpm exec vitest run "packages-user/data-state/src/coreEventLayer.test.ts"; if ($?) { pnpm exec vitest run "packages-user/data-base/src" } + 任一 vitest 输出出现 "failed" 字样或非零退出码——moverImpl 重构改变了 Phase 1 事件派发或 hero 移动判定行为 + + + - DefaultHeroMoveTopImpl 满足新 IHeroMoveTopImpl:含 predicate(): IPassPredicate,无 canPass/shouldHit 公有方法(源断言) + - data-base/src/hero/mover.ts 中无 `topImpl.canPass(`/`topImpl.shouldHit(` 直接调用,改经 predicate()(源断言) + - coreEventLayer.test.ts 与 data-base 既有测试全绿,Phase 1 OnEnter/OnLeave/OnTouch 行为不变(行为断言) + - 掩码语义保持:单向门(outPass/inPass 方向位)、事件层恒参与、其余层仅 onlyEvents 参与的行为与重构前等价(行为断言,由既有测试覆盖) + + moverImpl 满足用户新接口且行为保持:predicate() 可被 L3 注入 usePassPredicate,hero 移动判定与事件派发零回归。 + + + + Task 2: 逐步自动寻路端到端接线(CoreState + usePassPredicate 注入 + 路径翻译 + hero mover 执行) + packages-user/data-state/src/path/heroPathfinding.ts, packages-user/data-state/src/path/heroPathfinding.test.ts, packages-user/data-state/src/core.ts, packages-user/data-state/src/index.ts + packages-user/data-system/src/path/types.ts, packages-user/data-system/src/path/system.ts, packages-user/data-state/src/hero/moverImpl.ts, packages-user/data-state/src/core.ts, packages-user/data-base/src/hero/mover.ts, packages-user/data-common/src/common/mover.ts, packages-user/data-system/src/event/eventDispatch.test.ts, dev.md + 创建 L3 `HeroPathfinding` 类于 `packages-user/data-state/src/path/heroPathfinding.ts`(imports 骨架照 moverImpl.ts:1-32:从 `@user/data-system` 引入 L2 寻路系统——此方向合法)。职责:构造时绑定 `IObjectMovable`(hero mover)到 02-02 `PathfindingSystem` 的 `useMovable` 槽位、事件层 `IMapLayer` 到 finder 的 `useMapLayer` 槽位(D-03);将 Task 1 重构所得 hero mover 顶层实现的 `predicate()` 经 `usePassPredicate` 注入 L2(D-03/D-09 关键接线);注入拍板默认回退策略(「路径上存在事件即回退」,Task 3 实现细节,本任务先接策略槽位)。实现逐步执行:调用 `finder.find`/`system.getPath` 取得 `IPathfindingStep[]` 最小损失步骤序列后,将步骤的 `dir`(`FaceDirection`)翻译为 `ObjectMoveType.Dir` 步骤队列(每步一步,保证 canPass 逐格判定,per RESEARCH Code Examples 语义),经 mover `push` + `start` 启动(D-09);heroPathfinding 的公开入口与 `IPathfindingSystem` 的 `moveTo/teleportTo/getPath/interrupt` 对齐(签名以用户 types.ts 为唯一事实源,D-07)。core.ts 在 `//#endregion 勇士顶层初始化` region 内、`useTopImplementation` 之后新增寻路接线(照 core.ts:235-237 模式);data-state/src/index.ts 在 `export * from './hero';` 之后追加 path 接线导出(dev.md:不转发导出)。data-system/src/index.ts 的 path 导出已由 02-02 Task 3 落地(该文件的唯一归属任务),本任务只验证其存在且可被 `@user/data-system` 引入,不重复写入该行。heroPathfinding.test.ts 用 eventDispatch.test.ts:105-179 fixture 模式:勇士在 fixture 地图上自动寻路走两步,断言终点坐标、leave/enter 事件按 Phase 1 source-aware 顺序触发、途经带事件的图块事件被执行。 + + pnpm exec vitest run "packages-user/data-state/src/path/heroPathfinding.test.ts" vitest 输出出现 "failed" 字样或非零退出码——勇士未到达目标、事件未按序触发或翻译步骤数与路径不符 - 勇士沿最小损失路径逐步移动并停在目标格,坐标断言通过(行为断言) - 途经图块的 leave/enter 钩子触发且 env 的 heroLocator 与实际位置一致(行为断言) + - hero 的 predicate() 已注入 L2 usePassPredicate(L2 未注入谓词时不可行,注入后图可达——行为断言) - core.ts 含寻路接线且位于勇士顶层初始化 region(源断言) - - 两个 barrel 均导出寻路内容,`pnpm check:type 2>&1 | Select-String -Pattern "heroPathfinding|pathfinding|core\.ts"` 无输出(CLI 断言) + - 两个 barrel 均导出寻路内容,`pnpm check:type 2>&1 | Select-String -Pattern "heroPathfinding|src[\\/]path[\\/]|core\.ts"` 无输出(CLI 断言) 勇士能在 fixture 地图上自动寻路移动到指定格,途经事件自然触发——PATH-01 的核心闭环成立。 - Task 2: 瞬移执行 + 默认回退策略 + D-08 情形 1(相邻格 + OnTouch + 面朝) - packages-user/data-state/src/pathfinding/heroPathfinding.ts, packages-user/data-state/src/pathfinding/heroPathfinding.test.ts - .planning/phases/02-pathfinding/02-INTERFACE-DRAFT.md, packages-user/data-state/src/pathfinding/heroPathfinding.ts, packages-user/data-state/src/hero/moverImpl.ts, packages-user/data-common/src/common/mover.ts, packages-user/data-common/src/event/types.ts, packages-user/data-base/src/map/types.ts, .planning/phases/02-pathfinding/02-RESEARCH.md - 扩展 heroPathfinding.ts:(1) 瞬移执行——移动方式决策允许瞬移时以 `ObjectMoveType.Teleport` 步骤到达目标(D-04);(2) 默认回退策略——按拍板草案实现「路径上存在事件即回退」:沿路径用 moverImpl.ts:169-204 同款方式探测事件(点事件 + 静态/动态图块事件),命中即退为逐步执行(D-05);策略判不需回退时才瞬移;(3) D-08 情形 1——目标为 no-pass 图块且四邻存在可达格时:寻路至该相邻格并移动到达,按用户拍板方案派发目标 OnTouch(拍板方案 A:直接构造 `IGameEventInvocation`,env 逐字复刻 moverImpl.ts:206-222 `commonTrigger` 形态——state、type 按目标事件来源、trigger=`EventTrigger.OnTouch`、heroLocator=相邻格、triggerLocator=目标格、tile/layer/map 按 env 接口可空语义填实,调 `executor.execute`;拍板方案 B:按用户拍板的替代语义实现);随后以纯转向步或 `setFaceDir` 使勇士面朝目标(D-08)。测试补充:瞬移在无事件路径上直达目标;路径含事件时默认策略退为逐步且途经事件触发;no-pass 目标带 OnTouch 事件时勇士停于相邻格、面朝目标、mock 事件以 OnTouch 触发且 env.triggerLocator 为目标格(mock 事件照 eventDispatch.test.ts:181-211 addEvent 模式);无相邻可达格的不可达目标保持空数组不移动(D-08 情形 2 回归)。 + Task 3: 瞬移执行 + 拍板默认回退策略 + D-08 情形 1(相邻格 + OnTouch + 面朝) + packages-user/data-state/src/path/heroPathfinding.ts, packages-user/data-state/src/path/heroPathfinding.test.ts + packages-user/data-system/src/path/types.ts, packages-user/data-state/src/path/heroPathfinding.ts, packages-user/data-state/src/hero/moverImpl.ts, packages-user/data-common/src/common/mover.ts, packages-user/data-common/src/event/types.ts, packages-user/data-base/src/map/types.ts, .planning/phases/02-pathfinding/02-RESEARCH.md + 扩展 heroPathfinding.ts:(1) 瞬移执行——`PathFallbackPolicy(path) => boolean`(path 为 `IPathfindingStep[]` 步骤序列)决策允许瞬移(返回 false)时以 `ObjectMoveType.Teleport` 步骤到达目标(D-04);(2) 拍板默认回退策略——「路径上存在事件即回退」:沿步骤序列逐格用 moverImpl.ts:169-204 同款方式探测事件(点事件 + 静态/动态图块事件),命中即策略返回 true(必须逐步、禁止瞬移),L3 将此策略经 `useFallbackPolicy` 注入 L2(D-05;L2 的 null 默认为必定逐步,本策略为游戏默认,per 用户 types.ts + 拍板);(3) D-08 情形 1——目标为 no-pass 图块且四邻存在可达格时(相邻格可达性以 `finder.find(heroPos, 相邻格)` 判定):寻路至该相邻格并移动到达,按用户拍板方案派发目标 OnTouch(拍板方案 A:直接构造 `IGameEventInvocation`,env 逐字复刻 moverImpl.ts:206-222 `commonTrigger` 形态——state、type 按目标事件来源、trigger=`EventTrigger.OnTouch`、heroLocator=相邻格、triggerLocator=目标格、tile/layer/map 按 env 接口可空语义填实,调 `executor.execute`;拍板方案 B:按用户拍板的替代语义实现);随后以纯转向步或 `setFaceDir` 使勇士面朝目标(D-08)。测试补充:瞬移在无事件路径上直达目标;路径含事件时默认策略退为逐步且途经事件触发;no-pass 目标带 OnTouch 事件时勇士停于相邻格、面朝目标、mock 事件以 OnTouch 触发且 env.triggerLocator 为目标格(mock 事件照 eventDispatch.test.ts:181-211 addEvent 模式);无相邻可达格的不可达目标保持空数组不移动(D-08 情形 2 回归)。 - pnpm exec vitest run "packages-user/data-state/src/pathfinding/heroPathfinding.test.ts" + pnpm exec vitest run "packages-user/data-state/src/path/heroPathfinding.test.ts" vitest 输出出现 "failed" 字样或非零退出码——瞬移未直达、事件路径未回退逐步、OnTouch 未按拍板方案触发或面朝方向错误 - 无事件路径瞬移一步到达目标,坐标断言通过(行为断言) - - 含事件路径在默认策略下退为逐步且途经事件触发(行为断言) + - 含事件路径在默认策略(返回 true)下退为逐步且途经事件触发(行为断言) - no-pass 目标:勇士位于相邻格、朝向指向目标、OnTouch 事件以拍板方案派发且 triggerLocator 为目标格(行为断言) - 四邻皆不可达的 no-pass 目标:路径空数组、勇士不动(行为断言) @@ -118,21 +145,21 @@ Output: heroPathfinding.ts + 接线与测试;更新 core.ts、双 barrel、log - Task 3: 打断接管 + logger 码登记 + 阶段门禁 - packages-user/data-state/src/pathfinding/heroPathfinding.ts, packages-user/data-state/src/pathfinding/heroPathfinding.test.ts, packages/common/src/logger.json - .planning/phases/02-pathfinding/02-INTERFACE-DRAFT.md, packages-user/data-state/src/pathfinding/heroPathfinding.ts, packages-user/data-common/src/common/mover.ts, packages-user/data-base/src/hero/mover.ts, packages/common/src/logger.json, .planning/phases/02-pathfinding/02-RESEARCH.md - 实现打断接管(D-10):寻路控制器持有当前 `IMoverController` 引用;新寻路调用或显式打断入口到达时,按拍板的时序(stop 后 await 兑现,或 onEnd 回调驱动)停止旧移动,从最新坐标起算并启动新路径——注意 mover.start 移动中返回 null(mover.ts:669-670),必须先完成旧移动的停止再 start;识别 HeroMover.onStepEnd 在 CannotMove/Stop/Hit 时的自然终止,避免对已终止移动重复 stop(RESEARCH Pitfall 5)。在 logger.json 集中登记本阶段全部新码(warn ≥173、error ≥65,与 02-02 已登记码合并查重,RESEARCH Pitfall 7)。测试补充:移动中途发起新寻路,旧路径中止且新路径从最新位置起算到达新目标;显式打断入口等效。最后跑阶段门禁并修复本阶段文件暴露的问题。 + Task 4: 打断接管 + logger 码登记 + 阶段门禁 + packages-user/data-state/src/path/heroPathfinding.ts, packages-user/data-state/src/path/heroPathfinding.test.ts, packages/common/src/logger.json + packages-user/data-system/src/path/types.ts, packages-user/data-state/src/path/heroPathfinding.ts, packages-user/data-common/src/common/mover.ts, packages-user/data-base/src/hero/mover.ts, packages/common/src/logger.json, .planning/phases/02-pathfinding/02-RESEARCH.md + 实现打断接管(D-10):寻路控制器持有当前 `IMoverController` 引用(即 02-02 返回的 `IPathfindingController.controller`);L2 `interrupt(): Promise` 的语义在此接线——新寻路调用或显式 `interrupt()` 入口到达时,按拍板时序选项 1(stop 后 await 兑现,再起新寻路)停止旧移动,从最新坐标起算并启动新路径——注意 mover.start 移动中返回 null(mover.ts:669-670),必须先完成旧移动的停止(await 兑现)再 start;识别 HeroMover.onStepEnd 在 CannotMove/Stop/Hit 时的自然终止,避免对已终止移动重复 stop(RESEARCH Pitfall 5)。在 logger.json 集中登记本阶段全部新码(warn ≥173、error ≥65,与 02-02 已登记码合并查重,RESEARCH Pitfall 7)。测试补充:移动中途发起新寻路,旧路径中止且新路径从最新位置起算到达新目标;显式 interrupt() 入口等效。最后跑阶段门禁并修复本阶段文件暴露的问题。 - pnpm exec vitest run "packages-user/data-state/src/pathfinding/heroPathfinding.test.ts"; if ($?) { pnpm check:type 2>&1 | Select-String -Pattern "pathfinding|mover\.ts|core\.ts" } + pnpm exec vitest run "packages-user/data-state/src/path/heroPathfinding.test.ts"; if ($?) { pnpm check:type 2>&1 | Select-String -Pattern "src[\\/]path[\\/]|mover\.ts|core\.ts" } vitest 输出出现 "failed" 字样,或 Select-String 对本阶段文件输出任何类型诊断行(命中即存在计划内文件的类型错误) - 移动中发起新寻路:旧路径停止、新路径自最新坐标起算并到达新目标(行为断言) - 显式打断后 mover 处于可再次 start 的干净状态(行为断言) - logger.json 新码无撞码:warn 新码 ≥173、error 新码 ≥65 且与既有键位不重复(CLI 断言:node 枚举 logger.json 键位核对) - - `pnpm check:circular` 输出不含 pathfinding 相关路径;`pnpm lint:user` 对本阶段文件无报错(CLI 断言) + - `pnpm check:circular` 输出不含 path 相关路径;`pnpm lint:user` 对本阶段文件无报错(CLI 断言) - 打断接管按拍板时序可用;全量测试绿、循环依赖与 lint 门禁对本阶段文件清洁。 + 打断接管按拍板时序(stop 兑现后起新)可用;全量测试绿、循环依赖与 lint 门禁对本阶段文件清洁。 @@ -140,10 +167,11 @@ Output: heroPathfinding.ts + 接线与测试;更新 core.ts、双 barrel、log ## Artifacts this phase produces -- 类:`HeroPathfinding`(L3 接线类,`packages-user/data-state/src/pathfinding/heroPathfinding.ts`) -- 方法:路径→`ObjectMoveType.Dir` 步骤翻译、逐步执行(复用 `DefaultHeroMoveTopImpl` 事件链,D-09)、瞬移执行(D-04)、默认回退策略「路径上存在事件即回退」(D-05)、D-08 情形 1 相邻格 + OnTouch + 面朝、打断接管入口(D-10) -- 修改:`packages-user/data-state/src/core.ts`(CoreState 寻路接线)、`packages-user/data-state/src/index.ts`(barrel)、`packages/common/src/logger.json`(集中登记码位);`packages-user/data-system/src/index.ts` 的 pathfinding 导出已由 02-02 Task 3 落地(该文件唯一归属在 02-02),本计划仅验证不修改 -- 测试:`heroPathfinding.test.ts`(端到端逐步/瞬移/回退/D-08 双语义/打断) +- 类:`HeroPathfinding`(L3 接线类,`packages-user/data-state/src/path/heroPathfinding.ts`) +- 类/方法(重构):`DefaultHeroMoveTopImpl` 的 `predicate(): IPassPredicate`(通行性掩码逻辑提取为 `IPassPredicate` 实现,行为保持);`data-base/src/hero/mover.ts` onStepStart 改经 predicate().canPass/shouldHit +- 方法:路径步骤序列→`ObjectMoveType.Dir` 步骤翻译、逐步执行(复用 hero mover 事件链,D-09)、瞬移执行(D-04)、拍板默认回退策略「路径上存在事件即回退」经 useFallbackPolicy 注入(D-05)、D-08 情形 1 相邻格 + OnTouch + 面朝、打断接管入口(D-10) +- 修改:`packages-user/data-state/src/core.ts`(CoreState 寻路接线)、`packages-user/data-state/src/index.ts`(barrel)、`packages/common/src/logger.json`(集中登记码位);`packages-user/data-system/src/index.ts` 的 path 导出已由 02-02 Task 3 落地(该文件唯一归属在 02-02),本计划仅验证不修改 +- 测试:`heroPathfinding.test.ts`(端到端逐步/瞬移/回退/D-08 双语义/打断);moverImpl 重构以既有 coreEventLayer.test.ts 与 data-base 测试为回归护栏 ## Trust Boundaries @@ -162,15 +190,16 @@ Output: heroPathfinding.ts + 接线与测试;更新 core.ts、双 barrel、log -- `pnpm exec vitest run "packages-user/data-state/src/pathfinding/heroPathfinding.test.ts"` 全绿 +- `pnpm exec vitest run "packages-user/data-state/src/path/heroPathfinding.test.ts"` 全绿 +- `pnpm exec vitest run "packages-user/data-state/src/coreEventLayer.test.ts"` 全绿(moverImpl 重构回归护栏) - `pnpm exec vitest run` 全量通过(Phase 02 + Phase 01 既有测试无回归) -- `pnpm check:type 2>&1 | Select-String -Pattern "pathfinding|mover\.ts|core\.ts"` 无输出 -- `pnpm check:circular` 输出不含 pathfinding 相关路径(eventStore 既有循环维持 Phase 01 基线) +- `pnpm check:type 2>&1 | Select-String -Pattern "src[\\/]path[\\/]|mover\.ts|core\.ts"` 无输出 +- `pnpm check:circular` 输出不含 path 相关路径(eventStore 既有循环维持 Phase 01 基线) - `pnpm lint:user` 无新报错 -勇士在 fixture 地图上可自动寻路(逐步/瞬移/回退/不可达双语义/打断接管全部符合拍板契约);数据端入口经 barrel 导出且 Node 可验证;阶段门禁全绿。PATH-01 交付完成,PATH-02 以数据端入口形态交付(点击接线属 Phase 4,per D-11)。 +moverImpl 满足用户重构后的 IHeroMoveTopImpl(predicate(): IPassPredicate)且 Phase 1 行为零回归;勇士在 fixture 地图上可自动寻路(逐步/瞬移/回退/不可达双语义/打断接管全部符合拍板契约);数据端入口经 barrel 导出且 Node 可验证;阶段门禁全绿。PATH-01 交付完成,PATH-02 以数据端入口形态交付(点击接线属 Phase 4,per D-11)。