From 4c48b63e7e693d6a250f3754c33e474e44b153ab Mon Sep 17 00:00:00 2001 From: unanmed <1319491857@qq.com> Date: Wed, 6 May 2026 19:57:11 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E5=9C=B0=E5=9B=BE=E5=88=86?= =?UTF-8?q?=E5=8C=BA=E4=B8=8E=E5=B0=BA=E5=AF=B8=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/dev/map-store-improve.md | 212 ++++++++++++++++++ docs/dev/map-store-save.md | 50 ++--- packages-user/data-base/src/map/layerState.ts | 30 ++- packages-user/data-base/src/map/mapStore.ts | 119 ++++++++-- packages-user/data-base/src/map/types.ts | 98 +++++--- packages-user/data-state/src/core.ts | 16 +- packages/common/src/logger.json | 1 + 7 files changed, 441 insertions(+), 85 deletions(-) create mode 100644 docs/dev/map-store-improve.md diff --git a/docs/dev/map-store-improve.md b/docs/dev/map-store-improve.md new file mode 100644 index 0000000..f477b92 --- /dev/null +++ b/docs/dev/map-store-improve.md @@ -0,0 +1,212 @@ +# 需求综述 + +本次改动目标: + +1. **自动化分区激活器**:将楼层按游戏进程划分为若干"区域", + 到达新区域时自动激活对应楼层、失活旧区域楼层, + 从而替代目前繁琐的手动 `setActiveStatus` 调用。 +2. **楼层尺寸上移至 `LayerState`**:同一楼层的所有图层尺寸应当 + 保持一致,因此将尺寸的权威来源从 `MapLayer` 移至 `LayerState`。 + +--- + +# 实现思路 + +## 1. 有序地图 id 列表 + +当前 `IMapStore.maps` 是 `ReadonlySet`,无序且随楼层创建 +自动填充。区域功能需要以**下标**标识范围,因此需改为有序数组。 + +修改方案: + +- `IMapStore.maps` 类型改为 `ReadonlyArray`; +- 新增 `setMapList(maps: string[]): void`,由外部显式指定有序列表 + (一般在游戏初始化时调用一次); +- 新增 `useManualOrder(sort: (arr: string[]) => string[]): void`, + 允许自定义地图列表排序函数。调用时将当前 `maps` 的 `slice` 拷贝 + 传入 `sort`,再对输出做合法性校验:将新旧数组各转为 `Set`, + 校验 `size` 相等且新集合是旧集合的子集(利用 `Set.prototype.isSubsetOf`); + 校验通过后用返回值替换内部的 `maps`。这样当地图是动态生成时, + 作者依然可以自定义顺序,而不必手动维护全量列表; +- `createLayerState` 不再维护 `maps`,`maps` 完全由 `setMapList` 管理; +- 若 `createLayerState` 传入的 id 不在 `maps` 中,仍可正常创建, + 不影响存档逻辑,但该楼层不参与任何区域判断。 + +## 2. 区域定义与管理 + +### 类型定义 + +```ts +/** 单段闭区间 [start, end],start 和 end 均为 maps 下标 */ +export interface IMapAreaInterval { + readonly start: number; + readonly end: number; +} + +/** 一个区域由一个或多个独立区间组成 */ +export type MapArea = IMapAreaInterval[]; +``` + +### 接口 + +- `setArea(areas: Set): void`:一次性设置所有区域信息, + 覆盖原有区域定义;每个元素代表一个区域,区域可包含多个区间, + 使用 `Set` 表示无序区域集合; +- `activeArea(id: string): void`:手动激活指定楼层所在区域的所有楼层。 + 系统遍历 `areaList`,找到包含该楼层 id 的区域后,对该区域内的所有 + 楼层调用 `setMapActiveStatus(floor, true)`; +- `deactiveArea(id: string): void`:手动取消激活指定楼层所在区域的 + 所有楼层,逻辑与 `activeArea` 对称;判断时遍历 `areaList`, + 此操作为低频调用,无需缓存; + +## 3. 自动分区激活器 + +### 接口 + +- `useAutoActivitor(enable: boolean): void`:是否启用自动激活器。 + +### 触发接口 + +需要一个通知接口供玩家相关模块调用: + +- `notifyEnterFloor(id: string): void`:玩家进入指定楼层时调用此接口, + 通知地图管理器进行自动激活判断。 + +### 逻辑 + +`notifyEnterFloor(id)` 的执行流程(每次进入楼层均调用,内部短路): + +1. 若自动激活器未启用,直接返回; +2. 若 `isMapActive(id)` 为 `true`,直接返回(楼层已激活,无需操作); +3. 遍历 `areaList`,找出包含 `id` 的区域; +4. 若未找到,直接返回(该楼层不在任何区域内); +5. 若 `lastFloorId !== null`,调用 `deactiveArea(lastFloorId)` 失活上一个区域; +6. 调用 `activeArea(id)` 激活新区域,更新 `lastFloorId = id`。 + +### 内部状态 + +`MapStore` 新增: + +- `private areaList: Set`:所有区域定义; +- `private lastFloorId: string | null = null`:上一次触发 `notifyEnterFloor` + 的楼层 id,用于定位并失活上一个激活区域; +- `private autoActivitorEnabled: boolean = false`:自动激活器开关。 + +## 4. 楼层尺寸上移至 LayerState + +### 动机 + +当前 `MapLayer.width` / `MapLayer.height` 存储在图层中, +但同一楼层的所有图层尺寸必须一致,权威来源应当是 `LayerState`。 + +### 接口变动 + +**`ILayerState` 新增**: + +```ts +readonly width: number; +readonly height: number; +``` + +**`addLayer` 签名调整**: + +目前 `addLayer(width: number, height: number): IMapLayer`, +移除 width/height 参数,改为 `addLayer(): IMapLayer`, +使用 `LayerState` 内部存储的尺寸创建图层。 + +楼层尺寸在 `createLayerState` 创建时指定,`createLayerState` 签名改为: + +```ts +createLayerState(id: string, width: number, height: number): ILayerState; +``` + +运行时仍可通过 `resizeLayer` 修改楼层尺寸,该方法会同步对楼层内所有 +图层执行 resize,保持尺寸一致。 + +**`resizeLayer` 签名调整**: + +当前 `resizeLayer(layer, width, height, keepBlock?)` 只 resize 单个图层, +但既然尺寸是楼层级的,建议改为对该楼层的所有图层同步 resize: + +```ts +resizeLayer(width: number, height: number, keepBlock?: boolean): void; +``` + +**`IMapLayer.resize` / `IMapLayer.resize2`**: + +从 `IMapLayer` 接口中移除,保留为 `MapLayer` 的内部实现, +仅由 `LayerState.resizeLayer` 调用。 + +**`IMapLayer.width` / `IMapLayer.height`**: + +保留在 `IMapLayer` 接口中,供外部通过图层对象直接获取尺寸。 +其值始终与所属 `LayerState` 的 `width`/`height` 保持一致。 + +--- + +# 附加建议结论 + +1. **`IMapLayer.setMapRef` 可见性**:保留现有设计,偶尔有外部需求。 +2. **`active` 状态管理**:不需要单独维护区域激活状态; + `activeArea(id)` / `deactiveArea(id)` 是 `setMapActiveStatus` 的 + 快捷方式,遍历区域楼层批量调用即可,无需额外的区域状态字段。 +3. **`notifyEnterFloor` 返回值**:暂不添加,后续有需求再改进。 + +--- + +# 涉及文件 + +## 需要引用的文件 + +- `@user/data-base/src/map/types.ts`: 全部现有地图接口 +- `@user/data-base/src/map/mapStore.ts`: `MapStore` 实现类 +- `@user/data-base/src/map/layerState.ts`: `LayerState` 实现类 +- `@user/data-base/src/map/mapLayer.ts`: `MapLayer` 实现类 + +## 需要修改的文件 + +### `@user/data-base/src/map/types.ts` + +- [x] 新增 `IMapAreaInterval` 接口:区间定义,含 `start`、`end` +- [x] 新增 `MapArea` 类型别名:`IMapAreaInterval[]`,表示一个区域 +- [x] 修改 `ILayerState`: + - [x] 新增 `readonly width: number` 和 `readonly height: number` + - [x] 修改 `addLayer` 签名,移除 `width`/`height` 参数(使用 `LayerState` 自身尺寸) + - [x] 修改 `resizeLayer` 签名:移除 `layer` 参数,改为对整个楼层所有图层同步 resize +- [x] 修改 `IMapLayer`: + - [x] 移除 `resize` / `resize2`(改为 `MapLayer` 内部方法) +- [x] 修改 `IMapStore`: + - [x] 将 `readonly maps` 类型改为 `ReadonlyArray` + - [x] 修改 `createLayerState` 签名:新增 `width: number`、`height: number` 参数 + - [x] 新增 `setMapList(maps: string[]): void` + - [x] 新增 `useManualOrder(sort: (arr: string[]) => string[]): void` + - [x] 新增 `setArea(areas: Set): void` + - [x] 新增 `activeArea(id: string): void` + - [x] 新增 `deactiveArea(id: string): void` + - [x] 新增 `useAutoActivitor(enable: boolean): void` + - [x] 新增 `notifyEnterFloor(id: string): void` + +### `@user/data-base/src/map/mapStore.ts` + +- [x] 将 `maps: Set` 改为 `maps: string[]` +- [x] 修改 `createLayerState`:添加 `width`/`height` 参数,不再维护 `maps` +- [x] 实现 `setMapList` +- [x] 实现 `useManualOrder` +- [x] 新增 `private areaList: Set` +- [x] 新增 `private lastFloorId: string | null` +- [x] 新增 `private autoActivitorEnabled: boolean` +- [x] 实现 `setArea`、`activeArea`、`deactiveArea` +- [x] 实现 `useAutoActivitor` +- [x] 实现 `notifyEnterFloor` + +### `@user/data-base/src/map/layerState.ts` + +- [x] 新增 `width: number` 和 `height: number` 成员(由构造参数初始化) +- [x] 修改 `addLayer`,移除 `width`/`height` 参数,使用 `this.width`/`this.height` +- [x] 修改 `resizeLayer`,移除 `layer` 参数,改为对所有图层同步 resize + +### `@user/data-base/src/map/mapLayer.ts` + +- [x] 将 `resize`/`resize2` 改为内部方法(从公共接口移除) + +--- diff --git a/docs/dev/map-store-save.md b/docs/dev/map-store-save.md index fdc8c05..1af0da1 100644 --- a/docs/dev/map-store-save.md +++ b/docs/dev/map-store-save.md @@ -242,31 +242,31 @@ setActiveStatus(active: boolean): void; ### `@user/data-base/src/map/types.ts` -- [ ] 新增 `IMapLayerSave` 接口:单个 MapLayer 存档数据格式 -- [ ] 新增 `ILayerStateSave` 接口:单个楼层存档数据格式 -- [ ] 新增 `IMapStoreSave` 接口:MapStore 整体存档数据格式 -- [ ] 修改 `ILayerState`:新增 `readonly active: boolean` 和 +- [x] 新增 `IMapLayerSave` 接口:单个 MapLayer 存档数据格式 +- [x] 新增 `ILayerStateSave` 接口:单个楼层存档数据格式 +- [x] 新增 `IMapStoreSave` 接口:MapStore 整体存档数据格式 +- [x] 修改 `ILayerState`:新增 `readonly active: boolean` 和 `setActiveStatus(active: boolean): void` -- [ ] 修改 `IMapLayer`:新增 `setMapRef(array: Uint32Array): void` -- [ ] 新增 `IMapStore` 接口:继承 `ISaveableContent`, +- [x] 修改 `IMapLayer`:新增 `setMapRef(array: Uint32Array): void` +- [x] 新增 `IMapStore` 接口:继承 `ISaveableContent`, 含全部接口(见第 7 节) ### `@user/data-base/src/map/mapLayer.ts` ### `@user/data-base/src/map/layerState.ts` -- [ ] 新增 `active: boolean = false` 成员:楼层激活状态 -- [ ] 实现 `setActiveStatus(active: boolean): void` -- [ ] 新增 `private dirty: boolean = false` 成员:楼层级脏标记 -- [ ] 修改 `StateMapLayerHook.onUpdateArea`、`onUpdateBlock`、`onResize`: +- [x] 新增 `active: boolean = false` 成员:楼层激活状态 +- [x] 实现 `setActiveStatus(active: boolean): void` +- [x] 新增 `private dirty: boolean = false` 成员:楼层级脏标记 +- [x] 修改 `StateMapLayerHook.onUpdateArea`、`onUpdateBlock`、`onResize`: 在转发钩子的同时,将 `state.dirty` 置 `true` -- [ ] 新增 `isDirty(): boolean` 方法:返回 `this.dirty`,供 `MapStore` 读取 -- [ ] 新增 `setDirty(dirty: boolean): void` 方法: +- [x] 新增 `isDirty(): boolean` 方法:返回 `this.dirty`,供 `MapStore` 读取 +- [x] 新增 `setDirty(dirty: boolean): void` 方法: 供 `MapStore.compareWith` 时根据实际比较结果设置 ### `@user/data-base/src/map/mapLayer.ts` -- [ ] 新增 `setMapRef(array: Uint32Array): void` 方法: +- [x] 新增 `setMapRef(array: Uint32Array): void` 方法: 直接替换内部图块数组引用,跳过拷贝,供 `MapStore` 读档时使用。 需确保传入数组长度与 `width × height` 匹配, 并触发必要的钩子通知(不触发 `onResize`,应触发 `onUpdateArea` 通知全区域更新)。 @@ -274,21 +274,21 @@ setActiveStatus(active: boolean): void; ### `@user/data-base/src/map/mapStore.ts`(新文件) -- [ ] 实现 `MapStore` 类,实现 `IMapStore` -- [ ] `private mapData: Map`:楼层 id 到状态对象的映射 -- [ ] `readonly maps: ReadonlySet`:所有楼层 id 的只读集合视图 -- [ ] `private refData: Map> | null`:参考基准 -- [ ] 实现 `getLayerState`、`getActiveMap`、`createLayerState` -- [ ] 实现 `isMapActive`、`setMapActiveStatus`、`iterateActiveMaps`、`iterateInactiveMaps`、`iterateAllMaps` -- [ ] 实现 `compareWith` -- [ ] 实现 `saveNoCompression`、`saveLowCompression`、`saveHighCompression` -- [ ] 实现 `loadNoCompression`、`loadLowCompression`、`loadHighCompression` -- [ ] 实现 `saveState(compression)` 和 `loadState(state, compression)` 分发 +- [x] 实现 `MapStore` 类,实现 `IMapStore` +- [x] `private mapData: Map`:楼层 id 到状态对象的映射 +- [x] `readonly maps: ReadonlySet`:所有楼层 id 的只读集合视图 +- [x] `private refData: Map> | null`:参考基准 +- [x] 实现 `getLayerState`、`getActiveMap`、`createLayerState` +- [x] 实现 `isMapActive`、`setMapActiveStatus`、`iterateActiveMaps`、`iterateInactiveMaps`、`iterateAllMaps` +- [x] 实现 `compareWith` +- [x] 实现 `saveNoCompression`、`saveLowCompression`、`saveHighCompression` +- [x] 实现 `loadNoCompression`、`loadLowCompression`、`loadHighCompression` +- [x] 实现 `saveState(compression)` 和 `loadState(state, compression)` 分发 ### `@user/data-base/src/map/index.ts` -- [ ] 补充导出 `mapStore.ts` +- [x] 补充导出 `mapStore.ts` ### `@user/data-base/src/types.ts` -- [ ] 将 `IStateBase.layer` 类型由 `ILayerState` 改为 `IMapStore` +- [x] 将 `IStateBase.layer` 类型由 `ILayerState` 改为 `IMapStore` diff --git a/packages-user/data-base/src/map/layerState.ts b/packages-user/data-base/src/map/layerState.ts index 37f4359..9edb9ed 100644 --- a/packages-user/data-base/src/map/layerState.ts +++ b/packages-user/data-base/src/map/layerState.ts @@ -18,6 +18,8 @@ export class LayerState implements ILayerState { readonly layerList: Set = new Set(); + /** 具体 MapLayer 实例列表,供内部 resize 使用 */ + private readonly mapLayerList: Set = new Set(); /** 图层到图层别名映射 */ readonly layerAliasMap: WeakMap = new WeakMap(); /** 图层别名到图层的映射 */ @@ -35,10 +37,18 @@ export class LayerState /** 楼层级脏标记 */ private dirty: boolean = false; - addLayer(width: number, height: number): IMapLayer { - const array = new Uint32Array(width * height); - const layer = new MapLayer(array, width, height); + constructor( + public width: number, + public height: number + ) { + super(); + } + + addLayer(): IMapLayer { + const array = new Uint32Array(this.width * this.height); + const layer = new MapLayer(array, this.width, this.height); this.layerList.add(layer); + this.mapLayerList.add(layer); this.forEachHook(hook => { hook.onUpdateLayer?.(this.layerList); }); @@ -50,6 +60,7 @@ export class LayerState removeLayer(layer: IMapLayer): void { this.layerList.delete(layer); + this.mapLayerList.delete(layer as MapLayer); const alias = this.layerAliasMap.get(layer); if (alias) { const symbol = Symbol.for(alias); @@ -89,15 +100,18 @@ export class LayerState } resizeLayer( - layer: IMapLayer, width: number, height: number, keepBlock: boolean = false ): void { - if (keepBlock) { - layer.resize(width, height); - } else { - layer.resize2(width, height); + this.width = width; + this.height = height; + for (const layer of this.mapLayerList) { + if (keepBlock) { + layer.resize(width, height); + } else { + layer.resize2(width, height); + } } } diff --git a/packages-user/data-base/src/map/mapStore.ts b/packages-user/data-base/src/map/mapStore.ts index 19e1fe8..d72fc72 100644 --- a/packages-user/data-base/src/map/mapStore.ts +++ b/packages-user/data-base/src/map/mapStore.ts @@ -6,21 +6,65 @@ import { IMapLayer, IMapLayerSave, IMapStore, - IMapStoreSave + IMapStoreSave, + MapArea } from './types'; import { LayerState } from './layerState'; +import { uniq } from 'lodash-es'; export class MapStore implements IMapStore { /** 楼层 id 到状态对象的映射 */ private readonly mapData: Map = new Map(); - /** 所有楼层 id 的只读集合视图 */ - readonly maps: Set = new Set(); + /** 所有楼层 id 的有序数组 */ + readonly maps: string[] = []; /** 差分压缩参考基准,首次 compareWith 后设置,之后不再更新 */ private refData: Map> | null = null; - //#region 楼层访问 + /** 分区列表 */ + private areaList: Set = new Set(); + + /** 上一次调用 notifyEnterFloor 传入的楼层 id */ + private lastFloorId: string | null = null; + + /** 自动分区激活器开关 */ + private autoActivitorEnabled: boolean = false; + + //#region 楼层管理 + + createLayerState(id: string, width: number, height: number): ILayerState { + if (this.mapData.has(id)) { + logger.warn(121, id); + } else { + this.maps.push(id); + } + const state = new LayerState(width, height); + // 若 refData 已存在,新楼层直接视为全脏 + if (this.refData !== null) { + state.setDirty(true); + } + this.mapData.set(id, state); + return state; + } + + setMapList(maps: string[]): void { + this.maps.length = 0; + this.maps.push(...uniq(maps)); + } + + useManualOrder(sort: (arr: string[]) => string[]): void { + const copy = this.maps.slice(); + const sorted = sort(copy); + const oldSet = new Set(this.maps); + const newSet = new Set(sorted); + if (oldSet.size !== newSet.size || !newSet.isSubsetOf(oldSet)) { + logger.warn(125); + return; + } + this.maps.length = 0; + this.maps.push(...uniq(sorted)); + } getLayerState(id: string): ILayerState | null { return this.mapData.get(id) ?? null; @@ -34,20 +78,65 @@ export class MapStore implements IMapStore { //#endregion - //#region 楼层管理 + //#region 分区管理 - createLayerState(id: string): ILayerState { - if (this.mapData.has(id)) { - logger.warn(121, id); + setArea(areas: Set): void { + this.areaList = areas; + } + + activeArea(id: string): void { + const idx = this.maps.indexOf(id); + if (idx === -1) return; + const area = this.findAreaByIndex(idx); + if (!area) return; + this.setAreaActive(area, true); + } + + deactiveArea(id: string): void { + const idx = this.maps.indexOf(id); + if (idx === -1) return; + const area = this.findAreaByIndex(idx); + if (!area) return; + this.setAreaActive(area, false); + } + + useAutoActivitor(enable: boolean): void { + this.autoActivitorEnabled = enable; + } + + notifyEnterFloor(id: string): void { + if (!this.autoActivitorEnabled) return; + const idx = this.maps.indexOf(id); + if (idx === -1) return; + const area = this.findAreaByIndex(idx); + if (!area) return; + if (this.lastFloorId !== null) { + this.deactiveArea(this.lastFloorId); } - const state = new LayerState(); - // 若 refData 已存在,新楼层直接视为全脏 - if (this.refData !== null) { - state.setDirty(true); + this.activeArea(id); + this.lastFloorId = id; + } + + private findAreaByIndex(idx: number): MapArea | null { + for (const area of this.areaList) { + for (const interval of area) { + if (idx >= interval.start && idx <= interval.end) { + return area; + } + } + } + return null; + } + + private setAreaActive(area: MapArea, active: boolean): void { + for (const interval of area) { + for (let i = interval.start; i <= interval.end; i++) { + const floorId = this.maps[i]; + if (floorId !== undefined) { + this.setMapActiveStatus(floorId, active); + } + } } - this.mapData.set(id, state); - this.maps.add(id); - return state; } //#endregion diff --git a/packages-user/data-base/src/map/types.ts b/packages-user/data-base/src/map/types.ts index 7f959b3..47f0bfc 100644 --- a/packages-user/data-base/src/map/types.ts +++ b/packages-user/data-base/src/map/types.ts @@ -75,20 +75,6 @@ export interface IMapLayer extends IHookable< /** 图层纵深 */ readonly zIndex: number; - /** - * 调整地图尺寸,维持原有图块。如果尺寸变大,那么会补零,如果尺寸变小,那么会将当前数组裁剪 - * @param width 地图宽度 - * @param height 地图高度 - */ - resize(width: number, height: number): void; - - /** - * 调整地图尺寸,但是将地图全部重置为零,不保留原地图数据 - * @param width 地图宽度 - * @param height 地图高度 - */ - resize2(width: number, height: number): void; - /** * 设置某一点的图块 * @param block 图块数字 @@ -225,13 +211,15 @@ export interface ILayerState extends IHookable { readonly layerList: Set; /** 此楼层是否处于激活状态 */ readonly active: boolean; + /** 此楼层的地图宽度 */ + readonly width: number; + /** 此楼层的地图高度 */ + readonly height: number; /** - * 添加图层 - * @param width 地图宽度 - * @param height 地图高度 + * 添加图层,使用楼层预设的宽高 */ - addLayer(width: number, height: number): IMapLayer; + addLayer(): IMapLayer; /** * 移除指定图层 @@ -265,18 +253,12 @@ export interface ILayerState extends IHookable { getLayerAlias(layer: IMapLayer): string | undefined; /** - * 重新设置图层的大小 - * @param layer 图层对象 - * @param width 新的图层宽度 - * @param height 新的图层高度 + * 重新设置所有图层的大小,同时更新楼层预设宽高 + * @param width 新的地图宽度 + * @param height 新的地图高度 * @param keepBlock 是否保留原有图块,默认不保留 */ - resizeLayer( - layer: IMapLayer, - width: number, - height: number, - keepBlock?: boolean - ): void; + resizeLayer(width: number, height: number, keepBlock?: boolean): void; /** * 设置背景图块 @@ -336,9 +318,18 @@ export interface IMapStoreSave { readonly floors: ReadonlyMap; } +/** 单段闭区间 [start, end],start 和 end 均为 maps 下标 */ +export interface IMapAreaInterval { + readonly start: number; + readonly end: number; +} + +/** 一个区域由一个或多个独立区间组成 */ +export type MapArea = IMapAreaInterval[]; + export interface IMapStore extends ISaveableContent { - /** 所有楼层的 id 集合 */ - readonly maps: ReadonlySet; + /** 所有楼层的 id 有序数组 */ + readonly maps: ReadonlyArray; /** * 获取指定 id 的楼层状态,不存在则返回 null @@ -355,8 +346,10 @@ export interface IMapStore extends ISaveableContent { /** * 创建并注册一个空白楼层,若 id 已存在则警告并覆盖,返回楼层状态对象 * @param id 楼层 id + * @param width 地图宽度 + * @param height 地图高度 */ - createLayerState(id: string): ILayerState; + createLayerState(id: string, width: number, height: number): ILayerState; /** * 获取指定 id 的楼层是否激活,不存在的 id 返回 false @@ -391,4 +384,47 @@ export interface IMapStore extends ISaveableContent { * @param ref 外层 key = 楼层 id,内层 key = zIndex,value = 图层完整图块数据 */ compareWith(ref: Map>): void; + + /** + * 设定楼层有序列表。设定后有序列表将用于分区索引计算 + * @param maps 楼层 id 数组 + */ + setMapList(maps: string[]): void; + + /** + * 使用自定义排序函数重排 maps。排序函数接收当前列表的副本,返回新顺序。 + * 若返回的数组元素集合与原列表不一致,则警告并放弃本次排序 + * @param sort 排序函数 + */ + useManualOrder(sort: (arr: string[]) => string[]): void; + + /** + * 设定分区列表。每个分区由一个或多个区间组成 + * @param areas 分区集合 + */ + setArea(areas: Set): void; + + /** + * 激活指定楼层所属分区的所有楼层 + * @param id 楼层 id + */ + activeArea(id: string): void; + + /** + * 去激活指定楼层所属分区的所有楼层 + * @param id 楼层 id + */ + deactiveArea(id: string): void; + + /** + * 开启或关闭自动分区激活器 + * @param enable 是否开启 + */ + useAutoActivitor(enable: boolean): void; + + /** + * 通知当前进入的楼层。开启自动激活器时,将自动去激活上一个分区并激活新分区 + * @param id 楼层 id + */ + notifyEnterFloor(id: string): void; } diff --git a/packages-user/data-state/src/core.ts b/packages-user/data-state/src/core.ts index 4b3b325..87bb0c5 100644 --- a/packages-user/data-state/src/core.ts +++ b/packages-user/data-state/src/core.ts @@ -214,12 +214,16 @@ export class CoreState implements ICoreState { const reference = new Map>(); for (const id of floors) { const floor = data[id]; - const state = this.maps.createLayerState(id); - const bg = state.addLayer(floor.width, floor.height); - const bg2 = state.addLayer(floor.width, floor.height); - const event = state.addLayer(floor.width, floor.height); - const fg = state.addLayer(floor.width, floor.height); - const fg2 = state.addLayer(floor.width, floor.height); + const state = this.maps.createLayerState( + id, + floor.width, + floor.height + ); + const bg = state.addLayer(); + const bg2 = state.addLayer(); + const event = state.addLayer(); + const fg = state.addLayer(); + const fg2 = state.addLayer(); bg.setZIndex(BG_ZINDEX); bg2.setZIndex(BG2_ZINDEX); event.setZIndex(EVENT_ZINDEX); diff --git a/packages/common/src/logger.json b/packages/common/src/logger.json index e9b8dcd..20fe5f9 100644 --- a/packages/common/src/logger.json +++ b/packages/common/src/logger.json @@ -182,6 +182,7 @@ "122": "MapStore.loadState: floor '$1' not found in current map data, skipping.", "123": "MapLayer.setMapRef: array length $1 does not match expected size $2, setMapRef will be ignored.", "124": "MapStore.loadState: floor '$1' or its layer(s) not found in current reference data, skipping.", + "125": "Expected sorted floor id array has a same floor id set, but an array with a different floor id set is returned.", "1001": "Item-detail extension needs 'floor-binder' and 'floor-damage' extension as dependency." } }