diff --git a/packages-user/data-state/src/core.ts b/packages-user/data-state/src/core.ts index 9f605fd..e8c3218 100644 --- a/packages-user/data-state/src/core.ts +++ b/packages-user/data-state/src/core.ts @@ -21,7 +21,8 @@ import { ItemStore, IMapStore, MapStore, - IReplaySystem + IReplaySystem, + IMapRawData } from '@user/data-common'; import { EnemyManager, @@ -70,8 +71,11 @@ import { } from './shared'; import { createLegacyDependencies, - ILegacyLoadData + ILegacyLoadData, + ILegacySerializedLoadData, + LOAD_SERIALIZED_DATA } from './legacy/dependencies'; +import { registerSerializedEvents } from './legacy/events'; import { ILoadProgressTotal, LoadProgressTotal } from '@motajs/loader'; import { isNil } from 'lodash-es'; import { DirectionMapper, IDirectionMapper, logger } from '@motajs/common'; @@ -163,7 +167,6 @@ export class CoreState implements ICoreState { const eventStore = new GameEventStore(); this.eventStore = eventStore; this.directionMapper = new DirectionMapper(); - // TODO: 后续在此初始化路径注册外部序列化事件定义与地图事件 id 绑定。 //#endregion @@ -261,7 +264,21 @@ export class CoreState implements ICoreState { this.initTileStore(data.tiles); this.initItemStore(data.items); this.initEnemyManager(data.enemies); - this.initMapState(data.floors, data.maps); + if (data.serialized) { + this[LOAD_SERIALIZED_DATA](data.serialized); + } else { + this.initMapState(data.floors, data.maps); + } + } + + /** 经内部加载边界注册序列化事件并绑定原始地图事件 id */ + [LOAD_SERIALIZED_DATA](data: ILegacySerializedLoadData): void { + registerSerializedEvents( + this.eventStore, + this.eventSystem.executor.interpreter, + data.events + ); + this.initRawMapState(data.maps); } /** @@ -421,6 +438,20 @@ export class CoreState implements ICoreState { this.maps.compareWith(reference); } + private initRawMapState(data: readonly IMapRawData[]): void { + const reference = new Map>(); + for (const raw of data) { + const state = this.maps.fromRaw(raw); + if (!state) continue; + const ref = new Map(); + for (const [zIndex, map] of Object.entries(raw.map)) { + ref.set(Number(zIndex), new Uint32Array(map)); + } + reference.set(raw.floorId, ref); + } + this.maps.compareWith(reference); + } + //#endregion //#region 存档方法 diff --git a/packages-user/data-state/src/legacy/dependencies.ts b/packages-user/data-state/src/legacy/dependencies.ts index fc6064b..1bfa4b5 100644 --- a/packages-user/data-state/src/legacy/dependencies.ts +++ b/packages-user/data-state/src/legacy/dependencies.ts @@ -4,6 +4,7 @@ import { IEnemyAttr, ISaveSystem, ITileLegacyConverter, + IMapRawData, MemorySaveSystem, SaveSystem } from '@user/data-common'; @@ -12,6 +13,16 @@ import { IStateSystem } from '@user/data-system'; import { EnemyLegacyBridge } from '../enemy/legacy'; import { ItemLegacyBridge, LegacyItemData } from './item'; import { LegacyTileData, TileLegacyBridge } from './tile'; +import { ISerializedEventDefinitions } from './events'; + +/** CoreState 内部显式数据加载所需的序列化事件与原始地图 */ +export interface ILegacySerializedLoadData { + readonly events: ISerializedEventDefinitions; + readonly maps: readonly IMapRawData[]; +} + +/** 仅供 CoreState 与 legacy 数据源之间使用的加载方法标识 */ +export const LOAD_SERIALIZED_DATA = Symbol('loadSerializedData'); export interface ILegacyLoadData { readonly tiles: typeof core.maps.blocksInfo; @@ -19,6 +30,7 @@ export interface ILegacyLoadData { readonly enemies: Record; readonly floors: FloorIds[]; readonly maps: Record; + readonly serialized?: ILegacySerializedLoadData; } export interface ILegacyDependencies { diff --git a/packages-user/data-state/src/legacy/events.ts b/packages-user/data-state/src/legacy/events.ts new file mode 100644 index 0000000..0b9b4fc --- /dev/null +++ b/packages-user/data-state/src/legacy/events.ts @@ -0,0 +1,64 @@ +import { EventTrigger, GameEvent, IGameEventStore } from '@user/data-common'; +import { AnonTokyoInterpreter, Statement } from 'anon-tokyo'; + +/** 旧样板内部加载边界使用的序列化事件定义 */ +export interface ISerializedEventData { + readonly trigger: EventTrigger; + readonly rawEvent: Statement[]; +} + +/** 事件 id 到序列化事件定义的映射 */ +export interface ISerializedEventDefinitions { + readonly [id: string]: ISerializedEventData; +} + +interface IRuntimeStatement { + readonly type?: unknown; +} + +function isStatement(value: unknown): value is Statement { + if (!value || typeof value !== 'object') return false; + const statement = value as IRuntimeStatement; + return ( + typeof statement.type === 'number' && + Number.isInteger(statement.type) && + statement.type >= 0 && + statement.type <= 10 + ); +} + +function isEventTrigger(value: unknown): value is EventTrigger { + return ( + typeof value === 'number' && + Number.isInteger(value) && + value >= EventTrigger.None && + value <= EventTrigger.OnAfterChangeFloor + ); +} + +function isSerializedEventData(value: unknown): value is ISerializedEventData { + if (!value || typeof value !== 'object') return false; + const data = value as ISerializedEventData; + return isEventTrigger(data.trigger) && Array.isArray(data.rawEvent) + ? data.rawEvent.every(isStatement) + : false; +} + +/** 将内部序列化事件定义转换为带有统一解释器的游戏事件实例 */ +export function registerSerializedEvents( + store: IGameEventStore, + interpreter: AnonTokyoInterpreter, + definitions: ISerializedEventDefinitions +): void { + if (!definitions || typeof definitions !== 'object') return; + for (const [id, data] of Object.entries(definitions)) { + if (!id || !isSerializedEventData(data)) continue; + const event = new GameEvent< + Record, + Record, + any + >(interpreter, data.rawEvent); + event.setTrigger(data.trigger); + store.addEvent(id, event); + } +}