# 立方体世界功能文档

## 1. 文档信息

- 产品版本：3.0.0
- 开发分支：`feat/cube-world-rewrite`
- 基准提交：`ee34288b54e0c3cf94b06d4fdcc7874848a0d18b`
- 实现方式：以评审功能文档为规格独立重写，不复用或参照 `6be0ffae769c9d1f073d885c193b453e51f8c5da` 的旧实现。
- 世界尺寸：六个 13×13 正方形表面，楼层 ID 固定为 `MT0`～`MT5`。

本文既描述玩家可见行为，也规定拓扑、战斗和事件系统必须遵守的工程语义。文中“相邻”均指立方体表面的格子邻接，不是楼层数组中的前后顺序。

## 2. 产品目标

六张地图共同组成一个连续的立方体表面。玩家走出任意地图边缘时进入几何上相邻的表面；位置、朝向、射线和追猎方向均随立方体折叠关系旋转。领域、夹击、阻击、光环、支援等能力也必须跨表面生效。

核心体验包括：

1. 不依赖传送门的连续六面探索。
2. 可预测且可验证的空间旋转规则。
3. 跨面交互与本层门、怪物、道具、NPC 保持同等语义。
4. 六面共享的怪物能力与实时伤害计算。
5. 可查看全部表面的 3D 总览和移动端方向输入。
6. 不因跨面状态遗漏而错误折叠录像。

## 3. 六面坐标与拓扑

### 3.1 面定义

| 楼层 | 表面 | 屏幕右方向 `u` | 屏幕下方向 `v` | 外法向 `n` |
|---|---|---|---|---|
| MT0 | 正面 | +X | +Y | +Z |
| MT1 | 后面 | -X | +Y | -Z |
| MT2 | 左面 | +Z | +Y | -X |
| MT3 | 右面 | -Z | +Y | +X |
| MT4 | 顶面 | +X | +Z | -Y |
| MT5 | 底面 | +X | -Z | +Y |

邻面、目标入口边、沿边坐标是否反转以及跨面后的方向，全部由这组正交三维坐标基推导。业务代码不得再维护第二份邻接表或反转表。

### 3.2 24 条有向边

表格单元格式为“目标面 / 目标入口边”。

| 当前面 | 上 | 下 | 左 | 右 |
|---|---|---|---|---|
| MT0 正面 | MT4 / 下 | MT5 / 上 | MT2 / 右 | MT3 / 左 |
| MT1 后面 | MT4 / 上 | MT5 / 下 | MT3 / 右 | MT2 / 左 |
| MT2 左面 | MT4 / 左 | MT5 / 左 | MT1 / 右 | MT0 / 左 |
| MT3 右面 | MT4 / 右 | MT5 / 右 | MT0 / 右 | MT1 / 左 |
| MT4 顶面 | MT1 / 上 | MT0 / 上 | MT2 / 上 | MT3 / 上 |
| MT5 底面 | MT0 / 下 | MT1 / 下 | MT2 / 下 | MT3 / 下 |

跨面后的前进方向恒为“目标入口边的反方向”。例如：

- `MT0` 从右边离开，进入 `MT3` 左边，仍朝右。
- `MT4(12,9)` 向右离开，进入 `MT3(3,0)`，新方向为向下。

第二个例子同时发生沿边坐标反转：`t = 9` 映射为 `12 - 9 = 3`。

需要反转沿边坐标的四组物理边为：

- MT1 上 ↔ MT4 上
- MT1 下 ↔ MT5 下
- MT2 下 ↔ MT5 左
- MT3 上 ↔ MT4 右

### 3.3 唯一步进 API

`CubeWorld.createGeometry()` 提供唯一拓扑实现：

- `edge(face, direction)`：返回目标面、目标入口边、反转标记和新方向。
- `step(state, direction)`：前进一步，返回 `{ floorId, x, y, direction, crossed }`。
- `trace(state, direction, options)`：沿真实表面直线连续行进，遇到已访问的“格子 + 方向”前停止。
- `cardinalNeighbors` / `kingNeighbors`：返回表面四邻域或八邻域。
- `area` / `distance`：在六面格子图上进行去重 BFS。

相邻边长度必须相等；当前产品固定为六个 13×13 面。非法楼层、越界坐标和边长不一致会直接报错，不做模糊修正。

### 3.4 角部与去重

八邻域不是把二维坐标硬套到相邻层，而是按三维格子顶点建立共享关系。立方体一个顶点由三个面共同拥有，因此角部邻域会自然合并重复格子。

同一只怪物的领域或激光无论通过多少条路径覆盖目标格，目标格只结算一次；不同怪物仍可叠加。

## 4. 方向旋转的最终产品决策

评审文档中“旋转边是否允许追猎”存在冲突。本版本明确采用以下规则：

> 只要行为表示立方体表面上的连续直线，跨越旋转边后就继续前进，并把方向旋转为目标面的局部方向。

该规则同时适用于：

- 玩家移动；
- 激光；
- 追猎视线与追猎第一步；
- 阻击怪跨面移动时的朝向元数据。

追猎仍只检查源面和一个几何相邻面，即一条追猎射线最多跨一条边；“最多跨一条边”限制搜索范围，不表示禁止方向旋转。这样既保持相邻面设计约束，又避免玩家、激光和追猎使用互相冲突的空间模型。

### 4.1 跨面地图朝向（离散映射）

玩家跨越共享边后，目标地图不再强制以规范坐标朝上显示。运行时维护 `0/1/2/3` 四种画面朝向，并选择目标朝向，使“目标面的逻辑前进方向”映射为跨面前的同一屏幕方向。这样出口边与入口边在屏幕上相接，换面后再次按同一屏幕方向即可继续直行。

例如从 `MT4(12,9)` 向屏幕右侧离开时，几何目标是 `MT3(3,0)`，逻辑方向由右变为下。目标面采用四分之三转的理论朝向后，逻辑“下”显示为屏幕“右”，逻辑格 `(3,0)` 显示在屏幕格 `(0,9)`，玩家从画面左边进入并继续向右。

- 所谓“地图旋转”是离散的“逻辑格 → 屏幕格”坐标映射，不是画布或图片旋转。实现不得使用父容器 CSS `transform`、`canvas.rotate()` 或旋转后的中间位图。
- 朝向在目标地图绘制前一次性确定并立即生效；跨面不播放旋转动画，不插值，也不为转场临时锁住输入。
- 地形、背景/前景块、事件、勇士、怪物、物品、门、地图动画、显伤、自动拾取图标、在线留言标记和寻路提示只改变所在屏幕格。每个素材仍按原源矩形和原宽高正向绘制，文字也保持水平。
- 有方向帧的勇士或 NPC 使用“逻辑方向 → 屏幕方向”选择正常的正向素材帧，而不是旋转原帧。
- 行走、移动图块和跳跃的起止路径随地图朝向换算；跳跃的抬升属于屏幕演出，始终向画面上方，不会跟随地图转成横向。
- 地图数组、英雄坐标、事件坐标和拓扑结果始终使用规范逻辑坐标，不因显示朝向改变。
- 对话、菜单、遮罩、状态栏和 3D 总览等屏幕 UI 不参与地图格映射。
- 键盘、移动端方向键与点击寻路使用屏幕坐标输入，再反算为规范地图坐标。
- 画面朝向保存在英雄 flags 中；跨面时确定性更新，读档恢复，飞行或脚本直接换面时回到目标面的规范朝向。
- 绕立方体闭合路径产生的路径相关朝向是表面曲率的结果；录像折叠必须把该朝向视为游戏状态的一部分。

### 4.2 标准投影地图与增量绘制

地图朝向必须在数据边界完成，不能靠各画布层分别补坐标，也不能在出现残影后重绘整层来掩盖问题。每次全量绘制按当前四分之一转角生成一份只读显示投影，内容包括：

- 已换算为屏幕格坐标的标准 `blocks` 列表；
- 同步旋转后的主地图、`bg`/`fg` 二维数组，以及启用五图层插件时的 `bg2`/`fg2` 数组；
- 已换算所在屏幕格的楼层贴图；
- 有方向素材对应的屏幕方向 ID 和图块数字。

绘制期间，引擎临时把这份投影视为普通地图，完整调用原生 `bg → event → fg` 绘制流程；绘制结束立即恢复规范逻辑地图。投影对象不进入存档，不改变事件坐标、英雄坐标、碰撞判定或拓扑计算。`buildMapProjection()` 可生成当前显示投影，供测试和诊断验证。

增量行为必须与全量投影使用同一换算规则：

- `setBlock`、显示图块和全局动画队列接收投影后的标准 block；动画的每一帧都只更新同一个屏幕格，不能再按逻辑坐标绘制第二份图像。
- 战斗、拾取、隐藏图块和开门结束时，把待删除 block 投影后交给引擎原生单格清除流程；普通图块不得调用 `redrawMap()` 兜底。
- 原生清除同时处理 `event`、`event2`、全局动画记录和大图块动态画布。Autotile、模糊或阴影等引擎本来就要求全量重算的特殊图块仍沿用原生行为。
- 战斗动画在创建时把逻辑中心换算为屏幕中心，后续帧按标准动画对象播放；显伤、人物、跟随者和寻路路线也先生成屏幕坐标数据，再调用各自原生绘制函数。
- 楼层贴图的显隐 flag 永远使用规范像素坐标。投影副本保存原始坐标，绘制时按原始 flag 判断显隐，不能让投影后的坐标碰巧命中另一张贴图的 flag。
- 朝向改变时可以丢弃只属于旧屏幕坐标系的短时显示对象（地图动画、拾取动画、留言画布），随后从规范地图状态重新生成；不得借全图重绘掩盖战斗删除或普通增量绘制错位。

回归验收要求：旋转朝向下生成一个动画怪物后，整个事件画布只能在其投影格出现非透明像素；战斗结束后 `event`、`event2`、显伤、战斗动画及全局动画队列均不得在投影格、逻辑原格或其他格留下副本。

### 4.3 坐标空间契约

| 数据或入口 | 输入空间 | 处理规则 |
|---|---|---|
| 地图块、楼层贴图、显伤、`drawAnimate(..., alignWindow=false)` | 规范地图空间 | 在进入原生绘制器前投影一次；动画后续帧不再重复换算。 |
| `drawAnimate(..., alignWindow=true)`、菜单、状态栏、天气、小地图 | 屏幕空间 | 原样使用，禁止地图投影。 |
| 键盘和移动端方向键 | 屏幕方向 | 在移动入口换算为逻辑方向，录像记录逻辑方向。 |
| 鼠标、触摸像素 | 屏幕空间 | UI 分发始终接收原始像素；只有地图寻路处理器把目标格换算为逻辑格。 |
| 规范地图缩略图（楼传、中心飞、忽略物品） | 独立 UI 地图空间 | 保持规范朝向，点击坐标不使用当前主画面朝向。 |
| 覆盖当前主画面的点选层 | 屏幕格 | 在查询事件或怪物前反算为逻辑格。 |

玩法设置允许在运行时切换“跳过演出”。恢复演出时即使引擎重新绑定 `drawAnimate`，也必须恢复立方体投影版入口；设置键统一为 `skipPerform`，仅对旧存档兼容读取历史误拼 `skipPeform`。该重绑定不得使战斗动画退回逻辑原格。

## 5. 玩家跨面行为

### 5.1 触发条件与通行检查

玩家站在面边缘并继续向外输入方向时，运行时使用 `step()` 计算目标格。跨面前依次检查：

1. 源坐标的 `cannotMove`；
2. 源背景、前景和事件图块的 `cannotOut`；
3. 目标坐标的 `cannotMoveIn`；
4. 目标背景、前景和事件图块的 `cannotIn`；
5. 目标格事件、障碍及致死额外伤害。

键盘与触控输入先从屏幕方向换算为规范地图方向，录像记录换算后的逻辑方向。回放时重新调用拓扑 API，并由每次跨面确定性更新地图朝向，因此无需在路线中额外编码目标面或视觉转场；存档则通过英雄 flag 保存当前四分之一转角。

### 5.2 目标格语义

| 目标类型 | 行为 |
|---|---|
| 空地 | 立即换面并进入目标格，执行一步后的伤害和事件检查。 |
| 可拾取道具 | 进入目标格并且只结算一次道具效果；事件队列存在时也不会把道具遗留在脚下。 |
| 门 | 使用目标门自己的钥匙与开门回调；开门后玩家留在原面，下一次方向输入才跨面。 |
| 可战斗怪物 | 复用面内碰触战斗；在怪物所属面完整结算，胜利后玩家留在原面，下一次方向输入才进入目标格。 |
| 不可战斗怪物 | 提示失败并留在原面。 |
| NPC / 自定义事件 | 复用面内碰触事件；事件在目标面完整执行，玩家留在原面，下一次方向输入再检查并跨面。 |
| 箱子 | 不跨面推动，玩家留在原面。 |
| 其他不可通行块 | 记录本次方向尝试并留在原面。 |

跨面只负责把拓扑目标以显式 `floorId` 交给原生碰触事件分发。远程事件数组仍以“压入远程事件坐标 → 执行动作 → 恢复原坐标”的 LIFO 上下文执行；嵌套事件不会把楼层或坐标恢复到错误层。

### 5.3 显式楼层事件 API

以下引擎接口增加可选 `floorId`，跨面逻辑不得通过临时篡改 `core.status.floorId` 来执行远程事件：

```text
trigger(x, y, callback, floorId)
battle(id, x, y, force, callback, floorId)
openDoor(x, y, needKey, callback, floorId)
getItem(id, num, x, y, isGentleClick, callback, floorId)
insertAction(action, x, y, callback, addToLast, floorId)
```

`beforeBattle`、`afterBattle`、`afterOpenDoor`、`afterGetItem`、`setEvents` 和 `startEvents` 同样携带楼层上下文。对应 TypeScript 声明已同步。

### 5.4 跨面工具

- 破墙镐：可破坏正对相邻面的 `canBreak` 图块。
- 破冰镐：通过目标面的正式开门管线处理冰墙，保留开门音效和 `afterOpenDoor`。
- 炸弹：可炸掉正对相邻面的可炸怪物；`notBomb` 仍生效。
- 使用成功后只消耗一次道具并记录 `item:<id>` 路线；失败沿用原道具行为。
- `canUseItem()` 同样识别相邻面目标，工具栏不会错误置灰。

## 6. 跨面怪物机制

### 6.1 领域

领域以怪物所在格为起点，在六面格子图上按范围 BFS：

- 普通领域使用四邻域，对应表面菱形距离；
- `zoneSquare` 使用共享顶点构造的八邻域；
- 每只怪物对同一目标格最多命中一次；
- 多只怪物的领域可以叠加。

### 6.2 激光

激光从怪物向四个局部方向发射，跨边后按真实表面旋转继续传播。射线在即将进入已访问的“格子 + 方向”时停止，因而不会在封闭立方体上无限循环，也不会把发射源再次算作终点。

同一只激光怪的四条射线如果覆盖同一格，只结算一次激光伤害。

### 6.3 追猎

追猎规则如下：

1. 从怪物的上、右、下、左四个方向建立表面射线。
2. 射线最多跨一条边，允许跨旋转边并继续沿旋转后的方向前进。
3. 射线可穿过空地和不带 `event.data` 的普通物品；开启全塔开关 `chaseThroughEnemy` 后，也可穿过其他怪物。墙、门、NPC、带 `event.data` 的物品及其他事件图块会立即终止射线。
4. 怪物朝玩家所在射线移动一步，跨边时完整搬运到相邻面。
5. 下一步是可穿过的普通物品时，怪物与物品原子换位：怪物进入物品格，物品回到怪物原格。跨面换位保留双方块级事件、透明度、滤镜和自定义字段，物品素材保持正向；地图数据中不允许怪物与物品长期重合，也不得覆盖删除物品。
6. 开启 `chaseThroughEnemy` 且下一步是其他怪物时，按同样原则安全换位；未开启时怪物会阻挡追猎。
7. 同一怪物存在多个等距最短方向时不移动，避免依赖遍历顺序随机选边。
8. 追猎移动前后都检查玩家四邻格；进入相邻格的追猎怪会在其真实所属面发起战斗。

### 6.4 阻击与捕捉

- 阻击范围按四邻域或八邻域跨面计算。
- 玩家受伤后，阻击怪沿远离玩家的表面方向移动一格，可跨到相邻面；阻击落点必须为空，不继承追猎的物品或怪物换位规则。
- 搬运保留块级事件、透明度、滤镜、禁用状态和自定义字段。
- 具有朝向图块集的对象会更新事件 ID、图块数字和地图数组，三者保持一致。
- 执行移动前复核源 ID 和目标占用，陈旧记录不能误搬后来进入源格的其他对象。
- 捕捉怪可从相邻面发起战斗；不可见面的逻辑结果仍完整结算。

### 6.5 夹击

玩家左右或上下的表面相邻格，即使分属不同面，也能组成夹击。夹击要求两端是同一种、具有夹击能力的怪物。

夹击伤害依赖当前生命以及同一步的其他额外伤害，因此六面世界每走一步都从实时状态重新计算。生命、开关或地图变化后不会复用上一格结果。

### 6.6 光环与支援

- 无范围光环作用于完整六面。
- 有范围光环使用表面四邻域或八邻域距离。
- 同 ID 光环默认不叠加；`haloAdd` 允许叠加。
- 支援使用表面八邻域，支援记录包含 `{ floorId, x, y, id }`。
- 战斗公式、奖励、支援怪移除和战后事件均读取各自真实楼层。

### 6.7 吸噬

特殊属性 31 为“吸噬”，比例字段为 `absorbValue`，合法范围为 0～1。

只有怪物攻击实际穿透护盾、扣到玩家生命的部分才会回复怪物生命：

```text
本次回复 = 本次实际生命伤害 × absorbValue
本次实际生命伤害 = max(0, 攻击伤害 - 本次可用护盾)
```

护盾按战斗时间线逐次消耗，不是最后统一分摊。完整结算顺序为：

1. 吸血等非攻击战前伤害消耗护盾，但不触发吸噬；
2. 先攻消耗护盾，穿透部分可触发吸噬；
3. 破甲、净化消耗剩余护盾，但不触发吸噬；
4. 每个勇士攻击回合先结算反击吸噬，再结算勇士攻击；
5. 怪物未死亡时再结算本回合普通攻击吸噬；
6. 最后一击所在回合仍会触发反击，与最终伤害公式保持一致。

模拟器只用回复后的生命求实际回合数，返回给怪物手册和临界计算的 `mon_hp` 仍是原始怪物生命。护盾耗尽后若每轮怪物生命不再净下降，则战斗判定为不可获胜，而不是依赖固定次数死循环。

## 7. 实时计算与缓存策略

立方体六面上的额外伤害和怪物属性依赖：

- 当前生命；
- 怪物位置及跨面搬运；
- 领域、激光、追猎、夹击等开关；
- 点位怪物覆盖；
- 光环与支援来源；
- 道具和地图块变化。

因此六面楼层采用“正确性优先”策略：

- `buildCheckBlock(floorId)` 每次生成全新结果，`needCache` 恒为 `false`；
- 每走一步，在目标格事件与额外伤害结算前重建一次；
- 立方体楼层不使用原引擎的光环 `checkBlock.cache`；
- 怪物搬运、工具破坏和战斗后主动刷新显伤与 3D 总览。

非立方体楼层仍调用原引擎逻辑，不改变其缓存行为。

## 8. 地图与关卡内容

### 8.1 初始配置

- 英雄：12000 HP、20 攻、20 防、0 魔防。
- 初始道具：2 把黄钥匙、1 把蓝钥匙，以及基础常量道具。
- 宝石：红/蓝宝石 +4，绿宝石 +30 魔防。
- 药水：500 / 1200 / 2500 / 5000 HP。
- 金币商店和经验商店使用独立 ID：`moneyShop`、`expShop`。

### 8.2 六面职责

| 表面 | 内容定位 | 闭环 |
|---|---|---|
| MT0 正面 | 出生与教学；领域、光环、支援、激光示例 | 边缘均有可跨面入口，向导说明旋转与去重。 |
| MT1 后面 | 追猎、吸噬测试怪、最终 Boss | `tulipFairy` 同时具有阻击、激光、吸噬；击败后胜利。 |
| MT2 左面 | 四名初级卫兵、经验商店 | 四名卫兵全部清除后同时打开中央四扇机关门。 |
| MT3 右面 | 四名初级卫兵、金币商店 | 四名卫兵全部清除后同时打开中央四扇机关门。 |
| MT4 顶面 | 四名骷髅士兵、装备宝库 | 四名战士全部清除后打开四扇门，可取得铁剑、铁盾和红钥匙。 |
| MT5 底面 | 四名初级法师、四名石头人、中央奖励 | 八个目标全部清除后打开四扇机关门；跨面击杀后也会实时复核。 |

静态连通性测试确认：所有机关外目标和最终 Boss 均可从出生点经六面表面到达；机关关闭时四个中央区不可进入，清怪开门后均可进入。

## 9. 3D 总览与输入

### 9.1 入口与关闭

- 按 `C` 打开六面总览；再次按 `C` 或按 `Esc` 关闭。
- 状态栏飞行器图标改作“六面总览”入口。
- 查看器关闭后焦点回到游戏页面。

### 9.2 查看器行为

- 使用 CSS 3D 按与几何坐标基一致的朝向摆放六个面。
- 每个面绘制引擎缩略图、怪物和该面的实时额外伤害。
- 勇士所在面使用黄色边框，并绘制勇士位置。
- 支持拖动旋转、滚轮缩放、双指缩放和按钮对准指定面。
- iframe 仅在第一次打开时加载；隐藏期间没有 `requestAnimationFrame` 或后台帧循环。
- 地图绘制失败时只显示错误提示，关闭查看器后游戏仍可继续。

启动阶段图片请求失败时，加载器会记录具体资源，并用保持核心图集尺寸的透明占位图继续初始化；标题页上方显示可关闭的错误提示。单张素材失败不会让资源计数卡死或阻止进入游戏。

### 9.3 移动端

窄屏或粗指针设备显示四方向触控键。方向键始终表示玩家看到的屏幕方向，由六面运行时换算为当前地图的逻辑方向。每次有效指针按下只发送一次引擎 `keydown`，抬起、取消或丢失捕获只发送一次 `keyup`，避免重复释放或粘键。

## 10. 录像折叠

录像折叠默认启用，但只有位置索引和完整状态指纹都一致时才复用旧前缀。

位置索引包括：

- 当前面；
- X、Y；
- 朝向。

状态指纹包括：

- 英雄数值属性（步数除外）；
- flags、道具、装备、跟随者；
- 商店状态；
- 全局开关和全局数值；
- 六面地图数组、背景/前景、楼层删除与可见状态；
- 完整块事件、启用状态、透明度和滤镜；
- 点位怪物覆盖、访问/隐藏楼层及毒、衰、冻、乱、咒等隐藏状态。
- 当前四分之一画面转角；同一面、坐标和勇士方向但画面朝向不同，不得折叠。

对象键和图块数组顺序会规范化，不会因无语义的顺序变化阻止折叠。状态中出现函数、循环引用等无法稳定序列化的内容时，本次折叠直接禁用并清空候选，不使用可能与其他错误状态碰撞的共享哨兵。

## 11. 工程结构

| 文件 | 职责 |
|---|---|
| `project/cube.js` | 无引擎依赖的三维拓扑、表面图、视图方向换算、稳定序列化和吸噬模拟器。 |
| `project/cube-runtime.js` | mota-js 适配、跨面移动/交互、离散地图坐标映射、屏幕输入换算、怪物机制、工具与 UI 入口。 |
| `libs/events.js` | 带显式 `floorId` 的事件、战斗、开门、拾取与远程动作上下文。 |
| `project/functions.js` | 战斗、支援、奖励、吸噬公式和特殊属性说明。 |
| `libs/control.js` | 完整状态录像折叠指纹。 |
| `libs/loader.js` | 批量动画参数修复，以及图片失败占位、记录和可继续启动策略。 |
| `cube-map-viewer.html` | 懒加载六面 3D 总览。 |
| `project/floors/MT0.js`～`MT5.js` | 六面关卡内容与机关事件。 |
| `tests/unit/` | 拓扑、角部、公式、内容闭环和录像折叠测试。 |
| `tests/e2e/` | 真实页面、跨面事件、怪物机制、3D 和移动端浏览器测试。 |

## 12. 验收标准与证据

| 验收项 | 当前结果 |
|---|---|
| 唯一带方向转换的步进 API | 已完成；业务逻辑统一调用 `CubeWorld`。 |
| 24 条边逐格往返 | 24×13 个边坐标全部单测通过。 |
| 连续跨多边与角部去重 | 多边旋转闭环、三面共点邻域单测通过。 |
| 领域/激光目标格去重 | 单元结构验证并由浏览器激光场景验证。 |
| HP、开关、地图变化后实时更新 | 夹击 1000→500、800→400 的浏览器断言通过；六面禁用旧缓存。 |
| MT5 清怪开四门 | 内容闭环和真实动作队列浏览器测试通过。 |
| 吸噬完整公式 | 护盾、先攻、反击、最终一击、不可获胜五类断言通过。 |
| 跨面门/战斗/NPC/道具/工具 | 真实页面端到端测试通过。 |
| 跨面地图与输入连续 | 顶面右侧进入右面后采用四分之三转理论朝向；键盘继续向右、映射后点击寻路及读档恢复均有浏览器断言。 |
| 素材正向且无旋转动画 | 非对称四色图块在目标屏幕格与源像素逐字节相等；原逻辑格为空，且 DOM 中没有旋转容器、旋转矩阵或过渡时长。 |
| 旋转边追猎 | 明确允许并旋转；激光/追猎联合浏览器测试通过。 |
| 追猎穿过普通物品 | 同面与跨面均换位且保留双方元数据；带 `event.data` 的物品仍阻挡，阻击落点仍须为空。 |
| 启动图片失败降级 | 拦截核心 `icons.png` 后仍可进入 MT0，并显示明确资源错误。 |
| 测试体系 | Vitest 与 Playwright 独立匹配，互不误收集。 |
| 仓库卫生 | 测试产物已忽略，依赖锁文件保留，无一次性硬编码脚本。 |

本地验收命令：

```bash
npm ci
npm test
npm run test:e2e
```

截至本文本次更新时，单元测试为 26 项，Chromium 端到端测试为 14 项；最终交付数字以命令实际输出为准。

## 13. 已知边界

- 当前仅支持六个边长相同的正方形面；产品内容固定为 13×13。
- 追猎最多跨一条边，不会隔着两个以上表面锁定玩家。
- 3D 总览是只读视图，不在总览内直接移动或触发事件。
- 玩家同一时刻只能看到当前面，因此相邻面的支援、捕捉和搬运动画采用当前面可表达的反馈；逻辑、奖励和地图状态仍在真实来源面结算。
- 六面额外伤害选择实时重算以保证正确性；若未来出现性能压力，应以依赖版本号做可证明失效的缓存，不得恢复无条件复用。
