# 地图回合实现进度(台账) 本文档记录**代码已落地内容**与**待接线任务**,便于在对话上下文变长时单独查阅。规则、伪代码与冻结优先级以 [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"`** |