diff --git a/packages-user/data-base/src/map/eventPath.test.ts b/packages-user/data-base/src/map/eventPath.test.ts index c32cc54..b21d1b9 100644 --- a/packages-user/data-base/src/map/eventPath.test.ts +++ b/packages-user/data-base/src/map/eventPath.test.ts @@ -1,4 +1,9 @@ import { beforeAll, describe, expect, it, vi } from 'vitest'; +import { + type IDataCommon, + type IMapRawData, + type ITileStore +} from '@user/data-common'; interface TestModules { MapState: typeof import('./mapState').MapState; @@ -22,7 +27,7 @@ beforeAll(async () => { }; }); -function createRaw() { +function createRaw(): IMapRawData { return { floorId: 'F1', width: 2, @@ -33,8 +38,8 @@ function createRaw() { } function createMapState() { - const tileStore = new modules.TileStore(); - const state = { + const tileStore: ITileStore = new modules.TileStore() as never; + const state: IDataCommon = { tileStore, itemStore: {}, mapStore: {}, @@ -42,7 +47,7 @@ function createMapState() { roleFace: new modules.RoleFaceBinder(), faceManager: {}, saveSystem: {} - }; + } as never; return new modules.MapState(tileStore, state); } @@ -64,7 +69,13 @@ describe('MapState raw event path', () => { }); describe('MapState malformed raw event structures', () => { - const cases = [ + interface MalformedCase { + name: string; + mutate(raw: IMapRawData): void; + code: number; + } + + const cases: MalformedCase[] = [ { name: 'missing raw.map container', mutate: raw => Reflect.set(raw, 'map', null), diff --git a/packages-user/data-base/src/map/mapLifecycle.test.ts b/packages-user/data-base/src/map/mapLifecycle.test.ts index cda449c..a6b03d2 100644 --- a/packages-user/data-base/src/map/mapLifecycle.test.ts +++ b/packages-user/data-base/src/map/mapLifecycle.test.ts @@ -1,4 +1,15 @@ import { beforeAll, describe, expect, it, vi } from 'vitest'; +import { type IResizableMapLayer } from './types'; +import { + type IDataCommon, + type ITileStore, + SaveCompression +} from '@user/data-common'; + +vi.hoisted(() => { + vi.stubGlobal('main', { replayChecking: true }); + vi.stubGlobal('location', { origin: 'http://localhost' }); +}); interface TestModules { MapState: typeof import('./mapState').MapState; @@ -6,7 +17,6 @@ interface TestModules { RoleFaceBinder: typeof import('@user/data-common').RoleFaceBinder; FaceManager: typeof import('@user/data-common').FaceManager; Dir8FaceHandler: typeof import('@user/data-common').Dir8FaceHandler; - SaveCompression: typeof import('@user/data-common').SaveCompression; } let modules: TestModules; @@ -21,8 +31,7 @@ beforeAll(async () => { TileStore: commonModule.TileStore, RoleFaceBinder: commonModule.RoleFaceBinder, FaceManager: commonModule.FaceManager, - Dir8FaceHandler: commonModule.Dir8FaceHandler, - SaveCompression: commonModule.SaveCompression + Dir8FaceHandler: commonModule.Dir8FaceHandler }; }); @@ -30,7 +39,7 @@ function createMapState( pointEvents: Record> = {}, blocks: number[] = [1, 1, 1, 1] ) { - const tileStore = new modules.TileStore(); + const tileStore: ITileStore = new modules.TileStore() as never; tileStore.addTile({ num: 1, id: 'base', @@ -49,7 +58,7 @@ function createMapState( }); const faceManager = new modules.FaceManager(); faceManager.register(1, new modules.Dir8FaceHandler()); - const state = { + const state: IDataCommon = { tileStore, itemStore: {}, mapStore: {}, @@ -57,7 +66,7 @@ function createMapState( roleFace: new modules.RoleFaceBinder(), faceManager, saveSystem: {} - }; + } as never; const mapState = new modules.MapState(tileStore, state); const map = mapState.fromRaw({ floorId: 'F1', @@ -66,7 +75,10 @@ function createMapState( layerAlias: { 0: 'event' }, events: { 0: pointEvents } }); - return { map: map!, layer: map!.getLayerByAlias('event')! }; + return { + map: map!, + layer: map!.getLayerByAlias('event')! as IResizableMapLayer + }; } describe('MapLayer tile defaults snapshots conversion and movement', () => { @@ -94,20 +106,22 @@ describe('MapLayer tile defaults snapshots conversion and movement', () => { ); expect(dynamicTile.tileEvent().dirty()).toBe(false); - const cleanSave = dynamicTile.saveState(); + const cleanSave = dynamicTile.saveState(SaveCompression.NoCompression); expect(cleanSave.events).toBeUndefined(); - dynamicTile.loadState(cleanSave); + dynamicTile.loadState(cleanSave, SaveCompression.NoCompression); expect(dynamicTile.tileEvent().get()).toEqual( new Map([[20, 'alternate-event']]) ); expect(dynamicTile.tileEvent().dirty()).toBe(false); dynamicTile.tileEvent().set(30, 'override-event'); - const overrideSave = dynamicTile.saveState(); + const overrideSave = dynamicTile.saveState( + SaveCompression.NoCompression + ); const savedEvents = new Map(overrideSave.events!); dynamicTile.tileEvent().set(30, 'changed-after-save'); expect(overrideSave.events).toEqual(savedEvents); - dynamicTile.loadState(overrideSave); + dynamicTile.loadState(overrideSave, SaveCompression.NoCompression); expect(dynamicTile.tileEvent().get()).toEqual( new Map([ [20, 'alternate-event'], @@ -169,9 +183,9 @@ describe('MapLayer point event lifecycle', () => { expect(point.dirty()).toBe(false); const compressionLevels = [ - modules.SaveCompression.NoCompression, - modules.SaveCompression.LowCompression, - modules.SaveCompression.HighCompression + SaveCompression.NoCompression, + SaveCompression.LowCompression, + SaveCompression.HighCompression ]; for (const compression of compressionLevels) { point.set(7, 'saved-point'); @@ -208,7 +222,7 @@ describe('MapLayer point event lifecycle', () => { height: 2, fullMap: new Uint32Array([1, 1, 1, 1]) }, - modules.SaveCompression.NoCompression + SaveCompression.NoCompression ); expect(point.get()).toEqual(new Map([[5, 'raw-point']])); expect(point.dirty()).toBe(false); @@ -224,8 +238,8 @@ describe('MapLayer point event lifecycle', () => { expect(layer.dirty()).toBe(true); const compressionLevels = [ - modules.SaveCompression.LowCompression, - modules.SaveCompression.HighCompression + SaveCompression.LowCompression, + SaveCompression.HighCompression ]; for (const compression of compressionLevels) { const save = map.saveState(compression); diff --git a/packages-user/data-system/src/event/eventDispatch.test.ts b/packages-user/data-system/src/event/eventDispatch.test.ts index 1d112f8..0467f27 100644 --- a/packages-user/data-system/src/event/eventDispatch.test.ts +++ b/packages-user/data-system/src/event/eventDispatch.test.ts @@ -1,17 +1,60 @@ import { beforeAll, describe, expect, it, vi } from 'vitest'; +import { + BlockEventType, + type IBlockEventEnv, + type IGameEventInvocation +} from '@user/data-base'; +import { + type IDataCommon, + type IGameEventStore, + type IReadonlyGameEvent, + type ITileStore, + EventTrigger +} from '@user/data-common'; +import { type IStateSystem } from '../types'; +import { EventExecuteMode, EventReduceMode } from './types'; +import { AnonTokyoInterpreter } from 'anon-tokyo'; + +vi.hoisted(() => { + vi.stubGlobal('main', { replayChecking: true }); + vi.stubGlobal('location', { origin: 'http://localhost' }); +}); + +const eventTriggers = { + OnEnter: EventTrigger.OnEnter, + OnLeave: EventTrigger.OnLeave, + OnTouch: EventTrigger.OnTouch +} as const; + +const blockEventTypes = { + PointEvent: BlockEventType.PointEvent, + TileEvent: BlockEventType.TileEvent +} as const; + +const eventExecuteModes = { + Normal: EventExecuteMode.Normal, + CutIfFalsy: EventExecuteMode.CutIfFalsy, + CutIfTruthy: EventExecuteMode.CutIfTruthy +} as const; + +const eventReduceModes = { + NoReduce: EventReduceMode.NoReduce, + OrReduce: EventReduceMode.OrReduce, + AndReduce: EventReduceMode.AndReduce +} as const; interface TestModules { EventExecutor: typeof import('./executor').EventExecutor; - DefaultHeroMoveTopImpl: typeof import('@user/data-state/hero/moverImpl').DefaultHeroMoveTopImpl; + DefaultHeroMoveTopImpl: typeof import('@user/data-state').DefaultHeroMoveTopImpl; MapState: typeof import('@user/data-base').MapState; TileStore: typeof import('@user/data-common').TileStore; RoleFaceBinder: typeof import('@user/data-common').RoleFaceBinder; FaceManager: typeof import('@user/data-common').FaceManager; Dir8FaceHandler: typeof import('@user/data-common').Dir8FaceHandler; - EventTrigger: typeof import('@user/data-common').EventTrigger; - BlockEventType: typeof import('@user/data-base').BlockEventType; - EventExecuteMode: typeof import('./types').EventExecuteMode; - EventReduceMode: typeof import('./types').EventReduceMode; + EventTrigger: typeof eventTriggers; + BlockEventType: typeof blockEventTypes; + EventExecuteMode: typeof eventExecuteModes; + EventReduceMode: typeof eventReduceModes; } interface EventCall { @@ -29,22 +72,21 @@ beforeAll(async () => { vi.stubGlobal('main', { replayChecking: true }); vi.stubGlobal('location', { origin: 'http://localhost' }); const executorModule = await import('./executor'); - const moverModule = await import('@user/data-state/hero/moverImpl'); + const stateModule = await import('../../../data-state/src/hero'); const baseModule = await import('@user/data-base'); const commonModule = await import('@user/data-common'); - const eventModule = await import('./types'); modules = { EventExecutor: executorModule.EventExecutor, - DefaultHeroMoveTopImpl: moverModule.DefaultHeroMoveTopImpl, + DefaultHeroMoveTopImpl: stateModule.DefaultHeroMoveTopImpl, MapState: baseModule.MapState, TileStore: commonModule.TileStore, RoleFaceBinder: commonModule.RoleFaceBinder, FaceManager: commonModule.FaceManager, Dir8FaceHandler: commonModule.Dir8FaceHandler, - EventTrigger: commonModule.EventTrigger, - BlockEventType: baseModule.BlockEventType, - EventExecuteMode: eventModule.EventExecuteMode, - EventReduceMode: eventModule.EventReduceMode + EventTrigger: eventTriggers, + BlockEventType: blockEventTypes, + EventExecuteMode: eventExecuteModes, + EventReduceMode: eventReduceModes }; }); @@ -53,7 +95,7 @@ function createFixture( staticEvent: string = 'static-enter', dynamicEvent: string = 'dynamic-enter' ) { - const tileStore = new modules.TileStore(); + const tileStore: ITileStore = new modules.TileStore() as never; tileStore.addTile({ num: 1, id: 'static', @@ -72,7 +114,7 @@ function createFixture( }); const faceManager = new modules.FaceManager(); faceManager.register(1, new modules.Dir8FaceHandler()); - const commonState = { + const commonState: IDataCommon = { tileStore, itemStore: {}, mapStore: {}, @@ -80,7 +122,7 @@ function createFixture( roleFace: new modules.RoleFaceBinder(), faceManager, saveSystem: {} - }; + } as never; const maps = new modules.MapState(tileStore, commonState); const map = maps.fromRaw({ floorId: 'F1', @@ -93,18 +135,35 @@ function createFixture( const dynamic = layer.createDynamic(2, 1, 0); dynamic.set(2); const events = new Map(); - const store = { - getEvent(id: string) { - return events.get(id) ?? null; + const store: IGameEventStore = { + addEvent() {}, + getEvent< + P extends Record, + E extends Record, + R = void + >(id: string): IReadonlyGameEvent | null { + return events.get(id) as never; } }; - const executor = new modules.EventExecutor({}, () => store); + const executor = new modules.EventExecutor( + {} as AnonTokyoInterpreter, + () => store + ); const state = { maps, eventSystem: { executor } }; - const mover = new modules.DefaultHeroMoveTopImpl(state); - return { events, executor, layer, map: map!, dynamic, mover, state }; + const fixtureState: IStateSystem & IDataCommon = state as never; + const mover = new modules.DefaultHeroMoveTopImpl(fixtureState); + return { + events, + executor, + layer, + map: map!, + dynamic, + mover, + state: fixtureState + }; } function addEvent( @@ -139,8 +198,18 @@ function addEvent( }); } -function invocation(id: string, trigger: number) { - return { id, env: { trigger } }; +function invocation(id: string, trigger: EventTrigger): IGameEventInvocation { + const env: IBlockEventEnv = { + state: {} as IDataCommon, + type: BlockEventType.CommonEvent, + trigger, + heroLocator: { x: 0, y: 0 }, + triggerLocator: null, + tile: null, + layer: null, + map: null + }; + return { id, env }; } describe('source-aware matching dispatch', () => { diff --git a/packages-user/data-system/src/event/executor.ts b/packages-user/data-system/src/event/executor.ts index 659f47b..49301bf 100644 --- a/packages-user/data-system/src/event/executor.ts +++ b/packages-user/data-system/src/event/executor.ts @@ -1,5 +1,9 @@ import { logger } from '@motajs/common'; -import { IBlockEventParam, IGameEventInvocation } from '@user/data-base'; +import { + IBlockEventEnv, + IBlockEventParam, + IGameEventInvocation +} from '@user/data-base'; import { IGameEventStore } from '@user/data-common'; import { AnonTokyoInterpreter } from 'anon-tokyo'; import { EventExecuteMode, EventReduceMode, IGameEventExecutor } from './types';