118 lines
14 KiB
Markdown
118 lines
14 KiB
Markdown
# 地图回合实现进度(台账)
|
||
|
||
本文档记录**代码已落地内容**与**待接线任务**,便于在对话上下文变长时单独查阅。规则、伪代码与冻结优先级以 [map-turn-spec.md](map-turn-spec.md) 为唯一口径;本文不写新规则,只写状态与文件指针。**`patch` / `rebuild` / `performEnemyAction` 等与实现对齐的契约说明**见规范 **§6.2、§6.3、§6.7**。
|
||
|
||
---
|
||
|
||
## 1. 已完成(截至当前仓库)
|
||
|
||
### 1.1 数据表(`timeCost` 默认 `null`)
|
||
|
||
| 区域 | 说明 |
|
||
|------|------|
|
||
| [project/items.js](../project/items.js) | 每条道具在 `"cls"` 下一行含 **`"timeCost": null`**;需消耗地图时间时改为正整数。**样例(已接)**:`bigKey`(`tools`)**`timeCost: 3`**,见 §1.3。 |
|
||
| [project/enemys.js](../project/enemys.js) | 每条怪物在 **`point` 与 `special` 之间**含 **`"timeCost": null`**。 |
|
||
| `enemy48` | 与 `enemys` 共用 **`core.material.enemys`** 数值,**无**独立 `libs/enemys48.js` 数据表。 |
|
||
|
||
### 1.2 引擎初始化缺省补全
|
||
|
||
| 文件 | 行为 |
|
||
|------|------|
|
||
| [libs/items.js](../libs/items.js) `items.prototype._init` | 若道具 **`timeCost === undefined`**,补 **`timeCost: null`**(含编辑器新注册道具漏字段)。 |
|
||
| [libs/enemys.js](../libs/enemys.js) `enemys.prototype._init` | 若怪物 **`timeCost === undefined`**,补 **`timeCost: null`**。 |
|
||
|
||
### 1.3 使用道具推进地图时间
|
||
|
||
| 文件 | 行为 |
|
||
|------|------|
|
||
| [libs/items.js](../libs/items.js) `items.prototype.useItem` | 在 **`_useItemEffect`**(含 eval `useItemEffect`、并入队 `useItemEvent`)**之后**立即结算:若 **`typeof timeCost === "number"` 且 `> 0`**,且存在 **`core.plugin.mapTurn.consumeTime`**,则 **`consumeTime(_tc, "item:" + itemId)`**(如 **`item:bigKey`** → 3 tick)。**例外**:**`cls`** 为 **`skillbattle`** / **`skillupdate`**,或 **`mapTurnSkillRole`** 为 **`battle`** / **`update`** 时**不**走本条即时扣时(与规范 §3.1 对齐;工程上可用 **`cls:"tools"`** + **`mapTurnSkillRole`** 代替未注册的 skill cls),地图时间语义见 §1.4 / [map-turn-spec.md](map-turn-spec.md) §3.1。`null` 或非正数不推进;**与异步开门动画无关**(规范 §4 tools)。总开关关则 **`consumeTime`** 内早退。 |
|
||
|
||
### 1.4 插件:`core.plugin.mapTurn`
|
||
|
||
| 文件 | 说明 |
|
||
|------|------|
|
||
| [project/plugins.js](../project/plugins.js) 插件键 **`"mapTurn"`** | 挂载 **`this.mapTurn = { ... }`**,对外即 **`core.plugin.mapTurn.*`**。 |
|
||
|
||
已实现的 API(名称与 [map-turn-spec.md](map-turn-spec.md) §3.4 / §6 对齐):
|
||
|
||
- **`isEnabled` / `setEnabled(v)`**:总开关读写 **`flags.mapTurnEnabled`**(随存档);为真时 **`consumeTime` / `advanceMapTurnOne`** 才会推进状态。`setEnabled(true)` 或 **`bootstrapPersistedState()`** 在开关为真时会补齐 `mapTurnState` / `skillState` 缺省结构。
|
||
- **`consumeTime(deltaTime, reason)`**:`clock += floor(delta)`,再循环 **`deltaTime` 次** **`advanceMapTurnOne`**(§2.1)。
|
||
- **`advanceMapTurnOne(reason)`**:`mapTurn += 1`,调用 **`resolveEnemyActionsForSingleTick`**。
|
||
- **`resolveEnemyActionsForSingleTick(reason)`**:已按 §6.2 遍历 **`activeEnemiesByFloor[currentFloor]`**,维护 **`enemyActionGauge[floorId][runtimeId]`**,达阈调用 **`performEnemyAction`**;本函数路径内不调用 **`extractBlocks`**。
|
||
- **`performEnemyAction(enemyRef, def, floorId, reason)`**:已挂载;**特殊 29【庇护】(测试)**:`hasSpecial(..., 29)` 时本层所有怪 **`enemyOnPoint.hp += 1`**,不经改图块;否则初版 **`actType === idle`** 无操作;`chase`/`patrol`/`skill` 占位(同步改图块会经引擎 `removeBlock` 触发全表扫描,故不在此 tick 内实现)。
|
||
- **`rebuildActiveEnemies(floorId)`**:对该层 **`extractBlocks` 一次** 后遍历 **`map.blocks`**,收录 **`cls` 以 `enemy` 开头** 且 **`enemys[id].timeCost` 为正数** 的实例(**`runtimeId` / `x` / `y` / `enemyId` / `def`**),写回 **`activeEnemiesByFloor[floorId]`**,**`activeEnemiesVersion++`**,并修剪 **`enemyActionGauge[floorId]`**。换层由 [project/plugins.js](../project/plugins.js) 对 **`afterChangeFloor`** 的包装调用。
|
||
- **`patchActiveEnemiesForBlockChange(floorId, hint)`**:`hint.op` 为 **`removeCell`** / **`syncCell`** / **`migratePoint`** / **`rebuild`**(默认全量 **`rebuildActiveEnemies`**)。已在 **`maps.removeBlock` / `setBlock` / `hideBlock` / `showBlock` / `removeBlockByIndexes`** 与 **`events.moveEnemyOnPoint`** 挂钩;**`moveBlock` / `jumpBlock`** 用深度计数抑制中途 **`removeBlock`/`setBlock`** 的重复同步,**`keep===false`** 结束时补 **`removeCell`** 起点格;**`migratePoint`** 在 **`moveEnemyOnPoint`** 后迁移 **`runtimeId` 与 `enemyActionGauge`**。
|
||
- **战后成功路径**(同文件内 **「序章追击」** 等对 **`events.prototype.afterBattle`** 的包装):先调用原 **`afterBattle`**;若勇士仍存活且存在 **`mapTurn`**,则 **`consumeTime(settleBattleTimeCost(), "battle")`**,再 **`applyStatusAfterBattle("success")`**。
|
||
- **`settleBattleTimeCost`**:返回 **`max(1, M)`**(§6.4)。**M** = **`activeStatusSkills`** 各条 **`battleTimeCost`** 中的最大值,与 **`activeBattleSkillId`** 对应道具在 **`cls === "skillbattle"`** 或 **`mapTurnSkillRole === "battle"`** 时的 **`timeCost`** 取较大(二者仅取一层 max,再与基础 **1** 取 max)。
|
||
- **`syncBattleSkillItemState` / `resyncBattleSkillItemStates`**:战斗技能道具与 **`flag:skill`**、**`activeBattleSkillId`** 对齐(约定 **`skillN`** 型 id);**`useItem`** 末尾与读档 **`bootstrapPersistedState`** 内 **`resync`** 调用。**`migrateSkillItemBuckets`**:读档开头将 **`constants`** 或旧 **`skillbattle`/`skillremote`/`skillrecover`/`skillupdate`** 桶中、但 **`material`** 已改为 **`cls:"tools"`** 且带 **`mapTurnSkillRole`** 的条目迁入 **`tools`**;仍保留从 **`constants`** 迁往 **`skill*`** 桶的旧逻辑以兼容旧表。
|
||
- **`applyStatusAfterBattle`**:§6.5,按 **`remainBattles`** 等扣减 **`activeStatusSkills`**(**战后成功路径已调用**)。
|
||
- **`clearOnDeath`**:§6.6 清理 **`skillState`** 等逻辑已实现;**调用侧仍缺**:需在 **`lose` / 战斗失败** 出口调用(当前仓库未接,见 §3)。
|
||
- **道具栏**:[libs/ui.js](../libs/ui.js) **`getToolboxItems("tools")`** 与 **`getToolboxPermanentItems`**(**`constants`** + 四类技能 **`cls`**)分两行;**地图回合技能线若暂用 `cls:"tools"`**,走 **消耗道具行** 显示(与 **`bigKey`** 同区)。
|
||
|
||
### 1.5 编辑器 / 新素材注册
|
||
|
||
| 文件 | 说明 |
|
||
|------|------|
|
||
| [_server/table/comment.js](../_server/table/comment.js) | 道具表、怪物表 **`timeCost`** 列说明;**`items_template`** / **`enemys_template`** 含 **`timeCost: null`**。道具 **`cls`** 可选 **`skillbattle` / `skillremote` / `skillrecover` / `skillupdate`**(与 [map-turn-spec.md](map-turn-spec.md) §3.1 一致)。**工程临时**:未在编辑器注册 skill cls 时,可用 **`cls:"tools"`** + **`mapTurnSkillRole`**(`battle`/`remote`/`recover`/`update`)+ **`mapTurnKeepAfterUse: true`**(使用后不占消耗、**`addItem` 上限 1**),与 [libs/items.js](../libs/items.js) **`useItem`** / **`_afterUseItem`** 对齐。 |
|
||
|
||
### 1.6 当前接线口径补充(2026-04)
|
||
|
||
- **总开关开启时机**:已在 [project/data.js](../project/data.js) 的 **`startText` 前两条**:**`setValue`** 写入 **`flag:mapTurnEnabled`** 为真,再 **`bootstrapPersistedState()`** 补齐结构。开关存于存档,读档后随存档恢复。
|
||
- **首次触发路径约束**:新开局统一经过 `startText` 写入总开关;读档后由 [project/functions.js](../project/functions.js) **`loadData`** 调用 **`bootstrapPersistedState()`** 与存档中的 **`mapTurnEnabled`** 对齐。
|
||
- **演出移动与地图回合**:事件编辑器「地图处理」中在「无视地形移动勇士」下增加图块 **「移动勇者」**(JSON `type`: **`moveHeroMapTurn`**)。运行时由 [libs/events.js](../libs/events.js) **`_action_moveHeroMapTurn`** 将路径拆成每格一次 `eventMoveHero`,每格落点后 **`consumeTime(1, "event:moveHeroMapTurn")`**(`speed:` 与 `:0` 段不扣时)。语法见 [_server/MotaAction.g4](../_server/MotaAction.g4) `moveHeroMapTurn_s`。
|
||
- **分层相对回合约定**:`mapTurn` 以楼层相对值使用;**普通切层**后当前层回合计数按 `0` 起算。**读档**导致的换层(`__fromLoad__` 为真)不归零,以保持与存档一致。`clock` 可继续作为全局累计观测值。
|
||
|
||
---
|
||
|
||
## 2. 待完成(接线清单,对齐 map-turn-spec §4 / §6 / §8)
|
||
|
||
核心调度接线见 **§1**(含 §1.4、§1.6)。下表 **§2** 为大块任务勾选;细项与「仍缺」见 **§2.1**、**§3**。
|
||
|
||
- [x] **`resolveEnemyActionsForSingleTick`**:已在 [project/plugins.js](../project/plugins.js) **`mapTurn`** 中实现 §2 该条所列 1–4 步(含 **`performEnemyAction`**,`idle` 已实现,其余 `actType` 占位)。
|
||
|
||
- [x] **`rebuildActiveEnemies`**:已在 **`mapTurn.rebuildActiveEnemies`** 实现 §2 该条 1–4 步;**`afterChangeFloor`** 中已调用 **`rebuildActiveEnemies(core.status.floorId || floorId)`**。
|
||
|
||
- [x] **`patchActiveEnemiesForBlockChange`**:已在 **`mapTurn.patchActiveEnemiesForBlockChange`** 实现 **`removeCell` / `syncCell` / `migratePoint` / `rebuild`**;[project/plugins.js](../project/plugins.js) 在 **`mapTurn`** 插件末尾对 **`core.maps`** / **`core.events`** 安装挂钩(含 **`moveBlock`/`jumpBlock`** 与 **`keep===false`** 补清)。
|
||
|
||
### 2.1 后续扩展接线备忘
|
||
|
||
对应 [map-turn-spec.md](map-turn-spec.md) 中已有字段或流程的操作指向;**已接与仍缺分列如下**。
|
||
|
||
**已接(当前仓库)**
|
||
|
||
- **战斗技能(`skillbattle` 或 `mapTurnSkillRole:"battle"`)**:样例 **`skill1`**([project/items.js](../project/items.js),当前 **`cls:"tools"`** + **`mapTurnSkillRole:"battle"`** + **`mapTurnKeepAfterUse`**,**`timeCost: 3`**,id **`skill1`** ↔ **`flag:skill === 1`**);[libs/items.js](../libs/items.js) **`useItem`** 对 **`battle`/`update` 角色**跳过即时 **`consumeTime`**;战后 **`settleBattleTimeCost`**、**`syncBattleSkillItemState`** / **`resyncBattleSkillItemStates`**、**`migrateSkillItemBuckets`**(§1.4);**道具栏**走 **`tools`** 行(§1.4)。
|
||
- **`bigKey`**: **`tools`** + **`timeCost: 3`**,§1.3 即时 **`consumeTime`**(已验证)。
|
||
|
||
**仍待扩展**
|
||
|
||
- **`skillremote` / `skillrecover` / `skillupdate`(规范 cls)**:编辑器注册与 **`activeStatusSkills`** 战后路径仍可按规范扩展;**仓库内示例** **`I347`/`I348`/`I350`** 已用 **`tools` + `mapTurnSkillRole`** 实现拾取/使用时轴与道具栏(**`I350`** 仍为 **`mapTestAtkLayers` + tick 衰减**,非 **`activeStatusSkills`**)。
|
||
- **`contactBattleOnly` 与多怪捕捉**:在战斗触发逻辑(如 **`beforeBattle`** 或等价入口)读取怪物字段分支;多怪连续战斗时**每场成功**需调用 **`applyStatusAfterBattle("success")`**(普通单场已接,见 §1.4 **战后路径**)。
|
||
- **`actType` / `actArgs`**:在 **`performEnemyAction`**(或由 **`resolveEnemyActionsForSingleTick`** 内联)按怪物表字段分支执行移动/技能等行为(**`chase`/`patrol`/`skill`** 等仍占位,§1.4)。
|
||
|
||
---
|
||
|
||
## 3. 验收清单映射(对应 map-turn-spec §10)
|
||
|
||
| §10 条目 | 依赖 / 当前状态(台账口径) |
|
||
|----------|----------------|
|
||
| 状态技能不立即推进时间;战后层数 `-1` | **`applyStatusAfterBattle`** 已在单场战后成功路径调用(§1.4)。**规范路径仍缺**:**`cls:"skillupdate"`** 写入 **`activeStatusSkills`**(含 **`remainBattles`** / **`battleTimeCost`**)的配置与开关脚本。**`I350`** 为测试向 **`mapTestAtkLayers` + tick 衰减**,不覆盖 **`activeStatusSkills`** 验收。 |
|
||
| 远程/恢复立即推进;怪物按时间响应 | **`useItem`** 对 **`remote`/`recover` 角色**(或规范 **`skillremote`/`skillrecover`**)与普通 **`tools`** 一样按正数 **`timeCost`** **`consumeTime`**(§1.3)。示例 **`I347`/`I348`**(§2.1)。**敌调度**:**`resolveEnemyActionsForSingleTick`** + **`activeEnemies`** 已实装(§1.4)。 |
|
||
| 战斗耗时 **`max`** | **`afterBattle`** 成功路径已 **`consumeTime(settleBattleTimeCost(), "battle")`**(§1.4);含 **`skill1`**(**`battle` 角色**)与 **`activeStatusSkills`**。回归:全场战斗均走该 **`afterBattle`** 包装即可确认。 |
|
||
| 多怪捕捉逐场扣层 | **单场** **`afterBattle`** 已链 **`applyStatusAfterBattle`**。**仍缺**:多怪连战的 **`afterBattle`** 触发次数 / 事件链是否每场独立成功分支(见 §2.1)。 |
|
||
| Boss **`contactBattleOnly`** | **仍缺**:战斗触发层读取 **`contactBattleOnly`** 与单场结算分支(§2.1)。 |
|
||
| 死亡清理 | **`clearOnDeath`** 已实现;**仍缺**:在 **`events.lose`** 或等价失败出口调用(§1.4)。 |
|
||
| 存档读档连续 | **`flags.mapTurnState` / `skillState`** 等随档;读档后 **`bootstrapPersistedState`** + **`afterChangeFloor`** 侧 **`rebuildActiveEnemies`**(§1.6)。 |
|
||
| **`timeCost=0`** 不参与调度 | **`rebuildActiveEnemies`** 仅收录 **`timeCost > 0`**;**`resolveEnemyActionsForSingleTick`** 只扫缓存列表(§1.4)。 |
|
||
| **`consumeTime(3)`** 恰好 3 tick | **`consumeTime`** 内循环 **`advanceMapTurnOne`** 已实现;**建议**:对 **`bigKey`** 或脚本 **`consumeTime(3)`** 做日志/计数断言回归(§1.3)。 |
|
||
| **`activeEnemies` 路径无每 tick 全图扫描** | **`resolveEnemyActionsForSingleTick`** 不 **`extractBlocks`**;地图变更走 **`patch`/`rebuild`**(§1.4)。**例外**:**庇护 29** 每次行动 **`extractBlocks`** 一次(测试专用,§1.4)。 |
|
||
|
||
---
|
||
|
||
## 4. 相关路径速查
|
||
|
||
| 用途 | 路径 |
|
||
|------|------|
|
||
| 规范全文 | [docs/map-turn-spec.md](map-turn-spec.md) |
|
||
| 样板 API | [_docs/api.md](../_docs/api.md) |
|
||
| 地图回合插件 | [project/plugins.js](../project/plugins.js) 搜索 **`"mapTurn"`** |
|