@rpgjs/client 5.0.0-beta.26 → 5.0.0-beta.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/Game/Object.js.map +1 -1
  3. package/dist/Gui/Gui.d.ts +2 -2
  4. package/dist/Gui/Gui.js.map +1 -1
  5. package/dist/core/inject.d.ts +4 -4
  6. package/dist/core/inject.js.map +1 -1
  7. package/dist/core/setup.d.ts +3 -3
  8. package/dist/core/setup.js +1 -1
  9. package/dist/core/setup.js.map +1 -1
  10. package/dist/core/setup.spec.d.ts +1 -0
  11. package/dist/i18n.d.ts +1 -1
  12. package/dist/index.d.ts +1 -1
  13. package/dist/index.js +1 -2
  14. package/dist/module.d.ts +2 -2
  15. package/dist/module.js.map +1 -1
  16. package/dist/public-api-types.spec.d.ts +1 -0
  17. package/dist/services/AbstractSocket.d.ts +3 -3
  18. package/dist/services/AbstractSocket.js.map +1 -1
  19. package/dist/services/loadMap.d.ts +3 -7
  20. package/dist/services/loadMap.js +2 -2
  21. package/dist/services/loadMap.js.map +1 -1
  22. package/dist/services/mapStreaming.d.ts +2 -16
  23. package/dist/services/mapStreaming.js.map +1 -1
  24. package/dist/services/mmorpg.d.ts +4 -26
  25. package/dist/services/mmorpg.js.map +1 -1
  26. package/dist/services/standalone.d.ts +2 -98
  27. package/dist/services/standalone.js.map +1 -1
  28. package/package.json +3 -3
  29. package/src/Game/Object.spec.ts +1 -1
  30. package/src/Game/Object.ts +4 -4
  31. package/src/Gui/Gui.ts +3 -3
  32. package/src/core/inject.ts +6 -5
  33. package/src/core/setup.spec.ts +35 -0
  34. package/src/core/setup.ts +8 -6
  35. package/src/index.ts +12 -1
  36. package/src/module.ts +2 -2
  37. package/src/public-api-types.spec.ts +19 -0
  38. package/src/services/AbstractSocket.ts +2 -2
  39. package/src/services/loadMap.ts +8 -8
  40. package/src/services/mapStreaming.ts +7 -5
  41. package/src/services/mmorpg.ts +6 -7
  42. package/src/services/standalone.ts +4 -5
package/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  # @rpgjs/client
2
2
 
3
+ ## 5.0.0-beta.27
4
+
5
+ ### Patch Changes
6
+
7
+ - dc6aed5: Initialize recursively nested RPGJS provider lists in client and server setup so
8
+ sample and consumer configurations register every provider at runtime.
9
+ - e5ad24a: Establish the stable RPGJS-owned boundary for reactive gameplay properties,
10
+ dependency-injection providers, Node room storage, and Cloudflare room hosting.
11
+ Remove accidental Signe re-exports from the client and server roots, keep
12
+ direct Signe imports as an explicitly advanced plugin path, and protect every
13
+ published TypeScript entry with declaration reachability snapshots in CI.
14
+ Keep provider creation strategies mutually exclusive, support asynchronous
15
+ provider factories, and preserve strict member checking on the server engine.
16
+ Enforce these public contracts in CI, test the RPGJS-owned Node storage
17
+ lifecycle, and document complete stable migration examples.
18
+ - Updated dependencies [dc6aed5]
19
+ - Updated dependencies [e5ad24a]
20
+ - @rpgjs/server@5.0.0-beta.27
21
+ - @rpgjs/common@5.0.0-beta.26
22
+
3
23
  ## 5.0.0-beta.26
4
24
 
5
25
  ### Minor Changes
@@ -1 +1 @@
1
- {"version":3,"file":"Object.js","names":[],"sources":["../../src/Game/Object.ts"],"sourcesContent":["import { Hooks, ModulesToken, RpgCommonPlayer } from \"@rpgjs/common\";\nimport { trigger, signal, type Trigger } from \"canvasengine\";\nimport { combineLatest, from, map, of, startWith, Subscription, switchMap } from \"rxjs\";\nimport { inject } from \"../core/inject\";\nimport { RpgClientEngine } from \"../RpgClientEngine\";\ntype Frame = { x: number; y: number; ts: number };\n\ntype AnimationRestoreOptions = {\n restoreAnimationName?: string;\n restoreGraphics?: any[];\n timeoutMs?: number;\n};\n\ntype FlashType = 'alpha' | 'tint' | 'both';\n\ntype FlashOptions = {\n type?: FlashType;\n duration?: number;\n cycles?: number;\n alpha?: number;\n tint?: number | string;\n};\n\ntype FlashTriggerOptions = Omit<FlashOptions, \"tint\"> & {\n tint: number;\n};\n\ntype ConfigurableTrigger<T> = Omit<Trigger<T>, \"start\"> & {\n start(config?: T): Promise<void>;\n};\n\nconst toFiniteScale = (value: unknown, fallback: number): number => {\n if (typeof value === \"number\" && Number.isFinite(value)) return value;\n if (typeof value === \"string\" && value.trim()) {\n const parsed = Number(value);\n return Number.isFinite(parsed) ? parsed : fallback;\n }\n return fallback;\n};\n\nconst normalizeGraphicScale = (value: unknown, fallback: [number, number] = [1, 1]): [number, number] => {\n if (typeof value === \"number\" || typeof value === \"string\") {\n const scale = toFiniteScale(value, fallback[0]);\n return [scale, scale];\n }\n if (Array.isArray(value)) {\n const x = toFiniteScale(value[0], fallback[0]);\n const y = toFiniteScale(value[1] ?? value[0], x);\n return [x, y];\n }\n if (value && typeof value === \"object\") {\n const scale = value as { x?: unknown; y?: unknown };\n const x = toFiniteScale(scale.x, fallback[0]);\n const y = toFiniteScale(scale.y ?? scale.x, x);\n return [x, y];\n }\n return fallback;\n};\n\nexport const multiplyGraphicDisplayScale = (\n baseScale: unknown,\n instanceScale: unknown,\n): [number, number] => {\n const base = normalizeGraphicScale(baseScale);\n const instance = normalizeGraphicScale(instanceScale);\n return [base[0] * instance[0], base[1] * instance[1]];\n};\n\nexport const withGraphicDisplayScale = (spritesheet: any, scale: unknown): any => {\n if (!spritesheet || typeof spritesheet !== \"object\") return spritesheet;\n if (scale === undefined || scale === null) return spritesheet;\n return {\n ...spritesheet,\n displayScale: multiplyGraphicDisplayScale(spritesheet.displayScale, scale),\n };\n};\n\nexport const appendFramePayload = (current: unknown, items: unknown): Frame[] => {\n const frameItems = Array.isArray(items) ? items : items ? [items] : [];\n const nextFrames = frameItems.flatMap((item): Frame[] =>\n Array.isArray(item) ? item : [item as Frame]\n );\n const currentFrames = Array.isArray(current) ? current as Frame[] : [];\n return currentFrames.concat(nextFrames);\n};\n\nexport const getFrameTimestamp = (frame: Frame): number => {\n const timestamp = Number(frame.ts);\n return Number.isFinite(timestamp) ? timestamp : 0;\n};\n\nexport const mergeFreshFramePayload = (\n current: unknown,\n items: unknown,\n lastAppliedFrameTs = 0,\n): Frame[] => {\n return appendFramePayload(current, items)\n .filter((frame) => {\n const timestamp = getFrameTimestamp(frame);\n return timestamp === 0 || timestamp > lastAppliedFrameTs;\n })\n .sort((a, b) => getFrameTimestamp(a) - getFrameTimestamp(b));\n};\n\nexport abstract class RpgClientObject extends RpgCommonPlayer {\n abstract _type: string;\n emitParticleTrigger = trigger();\n particleName = signal(\"\");\n animationCurrentIndex = signal(0);\n animationIsPlaying = signal(false);\n _param = signal({});\n frames: Frame[] = [];\n graphicsSignals = signal<any[]>([]);\n flashTrigger: ConfigurableTrigger<FlashTriggerOptions> = trigger<FlashTriggerOptions>();\n private lastAppliedFrameTs = 0;\n private animationRestoreState?: {\n animationName: string;\n graphics: any[];\n };\n\n constructor() {\n super();\n const engine = this.engine;\n this.hooks.callHooks(\"client-sprite-onInit\", this).subscribe();\n\n this._frames.observable.subscribe(({ items }) => {\n if (!this.id) return;\n //if (this.id == this.engine.playerIdSignal()!) return;\n this.frames = mergeFreshFramePayload(\n this.frames,\n items,\n this.lastAppliedFrameTs,\n );\n });\n\n const graphics$ = this.graphics.observable.pipe(map(({ items }) => items));\n const graphicScale$ = this._graphicScale.observable.pipe(\n startWith({ value: this._graphicScale() }),\n map((payload: any) => payload?.value ?? payload),\n );\n\n combineLatest([graphics$, graphicScale$])\n .pipe(\n switchMap(([graphics, scale]) => {\n const graphicRefs = Array.isArray(graphics) ? graphics : [];\n if (graphicRefs.length === 0) return of([]);\n return from(Promise.all(graphicRefs.map(async (graphic) => {\n const spritesheet = await engine.getSpriteSheet(graphic);\n return withGraphicDisplayScale(spritesheet, scale);\n })));\n })\n )\n .subscribe((sheets) => { \n this.graphicsSignals.set(sheets);\n });\n\n engine.tick\n .pipe\n //throttleTime(10)\n ()\n .subscribe(() => {\n const frame = this.frames.shift();\n if (frame) {\n if (typeof frame.x !== \"number\" || typeof frame.y !== \"number\") return;\n const frameTs = this.getFrameTimestamp(frame);\n if (frameTs > 0 && frameTs <= this.lastAppliedFrameTs) return;\n engine.scene.setBodyPosition(\n this.id,\n frame.x,\n frame.y,\n \"top-left\"\n );\n if (frameTs > this.lastAppliedFrameTs) {\n this.lastAppliedFrameTs = frameTs;\n }\n }\n });\n }\n\n private getFrameTimestamp(frame: Frame): number {\n return getFrameTimestamp(frame);\n }\n\n /**\n * Access the shared client hook registry.\n *\n * @returns The hook service used to register and trigger client-side hooks.\n */\n get hooks() {\n return inject<Hooks>(ModulesToken);\n }\n\n /**\n * Access the current client engine instance.\n *\n * @returns The active {@link RpgClientEngine} instance.\n */\n get engine() {\n return inject(RpgClientEngine);\n }\n\n setHitbox(width: number, height: number): void {\n if (typeof width !== \"number\" || width <= 0) {\n throw new Error(\"setHitbox: width must be a positive number\");\n }\n if (typeof height !== \"number\" || height <= 0) {\n throw new Error(\"setHitbox: height must be a positive number\");\n }\n\n this.hitbox.set({\n w: width,\n h: height,\n });\n\n const sceneMap = (this.engine as any)?.sceneMap;\n if (sceneMap && this.id) {\n sceneMap.updateHitbox?.(this.id, this.x(), this.y(), width, height);\n }\n }\n\n private animationSubscription?: Subscription;\n private animationResetTimeout?: ReturnType<typeof setTimeout>;\n private animationWaitResolve?: () => void;\n\n private clearAnimationControls() {\n if (this.animationSubscription) {\n this.animationSubscription.unsubscribe();\n this.animationSubscription = undefined;\n }\n if (this.animationResetTimeout) {\n clearTimeout(this.animationResetTimeout);\n this.animationResetTimeout = undefined;\n }\n }\n\n private resolveAnimationWait() {\n const resolve = this.animationWaitResolve;\n this.animationWaitResolve = undefined;\n resolve?.();\n }\n\n private finishTemporaryAnimation() {\n const restoreState = this.animationRestoreState;\n this.clearAnimationControls();\n this.animationCurrentIndex.set(0);\n this.animationRestoreState = undefined;\n this.animationIsPlaying.set(false);\n if (restoreState) {\n this.animationName.set(restoreState.animationName);\n this.graphics.set([...restoreState.graphics]);\n }\n this.resolveAnimationWait();\n }\n\n /**\n * Trigger a flash animation on this sprite\n * \n * This method triggers a flash effect using CanvasEngine's flash directive.\n * The flash can be configured with various options including type (alpha, tint, or both),\n * duration, cycles, and color.\n * \n * ## Design\n * \n * The flash uses a trigger system that is connected to the flash directive in the\n * character component. This allows for flexible configuration and can be triggered\n * from both server events and client-side code.\n * \n * @param options - Flash configuration options\n * @param options.type - Type of flash effect: 'alpha' (opacity), 'tint' (color), or 'both' (default: 'alpha')\n * @param options.duration - Duration of the flash animation in milliseconds (default: 300)\n * @param options.cycles - Number of flash cycles (flash on/off) (default: 1)\n * @param options.alpha - Alpha value when flashing, from 0 to 1 (default: 0.3)\n * @param options.tint - Tint color when flashing as hex value or color name (default: 0xffffff - white)\n * \n * @example\n * ```ts\n * // Simple flash with default settings (alpha flash)\n * player.flash();\n * \n * // Flash with red tint\n * player.flash({ type: 'tint', tint: 0xff0000 });\n * \n * // Flash with both alpha and tint\n * player.flash({ \n * type: 'both', \n * alpha: 0.5, \n * tint: 0xff0000,\n * duration: 200,\n * cycles: 2\n * });\n * \n * // Quick damage flash\n * player.flash({ \n * type: 'tint', \n * tint: 0xff0000, \n * duration: 150,\n * cycles: 1\n * });\n * ```\n */\n flash(options?: FlashOptions): void {\n const flashOptions = {\n type: options?.type || 'alpha',\n duration: options?.duration ?? 300,\n cycles: options?.cycles ?? 1,\n alpha: options?.alpha ?? 0.3,\n tint: options?.tint ?? 0xffffff,\n };\n \n // Convert color name to hex if needed\n let tintValue = flashOptions.tint;\n if (typeof tintValue === 'string') {\n // Common color name to hex mapping\n const colorMap: Record<string, number> = {\n 'white': 0xffffff,\n 'red': 0xff0000,\n 'green': 0x00ff00,\n 'blue': 0x0000ff,\n 'yellow': 0xffff00,\n 'cyan': 0x00ffff,\n 'magenta': 0xff00ff,\n 'black': 0x000000,\n };\n tintValue = colorMap[tintValue.toLowerCase()] ?? 0xffffff;\n }\n \n this.flashTrigger.start({\n ...flashOptions,\n tint: tintValue,\n });\n }\n\n /**\n * Reset animation state when animation changes externally\n *\n * This method should be called when the animation changes due to movement\n * or other external factors to ensure the animation system doesn't get stuck\n *\n * @example\n * ```ts\n * // Reset when player starts moving\n * player.resetAnimationState();\n * ```\n */\n resetAnimationState() {\n if (this.animationRestoreState) {\n this.finishTemporaryAnimation();\n return;\n }\n this.animationIsPlaying.set(false);\n this.animationCurrentIndex.set(0);\n this.clearAnimationControls();\n this.resolveAnimationWait();\n }\n\n /**\n * Set a custom animation for a specific number of times\n *\n * Plays a custom animation for the specified number of repetitions.\n * The animation system prevents overlapping animations and automatically\n * returns to the previous animation when complete.\n *\n * @param animationName - Name of the animation to play\n * @param nbTimes - Number of times to repeat the animation (default: Infinity for continuous)\n * @param options - Restore and timeout options\n * @returns A promise resolved when a finite animation finishes, is interrupted, or times out\n *\n * @example\n * ```ts\n * // Play attack animation 3 times\n * await player.setAnimation('attack', 3);\n *\n * // Play continuous spell animation\n * player.setAnimation('spell');\n * ```\n */\n setAnimation(animationName: string, nbTimes?: number, options?: AnimationRestoreOptions): Promise<void>;\n /**\n * Set a custom animation with temporary graphic change\n *\n * Plays a custom animation for the specified number of repetitions and temporarily\n * changes the player's graphic (sprite sheet) during the animation. The graphic\n * is automatically reset when the animation finishes.\n *\n * @param animationName - Name of the animation to play\n * @param graphic - The graphic(s) to temporarily use during the animation\n * @param nbTimes - Number of times to repeat the animation (default: Infinity for continuous)\n * @param options - Restore and timeout options\n * @returns A promise resolved when a finite animation finishes, is interrupted, or times out\n *\n * @example\n * ```ts\n * // Play attack animation with temporary graphic change\n * await player.setAnimation('attack', 'hero_attack', 3);\n * ```\n */\n setAnimation(animationName: string, graphic?: string | string[], nbTimes?: number, options?: AnimationRestoreOptions): Promise<void>;\n setAnimation(\n animationName: string,\n graphicOrNbTimes?: string | string[] | number,\n nbTimesOrOptions?: number | AnimationRestoreOptions,\n options?: AnimationRestoreOptions\n ): Promise<void> {\n let graphic: string | string[] | undefined;\n let finalNbTimes: number = Infinity;\n let restoreOptions: AnimationRestoreOptions | undefined = options;\n\n // Handle overloads\n if (typeof graphicOrNbTimes === 'number') {\n // setAnimation(animationName, nbTimes)\n finalNbTimes = graphicOrNbTimes;\n restoreOptions = typeof nbTimesOrOptions === 'object' ? nbTimesOrOptions : options;\n } else if (graphicOrNbTimes !== undefined) {\n // setAnimation(animationName, graphic, nbTimes)\n graphic = graphicOrNbTimes;\n if (typeof nbTimesOrOptions === 'number') {\n finalNbTimes = nbTimesOrOptions;\n } else {\n finalNbTimes = Infinity;\n restoreOptions = nbTimesOrOptions ?? options;\n }\n } else {\n // setAnimation(animationName) - nbTimes remains Infinity\n finalNbTimes = Infinity;\n }\n\n if (this.animationIsPlaying()) {\n this.finishTemporaryAnimation();\n }\n\n const waitPromise =\n finalNbTimes === Infinity\n ? Promise.resolve()\n : new Promise<void>((resolve) => {\n this.animationWaitResolve = resolve;\n });\n\n this.animationIsPlaying.set(true);\n const previousAnimationName =\n restoreOptions?.restoreAnimationName ?? this.animationName();\n const previousGraphics = restoreOptions?.restoreGraphics\n ? [...restoreOptions.restoreGraphics]\n : [...this.graphics()];\n this.animationRestoreState = {\n animationName: previousAnimationName,\n graphics: previousGraphics,\n };\n this.animationCurrentIndex.set(0);\n\n // Temporarily change graphic if provided\n if (graphic !== undefined) {\n if (Array.isArray(graphic)) {\n this.graphics.set(graphic);\n } else {\n this.graphics.set([graphic]);\n }\n }\n\n this.clearAnimationControls();\n\n this.animationSubscription =\n this.animationCurrentIndex.observable.subscribe((index) => {\n if (index >= finalNbTimes) {\n this.finishTemporaryAnimation();\n }\n });\n\n if (finalNbTimes !== Infinity) {\n this.animationResetTimeout = setTimeout(() => {\n if (this.animationIsPlaying()) {\n this.finishTemporaryAnimation();\n }\n }, restoreOptions?.timeoutMs ?? Math.max(1000, finalNbTimes * 1000));\n }\n\n this.animationName.set(animationName);\n\n return waitPromise;\n }\n\n /**\n * Display a registered component animation effect on this object.\n *\n * @param id - Identifier of the component animation to play.\n * @param params - Parameters forwarded to the animation effect.\n * @returns A promise resolved when the animation component calls `onFinish`.\n */\n showComponentAnimation(id: string, params: any): Promise<void> {\n const engine = inject(RpgClientEngine);\n return engine.getComponentAnimation(id).displayEffect(params, this);\n }\n\n /**\n * Display a registered spritesheet animation effect on this object.\n *\n * @param graphic - Identifier of the spritesheet to use.\n * @param animationName - Name of the animation inside the spritesheet.\n * @returns A promise resolved when the animation component calls `onFinish`.\n */\n showAnimation(graphic: string, animationName: string = 'default'): Promise<void> {\n return this.showComponentAnimation('animation', {\n graphic,\n animationName,\n });\n }\n \n /**\n * Check whether this client object represents an event.\n *\n * @returns `true` if the object type is `event`, otherwise `false`.\n */\n isEvent(): boolean {\n return this._type === 'event';\n }\n\n /**\n * Check whether this client object represents a player.\n *\n * @returns `true` if the object type is `player`, otherwise `false`.\n */\n isPlayer(): boolean {\n return this._type === 'player';\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"Object.js","names":[],"sources":["../../src/Game/Object.ts"],"sourcesContent":["import { Hooks, ModulesToken, RpgCommonPlayer } from \"@rpgjs/common\";\nimport { trigger, signal, type Trigger } from \"canvasengine\";\nimport { combineLatest, from, map, of, startWith, Subscription, switchMap, type Observable } from \"rxjs\";\nimport { inject } from \"../core/inject\";\nimport { RpgClientEngine } from \"../RpgClientEngine\";\ntype Frame = { x: number; y: number; ts: number };\n\ntype AnimationRestoreOptions = {\n restoreAnimationName?: string;\n restoreGraphics?: any[];\n timeoutMs?: number;\n};\n\ntype FlashType = 'alpha' | 'tint' | 'both';\n\ntype FlashOptions = {\n type?: FlashType;\n duration?: number;\n cycles?: number;\n alpha?: number;\n tint?: number | string;\n};\n\ntype FlashTriggerOptions = Omit<FlashOptions, \"tint\"> & {\n tint: number;\n};\n\ntype ConfigurableTrigger<T> = Omit<Trigger<T>, \"start\"> & {\n start(config?: T): Promise<void>;\n};\n\nconst toFiniteScale = (value: unknown, fallback: number): number => {\n if (typeof value === \"number\" && Number.isFinite(value)) return value;\n if (typeof value === \"string\" && value.trim()) {\n const parsed = Number(value);\n return Number.isFinite(parsed) ? parsed : fallback;\n }\n return fallback;\n};\n\nconst normalizeGraphicScale = (value: unknown, fallback: [number, number] = [1, 1]): [number, number] => {\n if (typeof value === \"number\" || typeof value === \"string\") {\n const scale = toFiniteScale(value, fallback[0]);\n return [scale, scale];\n }\n if (Array.isArray(value)) {\n const x = toFiniteScale(value[0], fallback[0]);\n const y = toFiniteScale(value[1] ?? value[0], x);\n return [x, y];\n }\n if (value && typeof value === \"object\") {\n const scale = value as { x?: unknown; y?: unknown };\n const x = toFiniteScale(scale.x, fallback[0]);\n const y = toFiniteScale(scale.y ?? scale.x, x);\n return [x, y];\n }\n return fallback;\n};\n\nexport const multiplyGraphicDisplayScale = (\n baseScale: unknown,\n instanceScale: unknown,\n): [number, number] => {\n const base = normalizeGraphicScale(baseScale);\n const instance = normalizeGraphicScale(instanceScale);\n return [base[0] * instance[0], base[1] * instance[1]];\n};\n\nexport const withGraphicDisplayScale = (spritesheet: any, scale: unknown): any => {\n if (!spritesheet || typeof spritesheet !== \"object\") return spritesheet;\n if (scale === undefined || scale === null) return spritesheet;\n return {\n ...spritesheet,\n displayScale: multiplyGraphicDisplayScale(spritesheet.displayScale, scale),\n };\n};\n\nexport const appendFramePayload = (current: unknown, items: unknown): Frame[] => {\n const frameItems = Array.isArray(items) ? items : items ? [items] : [];\n const nextFrames = frameItems.flatMap((item): Frame[] =>\n Array.isArray(item) ? item : [item as Frame]\n );\n const currentFrames = Array.isArray(current) ? current as Frame[] : [];\n return currentFrames.concat(nextFrames);\n};\n\nexport const getFrameTimestamp = (frame: Frame): number => {\n const timestamp = Number(frame.ts);\n return Number.isFinite(timestamp) ? timestamp : 0;\n};\n\nexport const mergeFreshFramePayload = (\n current: unknown,\n items: unknown,\n lastAppliedFrameTs = 0,\n): Frame[] => {\n return appendFramePayload(current, items)\n .filter((frame) => {\n const timestamp = getFrameTimestamp(frame);\n return timestamp === 0 || timestamp > lastAppliedFrameTs;\n })\n .sort((a, b) => getFrameTimestamp(a) - getFrameTimestamp(b));\n};\n\nexport abstract class RpgClientObject extends RpgCommonPlayer {\n abstract _type: string;\n emitParticleTrigger = trigger();\n particleName = signal(\"\");\n animationCurrentIndex = signal(0);\n animationIsPlaying = signal(false);\n _param = signal({});\n frames: Frame[] = [];\n graphicsSignals = signal<any[]>([]);\n flashTrigger: ConfigurableTrigger<FlashTriggerOptions> = trigger<FlashTriggerOptions>();\n private lastAppliedFrameTs = 0;\n private animationRestoreState?: {\n animationName: string;\n graphics: any[];\n };\n\n constructor() {\n super();\n const engine = this.engine;\n this.hooks.callHooks(\"client-sprite-onInit\", this).subscribe();\n\n (this._frames as any).observable.subscribe(({ items }) => {\n if (!this.id) return;\n //if (this.id == this.engine.playerIdSignal()!) return;\n this.frames = mergeFreshFramePayload(\n this.frames,\n items,\n this.lastAppliedFrameTs,\n );\n });\n\n const graphics$: Observable<unknown> = (this.graphics as any).observable.pipe(map(({ items }) => items));\n const graphicScale$: Observable<unknown> = (this._graphicScale as any).observable.pipe(\n startWith({ value: this._graphicScale() }),\n map((payload: any) => payload?.value ?? payload),\n );\n\n combineLatest([graphics$, graphicScale$])\n .pipe(\n switchMap(([graphics, scale]) => {\n const graphicRefs = Array.isArray(graphics) ? graphics : [];\n if (graphicRefs.length === 0) return of([]);\n return from(Promise.all(graphicRefs.map(async (graphic) => {\n const spritesheet = await engine.getSpriteSheet(graphic);\n return withGraphicDisplayScale(spritesheet, scale);\n })));\n })\n )\n .subscribe((sheets) => { \n this.graphicsSignals.set(sheets);\n });\n\n engine.tick\n .pipe\n //throttleTime(10)\n ()\n .subscribe(() => {\n const frame = this.frames.shift();\n if (frame) {\n if (typeof frame.x !== \"number\" || typeof frame.y !== \"number\") return;\n const frameTs = this.getFrameTimestamp(frame);\n if (frameTs > 0 && frameTs <= this.lastAppliedFrameTs) return;\n engine.scene.setBodyPosition(\n this.id,\n frame.x,\n frame.y,\n \"top-left\"\n );\n if (frameTs > this.lastAppliedFrameTs) {\n this.lastAppliedFrameTs = frameTs;\n }\n }\n });\n }\n\n private getFrameTimestamp(frame: Frame): number {\n return getFrameTimestamp(frame);\n }\n\n /**\n * Access the shared client hook registry.\n *\n * @returns The hook service used to register and trigger client-side hooks.\n */\n get hooks() {\n return inject<Hooks>(ModulesToken);\n }\n\n /**\n * Access the current client engine instance.\n *\n * @returns The active {@link RpgClientEngine} instance.\n */\n get engine() {\n return inject(RpgClientEngine);\n }\n\n setHitbox(width: number, height: number): void {\n if (typeof width !== \"number\" || width <= 0) {\n throw new Error(\"setHitbox: width must be a positive number\");\n }\n if (typeof height !== \"number\" || height <= 0) {\n throw new Error(\"setHitbox: height must be a positive number\");\n }\n\n this.hitbox.set({\n w: width,\n h: height,\n });\n\n const sceneMap = (this.engine as any)?.sceneMap;\n if (sceneMap && this.id) {\n sceneMap.updateHitbox?.(this.id, this.x(), this.y(), width, height);\n }\n }\n\n private animationSubscription?: Subscription;\n private animationResetTimeout?: ReturnType<typeof setTimeout>;\n private animationWaitResolve?: () => void;\n\n private clearAnimationControls() {\n if (this.animationSubscription) {\n this.animationSubscription.unsubscribe();\n this.animationSubscription = undefined;\n }\n if (this.animationResetTimeout) {\n clearTimeout(this.animationResetTimeout);\n this.animationResetTimeout = undefined;\n }\n }\n\n private resolveAnimationWait() {\n const resolve = this.animationWaitResolve;\n this.animationWaitResolve = undefined;\n resolve?.();\n }\n\n private finishTemporaryAnimation() {\n const restoreState = this.animationRestoreState;\n this.clearAnimationControls();\n this.animationCurrentIndex.set(0);\n this.animationRestoreState = undefined;\n this.animationIsPlaying.set(false);\n if (restoreState) {\n this.animationName.set(restoreState.animationName);\n this.graphics.set([...restoreState.graphics]);\n }\n this.resolveAnimationWait();\n }\n\n /**\n * Trigger a flash animation on this sprite\n * \n * This method triggers a flash effect using CanvasEngine's flash directive.\n * The flash can be configured with various options including type (alpha, tint, or both),\n * duration, cycles, and color.\n * \n * ## Design\n * \n * The flash uses a trigger system that is connected to the flash directive in the\n * character component. This allows for flexible configuration and can be triggered\n * from both server events and client-side code.\n * \n * @param options - Flash configuration options\n * @param options.type - Type of flash effect: 'alpha' (opacity), 'tint' (color), or 'both' (default: 'alpha')\n * @param options.duration - Duration of the flash animation in milliseconds (default: 300)\n * @param options.cycles - Number of flash cycles (flash on/off) (default: 1)\n * @param options.alpha - Alpha value when flashing, from 0 to 1 (default: 0.3)\n * @param options.tint - Tint color when flashing as hex value or color name (default: 0xffffff - white)\n * \n * @example\n * ```ts\n * // Simple flash with default settings (alpha flash)\n * player.flash();\n * \n * // Flash with red tint\n * player.flash({ type: 'tint', tint: 0xff0000 });\n * \n * // Flash with both alpha and tint\n * player.flash({ \n * type: 'both', \n * alpha: 0.5, \n * tint: 0xff0000,\n * duration: 200,\n * cycles: 2\n * });\n * \n * // Quick damage flash\n * player.flash({ \n * type: 'tint', \n * tint: 0xff0000, \n * duration: 150,\n * cycles: 1\n * });\n * ```\n */\n flash(options?: FlashOptions): void {\n const flashOptions = {\n type: options?.type || 'alpha',\n duration: options?.duration ?? 300,\n cycles: options?.cycles ?? 1,\n alpha: options?.alpha ?? 0.3,\n tint: options?.tint ?? 0xffffff,\n };\n \n // Convert color name to hex if needed\n let tintValue = flashOptions.tint;\n if (typeof tintValue === 'string') {\n // Common color name to hex mapping\n const colorMap: Record<string, number> = {\n 'white': 0xffffff,\n 'red': 0xff0000,\n 'green': 0x00ff00,\n 'blue': 0x0000ff,\n 'yellow': 0xffff00,\n 'cyan': 0x00ffff,\n 'magenta': 0xff00ff,\n 'black': 0x000000,\n };\n tintValue = colorMap[tintValue.toLowerCase()] ?? 0xffffff;\n }\n \n this.flashTrigger.start({\n ...flashOptions,\n tint: tintValue,\n });\n }\n\n /**\n * Reset animation state when animation changes externally\n *\n * This method should be called when the animation changes due to movement\n * or other external factors to ensure the animation system doesn't get stuck\n *\n * @example\n * ```ts\n * // Reset when player starts moving\n * player.resetAnimationState();\n * ```\n */\n resetAnimationState() {\n if (this.animationRestoreState) {\n this.finishTemporaryAnimation();\n return;\n }\n this.animationIsPlaying.set(false);\n this.animationCurrentIndex.set(0);\n this.clearAnimationControls();\n this.resolveAnimationWait();\n }\n\n /**\n * Set a custom animation for a specific number of times\n *\n * Plays a custom animation for the specified number of repetitions.\n * The animation system prevents overlapping animations and automatically\n * returns to the previous animation when complete.\n *\n * @param animationName - Name of the animation to play\n * @param nbTimes - Number of times to repeat the animation (default: Infinity for continuous)\n * @param options - Restore and timeout options\n * @returns A promise resolved when a finite animation finishes, is interrupted, or times out\n *\n * @example\n * ```ts\n * // Play attack animation 3 times\n * await player.setAnimation('attack', 3);\n *\n * // Play continuous spell animation\n * player.setAnimation('spell');\n * ```\n */\n setAnimation(animationName: string, nbTimes?: number, options?: AnimationRestoreOptions): Promise<void>;\n /**\n * Set a custom animation with temporary graphic change\n *\n * Plays a custom animation for the specified number of repetitions and temporarily\n * changes the player's graphic (sprite sheet) during the animation. The graphic\n * is automatically reset when the animation finishes.\n *\n * @param animationName - Name of the animation to play\n * @param graphic - The graphic(s) to temporarily use during the animation\n * @param nbTimes - Number of times to repeat the animation (default: Infinity for continuous)\n * @param options - Restore and timeout options\n * @returns A promise resolved when a finite animation finishes, is interrupted, or times out\n *\n * @example\n * ```ts\n * // Play attack animation with temporary graphic change\n * await player.setAnimation('attack', 'hero_attack', 3);\n * ```\n */\n setAnimation(animationName: string, graphic?: string | string[], nbTimes?: number, options?: AnimationRestoreOptions): Promise<void>;\n setAnimation(\n animationName: string,\n graphicOrNbTimes?: string | string[] | number,\n nbTimesOrOptions?: number | AnimationRestoreOptions,\n options?: AnimationRestoreOptions\n ): Promise<void> {\n let graphic: string | string[] | undefined;\n let finalNbTimes: number = Infinity;\n let restoreOptions: AnimationRestoreOptions | undefined = options;\n\n // Handle overloads\n if (typeof graphicOrNbTimes === 'number') {\n // setAnimation(animationName, nbTimes)\n finalNbTimes = graphicOrNbTimes;\n restoreOptions = typeof nbTimesOrOptions === 'object' ? nbTimesOrOptions : options;\n } else if (graphicOrNbTimes !== undefined) {\n // setAnimation(animationName, graphic, nbTimes)\n graphic = graphicOrNbTimes;\n if (typeof nbTimesOrOptions === 'number') {\n finalNbTimes = nbTimesOrOptions;\n } else {\n finalNbTimes = Infinity;\n restoreOptions = nbTimesOrOptions ?? options;\n }\n } else {\n // setAnimation(animationName) - nbTimes remains Infinity\n finalNbTimes = Infinity;\n }\n\n if (this.animationIsPlaying()) {\n this.finishTemporaryAnimation();\n }\n\n const waitPromise =\n finalNbTimes === Infinity\n ? Promise.resolve()\n : new Promise<void>((resolve) => {\n this.animationWaitResolve = resolve;\n });\n\n this.animationIsPlaying.set(true);\n const previousAnimationName =\n restoreOptions?.restoreAnimationName ?? this.animationName();\n const previousGraphics = restoreOptions?.restoreGraphics\n ? [...restoreOptions.restoreGraphics]\n : [...this.graphics()];\n this.animationRestoreState = {\n animationName: previousAnimationName,\n graphics: previousGraphics,\n };\n this.animationCurrentIndex.set(0);\n\n // Temporarily change graphic if provided\n if (graphic !== undefined) {\n if (Array.isArray(graphic)) {\n this.graphics.set(graphic);\n } else {\n this.graphics.set([graphic]);\n }\n }\n\n this.clearAnimationControls();\n\n this.animationSubscription =\n this.animationCurrentIndex.observable.subscribe((index) => {\n if (index >= finalNbTimes) {\n this.finishTemporaryAnimation();\n }\n });\n\n if (finalNbTimes !== Infinity) {\n this.animationResetTimeout = setTimeout(() => {\n if (this.animationIsPlaying()) {\n this.finishTemporaryAnimation();\n }\n }, restoreOptions?.timeoutMs ?? Math.max(1000, finalNbTimes * 1000));\n }\n\n this.animationName.set(animationName);\n\n return waitPromise;\n }\n\n /**\n * Display a registered component animation effect on this object.\n *\n * @param id - Identifier of the component animation to play.\n * @param params - Parameters forwarded to the animation effect.\n * @returns A promise resolved when the animation component calls `onFinish`.\n */\n showComponentAnimation(id: string, params: any): Promise<void> {\n const engine = inject(RpgClientEngine);\n return engine.getComponentAnimation(id).displayEffect(params, this);\n }\n\n /**\n * Display a registered spritesheet animation effect on this object.\n *\n * @param graphic - Identifier of the spritesheet to use.\n * @param animationName - Name of the animation inside the spritesheet.\n * @returns A promise resolved when the animation component calls `onFinish`.\n */\n showAnimation(graphic: string, animationName: string = 'default'): Promise<void> {\n return this.showComponentAnimation('animation', {\n graphic,\n animationName,\n });\n }\n \n /**\n * Check whether this client object represents an event.\n *\n * @returns `true` if the object type is `event`, otherwise `false`.\n */\n isEvent(): boolean {\n return this._type === 'event';\n }\n\n /**\n * Check whether this client object represents a player.\n *\n * @returns `true` if the object type is `player`, otherwise `false`.\n */\n isPlayer(): boolean {\n return this._type === 'player';\n }\n}\n"],"mappings":"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"}
package/dist/Gui/Gui.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- import { Context } from '@signe/di';
2
1
  import { Signal, WritableSignal } from 'canvasengine';
3
2
  import { Subscription } from 'rxjs';
3
+ import { RpgContext } from '@rpgjs/common';
4
4
  interface GuiOptions {
5
5
  name?: string;
6
6
  id?: string;
@@ -60,7 +60,7 @@ export declare class RpgGui {
60
60
  * Key: player ID, Value: boolean (true = show, false = hide)
61
61
  */
62
62
  attachedGuiDisplayState: import('canvasengine').WritableObjectSignal<Record<string, boolean>>;
63
- constructor(context: Context);
63
+ constructor(context: RpgContext);
64
64
  _initialize(): Promise<void>;
65
65
  /**
66
66
  * Set the VueGui instance reference for Vue component management
@@ -1 +1 @@
1
- {"version":3,"file":"Gui.js","names":[],"sources":["../../src/Gui/Gui.ts"],"sourcesContent":["import { Context, inject } from \"@signe/di\";\nimport { signal, Signal, WritableSignal } from \"canvasengine\";\nimport { AbstractWebsocket, WebSocketToken } from \"../services/AbstractSocket\";\nimport { DialogboxComponent, ShopComponent, SaveLoadComponent, MainMenuComponent, NotificationComponent, TitleScreenComponent, GameoverComponent, InputComponent } from \"../components/gui\";\nimport { combineLatest, Subscription } from \"rxjs\";\nimport { PrebuiltGui } from \"@rpgjs/common\";\n\ninterface GuiOptions {\n name?: string;\n id?: string;\n component?: any;\n display?: boolean;\n data?: any;\n /**\n * Auto display the GUI when added to the system\n * @default false\n */\n autoDisplay?: boolean;\n /**\n * Function that returns an array of Signal dependencies\n * The GUI will only display when all dependencies are resolved (!= undefined)\n * @returns Array of Signal dependencies\n */\n dependencies?: () => Signal[];\n /**\n * Attach the GUI to sprites instead of displaying globally\n * When true, the GUI will be rendered in character.ce for each sprite\n * @default false\n */\n attachToSprite?: boolean;\n /**\n * Vue v4 compatibility flag. Prefer attachToSprite in v5 projects.\n */\n rpgAttachToSprite?: boolean;\n}\n\nexport interface GuiInstance {\n name: string;\n component: any;\n display: WritableSignal<boolean>;\n data: WritableSignal<any>;\n openId?: string;\n autoDisplay: boolean;\n dependencies?: Signal[];\n subscription?: Subscription;\n attachToSprite?: boolean;\n}\n\ntype GuiState = {\n name: string;\n component: any;\n display: boolean;\n data: any;\n openId?: string;\n attachToSprite: boolean;\n};\n\ntype VueGuiBridge = {\n updateGuiState?: (state: GuiState) => void;\n initializeGuiStates?: (states: GuiState[]) => void;\n};\n\ninterface GuiAction {\n guiId: string;\n name: string;\n data: any;\n clientActionId: string;\n}\n\ntype OptimisticReducer = (data: any, action: GuiAction) => any;\n\nconst throwError = (id: string) => {\n throw `The GUI named ${id} is non-existent. Please add the component in the gui property of the decorator @RpgClient`;\n};\n\nconst updateItemQuantity = (items: any[], id: string) => {\n const index = items.findIndex((item) => item?.id === id);\n if (index === -1) return items;\n const item = items[index];\n if (item?.usable === false) return items;\n if (item?.consumable === false) return items;\n const quantity = typeof item?.quantity === \"number\" ? item.quantity : 1;\n const nextQuantity = Math.max(0, quantity - 1);\n if (nextQuantity === quantity) return items;\n if (nextQuantity <= 0) {\n return items.filter((_, idx) => idx !== index);\n }\n const nextItems = items.slice();\n nextItems[index] = { ...item, quantity: nextQuantity };\n return nextItems;\n};\n\nconst updateEquippedFlag = (items: any[], id: string, equip: boolean) => {\n const index = items.findIndex((item) => item?.id === id);\n if (index === -1) return items;\n const item = items[index];\n if (item?.equipped === equip) return items;\n const nextItems = items.slice();\n nextItems[index] = { ...item, equipped: equip };\n return nextItems;\n};\n\nconst mainMenuOptimisticReducer: OptimisticReducer = (data, action) => {\n if (!data || typeof data !== \"object\") return data;\n if (action.name === \"useItem\") {\n if (!Array.isArray(data.items)) return data;\n const id = action.data?.id;\n if (!id) return data;\n const nextItems = updateItemQuantity(data.items, id);\n if (nextItems === data.items) return data;\n return { ...data, items: nextItems };\n }\n if (action.name === \"equipItem\") {\n const id = action.data?.id;\n if (!id || typeof action.data?.equip !== \"boolean\") return data;\n const equip = action.data.equip;\n let nextItems = data.items;\n let nextEquips = data.equips;\n if (Array.isArray(data.items)) {\n nextItems = updateEquippedFlag(data.items, id, equip);\n }\n if (Array.isArray(data.equips)) {\n nextEquips = updateEquippedFlag(data.equips, id, equip);\n }\n if (nextItems === data.items && nextEquips === data.equips) return data;\n return {\n ...data,\n ...(nextItems !== data.items ? { items: nextItems } : {}),\n ...(nextEquips !== data.equips ? { equips: nextEquips } : {})\n };\n }\n return data;\n};\n\nexport class RpgGui {\n private webSocket: AbstractWebsocket;\n gui = signal<Record<string, GuiInstance>>({});\n extraGuis: GuiInstance[] = [];\n private vueGuiInstance: VueGuiBridge | null = null;\n private optimisticReducers = new Map<string, OptimisticReducer[]>();\n private pendingActions = new Map<string, GuiAction[]>();\n /**\n * Signal tracking which player IDs should display attached GUIs\n * Key: player ID, Value: boolean (true = show, false = hide)\n */\n attachedGuiDisplayState = signal<Record<string, boolean>>({});\n\n constructor(private context: Context) {\n this.webSocket = inject(context, WebSocketToken);\n this.add({\n name: \"rpg-dialog\",\n component: DialogboxComponent,\n });\n this.add({\n name: PrebuiltGui.MainMenu,\n component: MainMenuComponent,\n });\n this.add({\n name: PrebuiltGui.Shop,\n component: ShopComponent,\n });\n this.add({\n name: PrebuiltGui.Notification,\n component: NotificationComponent,\n autoDisplay: true,\n });\n this.add({\n name: PrebuiltGui.Save,\n component: SaveLoadComponent,\n });\n this.add({\n name: PrebuiltGui.TitleScreen,\n component: TitleScreenComponent,\n });\n this.add({\n name: PrebuiltGui.Gameover,\n component: GameoverComponent,\n });\n this.add({\n name: PrebuiltGui.Input,\n component: InputComponent,\n });\n\n this.registerOptimisticReducer(PrebuiltGui.MainMenu, mainMenuOptimisticReducer);\n }\n\n async _initialize() {\n this.webSocket.on(\"gui.open\", (data: { guiId: string; data: any; guiOpenId?: string }) => {\n this.clearPendingActions(data.guiId);\n this.display(data.guiId, data.data, [], data.guiOpenId);\n });\n\n this.webSocket.on(\"gui.exit\", (payload: string | { guiId: string; guiOpenId?: string }) => {\n const guiId = typeof payload === \"string\" ? payload : payload.guiId;\n const guiOpenId = typeof payload === \"string\" ? undefined : payload.guiOpenId;\n const current = this.get(guiId);\n if (guiOpenId && current?.openId && current.openId !== guiOpenId) {\n return;\n }\n this.hide(guiId);\n });\n\n this.webSocket.on(\"gui.update\", (payload: { guiId: string; data: any; clientActionId?: string }) => {\n this.applyServerUpdate(payload.guiId, payload.data, payload.clientActionId);\n });\n\n /**\n * Listen for tooltip display state changes from server\n * This is triggered by showAttachedGui/hideAttachedGui on the server\n */\n this.webSocket.on(\"gui.tooltip\", (data: { players: string[]; display: boolean }) => {\n const currentState = { ...this.attachedGuiDisplayState() };\n data.players.forEach((playerId) => {\n currentState[playerId] = data.display;\n });\n this.attachedGuiDisplayState.set(currentState);\n });\n }\n\n /**\n * Set the VueGui instance reference for Vue component management\n * This is called by VueGui when it's initialized\n * \n * @param vueGuiInstance - The VueGui instance\n */\n _setVueGuiInstance(vueGuiInstance: any) {\n this.vueGuiInstance = vueGuiInstance;\n this._initializeVueComponents();\n }\n\n /**\n * Notify VueGui about GUI state changes\n * This synchronizes the Vue component display state\n * \n * @param guiId - The GUI component ID\n * @param display - Display state\n * @param data - Component data\n */\n private _notifyVueGui(guiId: string, display: boolean, data: any = {}) {\n const extraGui = this.extraGuis.find(gui => gui.name === guiId);\n if (!extraGui) return;\n this.vueGuiInstance?.updateGuiState?.(this.toGuiState(extraGui, display, data));\n }\n\n /**\n * Initialize Vue components in the VueGui instance\n * This should be called after VueGui is mounted\n */\n _initializeVueComponents() {\n this.vueGuiInstance?.initializeGuiStates?.(\n this.extraGuis.map(gui => this.toGuiState(gui))\n );\n }\n\n guiInteraction(guiId: string, name: string, data: any) {\n const clientActionId = globalThis.crypto?.randomUUID?.() || `${Date.now()}-${Math.random()}`;\n const actionData = { ...(data || {}), clientActionId };\n this.applyOptimisticAction({\n guiId,\n name,\n data: actionData,\n clientActionId\n });\n this.webSocket.emit(\"gui.interaction\", {\n guiId,\n name,\n data: actionData,\n });\n }\n\n guiClose(guiId: string, data?: any, guiOpenId?: unknown) {\n const normalizedOpenId =\n typeof guiOpenId === \"string\" && guiOpenId.length > 0 ? guiOpenId : undefined;\n this.webSocket.emit(\"gui.exit\", {\n guiId,\n guiOpenId: normalizedOpenId,\n data,\n });\n }\n\n /**\n * Add a GUI component to the system\n * \n * By default, only CanvasEngine components (.ce files) are accepted.\n * Vue components should be handled by the @rpgjs/vue package.\n * \n * @param gui - GUI configuration options\n * @param gui.name - Name or ID of the GUI component\n * @param gui.id - Alternative ID if name is not provided\n * @param gui.component - The component to render (must be a CanvasEngine component)\n * @param gui.display - Initial display state (default: false)\n * @param gui.data - Initial data for the component\n * @param gui.autoDisplay - Auto display when added (default: false)\n * @param gui.dependencies - Function returning Signal dependencies\n * @param gui.attachToSprite - Attach GUI to sprites instead of global display (default: false)\n * \n * @example\n * ```ts\n * gui.add({\n * name: 'inventory',\n * component: InventoryComponent, // Must be a .ce component\n * autoDisplay: true,\n * dependencies: () => [playerSignal, inventorySignal]\n * });\n * \n * // Attach to sprites\n * gui.add({\n * name: 'tooltip',\n * component: TooltipComponent,\n * attachToSprite: true\n * });\n * ```\n */\n add(gui: GuiOptions | any) {\n const component = this.resolveComponent(gui);\n const guiId = this.resolveGuiId(gui, component);\n if (!guiId) {\n throw new Error(\"GUI must have a name or id\");\n }\n const attachToSprite = this.resolveAttachToSprite(gui, component);\n const guiInstance: GuiInstance = {\n name: guiId,\n component,\n display: signal<boolean>(gui.display || false),\n data: signal<any>(gui.data || {}),\n openId: undefined,\n autoDisplay: gui.autoDisplay || false,\n dependencies: gui.dependencies ? gui.dependencies() : [],\n attachToSprite,\n };\n\n if (this.isVueComponentInstance(guiInstance)) {\n this.removeCanvasGui(guiId);\n const existingIndex = this.extraGuis.findIndex(existing => existing.name === guiId);\n if (existingIndex >= 0) {\n this.extraGuis[existingIndex].subscription?.unsubscribe();\n this.extraGuis[existingIndex] = guiInstance;\n } else {\n this.extraGuis.push(guiInstance);\n }\n\n this._initializeVueComponents();\n \n if (guiInstance.autoDisplay) {\n this.display(guiId, gui.data);\n } else {\n this._notifyVueGui(guiId, guiInstance.display(), guiInstance.data());\n }\n return;\n }\n\n this.removeVueGui(guiId);\n this.gui()[guiId] = guiInstance;\n this._initializeVueComponents();\n\n // Auto display if enabled and it's a CanvasEngine component\n if (guiInstance.autoDisplay && typeof gui.component === 'function') {\n this.display(guiId, gui.data);\n }\n }\n\n registerOptimisticReducer(guiId: string, reducer: OptimisticReducer) {\n const existing = this.optimisticReducers.get(guiId) || [];\n this.optimisticReducers.set(guiId, existing.concat(reducer));\n }\n\n /**\n * Get all attached GUI components (attachToSprite: true)\n * \n * Returns all GUI instances that are configured to be attached to sprites.\n * These GUIs should be rendered in character.ce instead of canvas.ce.\n * \n * @returns Array of GUI instances with attachToSprite: true\n * \n * @example\n * ```ts\n * const attachedGuis = gui.getAttachedGuis();\n * // Use in character.ce to render tooltips\n * ```\n */\n getAttachedGuis(): GuiInstance[] {\n return Object.values(this.gui()).filter(gui => gui.attachToSprite === true);\n }\n\n getVueGuis(): GuiInstance[] {\n return [...this.extraGuis];\n }\n\n getAttachedVueGuis(): GuiInstance[] {\n return this.extraGuis.filter(gui => gui.attachToSprite === true);\n }\n\n /**\n * Check if a player should display attached GUIs\n * \n * @param playerId - The player ID to check\n * @returns true if attached GUIs should be displayed for this player\n */\n shouldDisplayAttachedGui(playerId: string): boolean {\n return this.attachedGuiDisplayState()[playerId] === true;\n }\n\n get(id: string): GuiInstance | undefined {\n // Check CanvasEngine GUIs first\n const canvasGui = this.gui()[id];\n if (canvasGui) {\n return canvasGui;\n }\n \n // Check Vue GUIs in extraGuis\n return this.extraGuis.find(gui => gui.name === id);\n }\n\n exists(id: string): boolean {\n return !!this.get(id);\n }\n\n getAll(): Record<string, GuiInstance> {\n const allGuis = { ...this.gui() };\n \n // Add extraGuis to the result\n this.extraGuis.forEach(gui => {\n allGuis[gui.name] = gui;\n });\n \n return allGuis;\n }\n\n /**\n * Display a GUI component\n * \n * Displays the GUI immediately if no dependencies are configured,\n * or waits for all dependencies to be resolved if dependencies are present.\n * Automatically manages subscriptions to prevent memory leaks.\n * Works with both CanvasEngine components and Vue components.\n * \n * @param id - The GUI component ID\n * @param data - Data to pass to the component\n * @param dependencies - Optional runtime dependencies (overrides config dependencies)\n * \n * @example\n * ```ts\n * // Display immediately\n * gui.display('inventory', { items: [] });\n * \n * // Display with runtime dependencies\n * gui.display('shop', { shopId: 1 }, [playerSignal, shopSignal]);\n * ```\n */\n display(id: string, data = {}, dependencies: Signal[] = [], openId?: string) {\n if (!this.exists(id)) {\n throw throwError(id);\n }\n\n const guiInstance = this.get(id)!;\n const isVueComponent = this.extraGuis.some(gui => gui.name === id);\n\n if (guiInstance.subscription) {\n guiInstance.subscription.unsubscribe();\n guiInstance.subscription = undefined;\n }\n\n const show = () => {\n guiInstance.openId = openId;\n guiInstance.data.set(data);\n guiInstance.display.set(true);\n if (isVueComponent) {\n this._notifyVueGui(id, true, data);\n }\n };\n\n const deps = dependencies.length > 0\n ? dependencies\n : (guiInstance.dependencies ?? []);\n\n if (deps.length > 0) {\n const values = deps.map(dependency => dependency());\n const subscription = new Subscription();\n const showIfReady = () => {\n if (values.every(value => value !== undefined)) {\n show();\n }\n };\n\n deps.forEach((dependency, index) => {\n subscription.add(dependency.observable.subscribe((value) => {\n values[index] = value;\n showIfReady();\n }));\n });\n\n guiInstance.subscription = subscription;\n showIfReady();\n return;\n }\n\n show();\n }\n\n isDisplaying(id: string): boolean {\n const guiInstance = this.get(id);\n if (!guiInstance) return false;\n return guiInstance.display();\n }\n\n /**\n * Handle Vue component display logic\n * \n * @param id - GUI component ID\n * @param data - Component data\n * @param dependencies - Runtime dependencies\n * @param guiInstance - GUI instance\n */\n private _handleVueComponentDisplay(id: string, data: any, dependencies: Signal[], guiInstance: GuiInstance, openId?: string) {\n // Unsubscribe from previous subscription if exists\n if (guiInstance.subscription) {\n guiInstance.subscription.unsubscribe();\n guiInstance.subscription = undefined;\n }\n\n // Use runtime dependencies or config dependencies\n const deps = dependencies.length > 0 \n ? dependencies \n : (guiInstance.dependencies ?? []);\n\n if (deps.length > 0) {\n const values = deps.map(dependency => dependency());\n const subscription = new Subscription();\n const showIfReady = () => {\n if (values.every(value => value !== undefined)) {\n guiInstance.openId = openId;\n guiInstance.data.set(data);\n guiInstance.display.set(true);\n this._notifyVueGui(id, true, data);\n }\n };\n\n deps.forEach((dependency, index) => {\n subscription.add(dependency.observable.subscribe((value) => {\n values[index] = value;\n showIfReady();\n }));\n });\n\n guiInstance.subscription = subscription;\n showIfReady();\n return;\n }\n\n // No dependencies, display immediately\n guiInstance.openId = openId;\n guiInstance.data.set(data);\n guiInstance.display.set(true);\n this._notifyVueGui(id, true, data);\n }\n\n /**\n * Hide a GUI component\n * \n * Hides the GUI and cleans up any active subscriptions.\n * Works with both CanvasEngine components and Vue components.\n * \n * @param id - The GUI component ID\n * \n * @example\n * ```ts\n * gui.hide('inventory');\n * ```\n */\n hide(id: string) {\n if (!this.exists(id)) {\n throw throwError(id);\n }\n\n const guiInstance = this.get(id)!;\n \n // Unsubscribe if there's an active subscription\n if (guiInstance.subscription) {\n guiInstance.subscription.unsubscribe();\n guiInstance.subscription = undefined;\n }\n\n guiInstance.display.set(false)\n guiInstance.openId = undefined;\n \n // Check if it's a Vue component and notify VueGui\n const isVueComponent = this.extraGuis.some(gui => gui.name === id);\n if (isVueComponent) {\n this._notifyVueGui(id, false);\n }\n }\n\n private isVueComponent(id: string) {\n return this.extraGuis.some(gui => gui.name === id);\n }\n\n private isVueComponentInstance(gui: GuiInstance) {\n return typeof gui.component !== \"function\";\n }\n\n private removeCanvasGui(guiId: string) {\n const current = this.gui();\n if (!(guiId in current)) return;\n const next = { ...current };\n delete next[guiId];\n this.gui.set(next);\n }\n\n private removeVueGui(guiId: string) {\n const removed = this.extraGuis.filter(existing => existing.name === guiId);\n removed.forEach(gui => gui.subscription?.unsubscribe());\n if (removed.length > 0) {\n this.extraGuis = this.extraGuis.filter(existing => existing.name !== guiId);\n }\n }\n\n private resolveComponent(gui: GuiOptions | any) {\n return gui?.component ?? gui;\n }\n\n private resolveGuiId(gui: GuiOptions | any, component: any) {\n return gui?.name || gui?.id || component?.name || component?.__name;\n }\n\n private resolveAttachToSprite(gui: GuiOptions | any, component: any) {\n return !!(gui?.attachToSprite || gui?.rpgAttachToSprite || component?.attachToSprite || component?.rpgAttachToSprite);\n }\n\n private toGuiState(gui: GuiInstance, display = gui.display(), data = gui.data()): GuiState {\n return {\n name: gui.name,\n component: gui.component,\n display,\n data,\n openId: gui.openId,\n attachToSprite: gui.attachToSprite || false,\n };\n }\n\n private clearPendingActions(guiId: string) {\n this.pendingActions.delete(guiId);\n }\n\n private applyReducers(guiId: string, data: any, actions: GuiAction[]) {\n const reducers = this.optimisticReducers.get(guiId);\n if (!reducers || reducers.length === 0) return data;\n let next = data;\n for (const action of actions) {\n for (const reducer of reducers) {\n const updated = reducer(next, action);\n if (updated !== undefined && updated !== null && updated !== next) {\n next = updated;\n }\n }\n }\n return next;\n }\n\n private applyOptimisticAction(action: GuiAction) {\n const guiInstance = this.get(action.guiId);\n if (!guiInstance) return;\n const reducers = this.optimisticReducers.get(action.guiId);\n if (!reducers || reducers.length === 0) return;\n const currentData = guiInstance.data();\n const nextData = this.applyReducers(action.guiId, currentData, [action]);\n if (nextData === currentData) return;\n guiInstance.data.set(nextData);\n const pending = this.pendingActions.get(action.guiId) || [];\n pending.push(action);\n this.pendingActions.set(action.guiId, pending);\n if (this.isVueComponent(action.guiId)) {\n this._notifyVueGui(action.guiId, guiInstance.display(), nextData);\n }\n }\n\n private applyServerUpdate(guiId: string, data: any, clientActionId?: string) {\n const guiInstance = this.get(guiId);\n if (!guiInstance) return;\n let pending = this.pendingActions.get(guiId) || [];\n if (clientActionId) {\n pending = pending.filter(action => action.clientActionId !== clientActionId);\n } else {\n pending = [];\n }\n let nextData = data;\n if (pending.length) {\n nextData = this.applyReducers(guiId, nextData, pending);\n }\n guiInstance.data.set(nextData);\n this.pendingActions.set(guiId, pending);\n if (this.isVueComponent(guiId)) {\n this._notifyVueGui(guiId, guiInstance.display(), nextData);\n }\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;AAuEA,IAAM,cAAc,OAAe;CACjC,MAAM,iBAAiB,GAAG;AAC5B;AAEA,IAAM,sBAAsB,OAAc,OAAe;CACvD,MAAM,QAAQ,MAAM,WAAW,SAAS,MAAM,OAAO,EAAE;CACvD,IAAI,UAAU,IAAI,OAAO;CACzB,MAAM,OAAO,MAAM;CACnB,IAAI,MAAM,WAAW,OAAO,OAAO;CACnC,IAAI,MAAM,eAAe,OAAO,OAAO;CACvC,MAAM,WAAW,OAAO,MAAM,aAAa,WAAW,KAAK,WAAW;CACtE,MAAM,eAAe,KAAK,IAAI,GAAG,WAAW,CAAC;CAC7C,IAAI,iBAAiB,UAAU,OAAO;CACtC,IAAI,gBAAgB,GAClB,OAAO,MAAM,QAAQ,GAAG,QAAQ,QAAQ,KAAK;CAE/C,MAAM,YAAY,MAAM,MAAM;CAC9B,UAAU,SAAS;EAAE,GAAG;EAAM,UAAU;CAAa;CACrD,OAAO;AACT;AAEA,IAAM,sBAAsB,OAAc,IAAY,UAAmB;CACvE,MAAM,QAAQ,MAAM,WAAW,SAAS,MAAM,OAAO,EAAE;CACvD,IAAI,UAAU,IAAI,OAAO;CACzB,MAAM,OAAO,MAAM;CACnB,IAAI,MAAM,aAAa,OAAO,OAAO;CACrC,MAAM,YAAY,MAAM,MAAM;CAC9B,UAAU,SAAS;EAAE,GAAG;EAAM,UAAU;CAAM;CAC9C,OAAO;AACT;AAEA,IAAM,6BAAgD,MAAM,WAAW;CACrE,IAAI,CAAC,QAAQ,OAAO,SAAS,UAAU,OAAO;CAC9C,IAAI,OAAO,SAAS,WAAW;EAC7B,IAAI,CAAC,MAAM,QAAQ,KAAK,KAAK,GAAG,OAAO;EACvC,MAAM,KAAK,OAAO,MAAM;EACxB,IAAI,CAAC,IAAI,OAAO;EAChB,MAAM,YAAY,mBAAmB,KAAK,OAAO,EAAE;EACnD,IAAI,cAAc,KAAK,OAAO,OAAO;EACrC,OAAO;GAAE,GAAG;GAAM,OAAO;EAAU;CACrC;CACA,IAAI,OAAO,SAAS,aAAa;EAC/B,MAAM,KAAK,OAAO,MAAM;EACxB,IAAI,CAAC,MAAM,OAAO,OAAO,MAAM,UAAU,WAAW,OAAO;EAC3D,MAAM,QAAQ,OAAO,KAAK;EAC1B,IAAI,YAAY,KAAK;EACrB,IAAI,aAAa,KAAK;EACtB,IAAI,MAAM,QAAQ,KAAK,KAAK,GAC1B,YAAY,mBAAmB,KAAK,OAAO,IAAI,KAAK;EAEtD,IAAI,MAAM,QAAQ,KAAK,MAAM,GAC3B,aAAa,mBAAmB,KAAK,QAAQ,IAAI,KAAK;EAExD,IAAI,cAAc,KAAK,SAAS,eAAe,KAAK,QAAQ,OAAO;EACnE,OAAO;GACL,GAAG;GACH,GAAI,cAAc,KAAK,QAAQ,EAAE,OAAO,UAAU,IAAI,CAAC;GACvD,GAAI,eAAe,KAAK,SAAS,EAAE,QAAQ,WAAW,IAAI,CAAC;EAC7D;CACF;CACA,OAAO;AACT;AAEA,IAAa,SAAb,MAAoB;CAalB,YAAY,SAA0B;EAAlB,KAAA,UAAA;aAXd,OAAoC,CAAC,CAAC;mBACjB,CAAC;wBACkB;4CACjB,IAAI,IAAiC;wCACzC,IAAI,IAAyB;iCAK5B,OAAgC,CAAC,CAAC;EAG1D,KAAK,YAAY,OAAO,SAAS,cAAc;EAC/C,KAAK,IAAI;GACP,MAAM;GACN,WAAW;EACb,CAAC;EACD,KAAK,IAAI;GACP,MAAM,YAAY;GAClB,WAAW;EACb,CAAC;EACD,KAAK,IAAI;GACP,MAAM,YAAY;GAClB,WAAW;EACb,CAAC;EACD,KAAK,IAAI;GACP,MAAM,YAAY;GAClB,WAAW;GACX,aAAa;EACf,CAAC;EACD,KAAK,IAAI;GACP,MAAM,YAAY;GAClB,WAAW;EACb,CAAC;EACD,KAAK,IAAI;GACP,MAAM,YAAY;GAClB,WAAW;EACb,CAAC;EACD,KAAK,IAAI;GACP,MAAM,YAAY;GAClB,WAAW;EACb,CAAC;EACD,KAAK,IAAI;GACP,MAAM,YAAY;GAClB,WAAW;EACb,CAAC;EAED,KAAK,0BAA0B,YAAY,UAAU,yBAAyB;CAChF;CAEA,MAAM,cAAc;EAClB,KAAK,UAAU,GAAG,aAAa,SAA2D;GACxF,KAAK,oBAAoB,KAAK,KAAK;GACnC,KAAK,QAAQ,KAAK,OAAO,KAAK,MAAM,CAAC,GAAG,KAAK,SAAS;EACxD,CAAC;EAED,KAAK,UAAU,GAAG,aAAa,YAA4D;GACzF,MAAM,QAAQ,OAAO,YAAY,WAAW,UAAU,QAAQ;GAC9D,MAAM,YAAY,OAAO,YAAY,WAAW,KAAA,IAAY,QAAQ;GACpE,MAAM,UAAU,KAAK,IAAI,KAAK;GAC9B,IAAI,aAAa,SAAS,UAAU,QAAQ,WAAW,WACrD;GAEF,KAAK,KAAK,KAAK;EACjB,CAAC;EAED,KAAK,UAAU,GAAG,eAAe,YAAmE;GAClG,KAAK,kBAAkB,QAAQ,OAAO,QAAQ,MAAM,QAAQ,cAAc;EAC5E,CAAC;;;;;EAMD,KAAK,UAAU,GAAG,gBAAgB,SAAkD;GAClF,MAAM,eAAe,EAAE,GAAG,KAAK,wBAAwB,EAAE;GACzD,KAAK,QAAQ,SAAS,aAAa;IACjC,aAAa,YAAY,KAAK;GAChC,CAAC;GACD,KAAK,wBAAwB,IAAI,YAAY;EAC/C,CAAC;CACH;;;;;;;CAQA,mBAAmB,gBAAqB;EACtC,KAAK,iBAAiB;EACtB,KAAK,yBAAyB;CAChC;;;;;;;;;CAUA,cAAsB,OAAe,SAAkB,OAAY,CAAC,GAAG;EACrE,MAAM,WAAW,KAAK,UAAU,MAAK,QAAO,IAAI,SAAS,KAAK;EAC9D,IAAI,CAAC,UAAU;EACf,KAAK,gBAAgB,iBAAiB,KAAK,WAAW,UAAU,SAAS,IAAI,CAAC;CAChF;;;;;CAMA,2BAA2B;EACzB,KAAK,gBAAgB,sBACnB,KAAK,UAAU,KAAI,QAAO,KAAK,WAAW,GAAG,CAAC,CAChD;CACF;CAEA,eAAe,OAAe,MAAc,MAAW;EACrD,MAAM,iBAAiB,WAAW,QAAQ,aAAa,KAAK,GAAG,KAAK,IAAI,EAAE,GAAG,KAAK,OAAO;EACzF,MAAM,aAAa;GAAE,GAAI,QAAQ,CAAC;GAAI;EAAe;EACrD,KAAK,sBAAsB;GACzB;GACA;GACA,MAAM;GACN;EACF,CAAC;EACD,KAAK,UAAU,KAAK,mBAAmB;GACrC;GACA;GACA,MAAM;EACR,CAAC;CACH;CAEA,SAAS,OAAe,MAAY,WAAqB;EACvD,MAAM,mBACJ,OAAO,cAAc,YAAY,UAAU,SAAS,IAAI,YAAY,KAAA;EACtE,KAAK,UAAU,KAAK,YAAY;GAC9B;GACA,WAAW;GACX;EACF,CAAC;CACH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmCA,IAAI,KAAuB;EACzB,MAAM,YAAY,KAAK,iBAAiB,GAAG;EAC3C,MAAM,QAAQ,KAAK,aAAa,KAAK,SAAS;EAC9C,IAAI,CAAC,OACH,MAAM,IAAI,MAAM,4BAA4B;EAE9C,MAAM,iBAAiB,KAAK,sBAAsB,KAAK,SAAS;EAChE,MAAM,cAA2B;GAC/B,MAAM;GACN;GACA,SAAS,OAAgB,IAAI,WAAW,KAAK;GAC7C,MAAM,OAAY,IAAI,QAAQ,CAAC,CAAC;GAChC,QAAQ,KAAA;GACR,aAAa,IAAI,eAAe;GAChC,cAAc,IAAI,eAAe,IAAI,aAAa,IAAI,CAAC;GACvD;EACF;EAEA,IAAI,KAAK,uBAAuB,WAAW,GAAG;GAC5C,KAAK,gBAAgB,KAAK;GAC1B,MAAM,gBAAgB,KAAK,UAAU,WAAU,aAAY,SAAS,SAAS,KAAK;GAClF,IAAI,iBAAiB,GAAG;IACtB,KAAK,UAAU,eAAe,cAAc,YAAY;IACxD,KAAK,UAAU,iBAAiB;GAClC,OACE,KAAK,UAAU,KAAK,WAAW;GAGjC,KAAK,yBAAyB;GAE9B,IAAI,YAAY,aACd,KAAK,QAAQ,OAAO,IAAI,IAAI;QAE5B,KAAK,cAAc,OAAO,YAAY,QAAQ,GAAG,YAAY,KAAK,CAAC;GAErE;EACF;EAEA,KAAK,aAAa,KAAK;EACvB,KAAK,IAAI,EAAE,SAAS;EACpB,KAAK,yBAAyB;EAG9B,IAAI,YAAY,eAAe,OAAO,IAAI,cAAc,YACtD,KAAK,QAAQ,OAAO,IAAI,IAAI;CAEhC;CAEA,0BAA0B,OAAe,SAA4B;EACnE,MAAM,WAAW,KAAK,mBAAmB,IAAI,KAAK,KAAK,CAAC;EACxD,KAAK,mBAAmB,IAAI,OAAO,SAAS,OAAO,OAAO,CAAC;CAC7D;;;;;;;;;;;;;;;CAgBA,kBAAiC;EAC/B,OAAO,OAAO,OAAO,KAAK,IAAI,CAAC,EAAE,QAAO,QAAO,IAAI,mBAAmB,IAAI;CAC5E;CAEA,aAA4B;EAC1B,OAAO,CAAC,GAAG,KAAK,SAAS;CAC3B;CAEA,qBAAoC;EAClC,OAAO,KAAK,UAAU,QAAO,QAAO,IAAI,mBAAmB,IAAI;CACjE;;;;;;;CAQA,yBAAyB,UAA2B;EAClD,OAAO,KAAK,wBAAwB,EAAE,cAAc;CACtD;CAEA,IAAI,IAAqC;EAEvC,MAAM,YAAY,KAAK,IAAI,EAAE;EAC7B,IAAI,WACF,OAAO;EAIT,OAAO,KAAK,UAAU,MAAK,QAAO,IAAI,SAAS,EAAE;CACnD;CAEA,OAAO,IAAqB;EAC1B,OAAO,CAAC,CAAC,KAAK,IAAI,EAAE;CACtB;CAEA,SAAsC;EACpC,MAAM,UAAU,EAAE,GAAG,KAAK,IAAI,EAAE;EAGhC,KAAK,UAAU,SAAQ,QAAO;GAC5B,QAAQ,IAAI,QAAQ;EACtB,CAAC;EAED,OAAO;CACT;;;;;;;;;;;;;;;;;;;;;;CAuBA,QAAQ,IAAY,OAAO,CAAC,GAAG,eAAyB,CAAC,GAAG,QAAiB;EAC3E,IAAI,CAAC,KAAK,OAAO,EAAE,GACjB,MAAM,WAAW,EAAE;EAGrB,MAAM,cAAc,KAAK,IAAI,EAAE;EAC/B,MAAM,iBAAiB,KAAK,UAAU,MAAK,QAAO,IAAI,SAAS,EAAE;EAEjE,IAAI,YAAY,cAAc;GAC5B,YAAY,aAAa,YAAY;GACrC,YAAY,eAAe,KAAA;EAC7B;EAEA,MAAM,aAAa;GACjB,YAAY,SAAS;GACrB,YAAY,KAAK,IAAI,IAAI;GACzB,YAAY,QAAQ,IAAI,IAAI;GAC5B,IAAI,gBACF,KAAK,cAAc,IAAI,MAAM,IAAI;EAErC;EAEA,MAAM,OAAO,aAAa,SAAS,IAC/B,eACC,YAAY,gBAAgB,CAAC;EAElC,IAAI,KAAK,SAAS,GAAG;GACnB,MAAM,SAAS,KAAK,KAAI,eAAc,WAAW,CAAC;GAClD,MAAM,eAAe,IAAI,aAAa;GACtC,MAAM,oBAAoB;IACxB,IAAI,OAAO,OAAM,UAAS,UAAU,KAAA,CAAS,GAC3C,KAAK;GAET;GAEA,KAAK,SAAS,YAAY,UAAU;IAClC,aAAa,IAAI,WAAW,WAAW,WAAW,UAAU;KAC1D,OAAO,SAAS;KAChB,YAAY;IACd,CAAC,CAAC;GACJ,CAAC;GAED,YAAY,eAAe;GAC3B,YAAY;GACZ;EACF;EAEA,KAAK;CACP;CAEA,aAAa,IAAqB;EAChC,MAAM,cAAc,KAAK,IAAI,EAAE;EAC/B,IAAI,CAAC,aAAa,OAAO;EACzB,OAAO,YAAY,QAAQ;CAC7B;;;;;;;;;CAUA,2BAAmC,IAAY,MAAW,cAAwB,aAA0B,QAAiB;EAE3H,IAAI,YAAY,cAAc;GAC5B,YAAY,aAAa,YAAY;GACrC,YAAY,eAAe,KAAA;EAC7B;EAGA,MAAM,OAAO,aAAa,SAAS,IAC/B,eACC,YAAY,gBAAgB,CAAC;EAElC,IAAI,KAAK,SAAS,GAAG;GACnB,MAAM,SAAS,KAAK,KAAI,eAAc,WAAW,CAAC;GAClD,MAAM,eAAe,IAAI,aAAa;GACtC,MAAM,oBAAoB;IACxB,IAAI,OAAO,OAAM,UAAS,UAAU,KAAA,CAAS,GAAG;KAC9C,YAAY,SAAS;KACrB,YAAY,KAAK,IAAI,IAAI;KACzB,YAAY,QAAQ,IAAI,IAAI;KAC5B,KAAK,cAAc,IAAI,MAAM,IAAI;IACnC;GACF;GAEA,KAAK,SAAS,YAAY,UAAU;IAClC,aAAa,IAAI,WAAW,WAAW,WAAW,UAAU;KAC1D,OAAO,SAAS;KAChB,YAAY;IACd,CAAC,CAAC;GACJ,CAAC;GAED,YAAY,eAAe;GAC3B,YAAY;GACZ;EACF;EAGA,YAAY,SAAS;EACrB,YAAY,KAAK,IAAI,IAAI;EACzB,YAAY,QAAQ,IAAI,IAAI;EAC5B,KAAK,cAAc,IAAI,MAAM,IAAI;CACnC;;;;;;;;;;;;;;CAeA,KAAK,IAAY;EACf,IAAI,CAAC,KAAK,OAAO,EAAE,GACjB,MAAM,WAAW,EAAE;EAGrB,MAAM,cAAc,KAAK,IAAI,EAAE;EAG/B,IAAI,YAAY,cAAc;GAC5B,YAAY,aAAa,YAAY;GACrC,YAAY,eAAe,KAAA;EAC7B;EAEA,YAAY,QAAQ,IAAI,KAAK;EAC7B,YAAY,SAAS,KAAA;EAIrB,IADuB,KAAK,UAAU,MAAK,QAAO,IAAI,SAAS,EAC3D,GACF,KAAK,cAAc,IAAI,KAAK;CAEhC;CAEA,eAAuB,IAAY;EACjC,OAAO,KAAK,UAAU,MAAK,QAAO,IAAI,SAAS,EAAE;CACnD;CAEA,uBAA+B,KAAkB;EAC/C,OAAO,OAAO,IAAI,cAAc;CAClC;CAEA,gBAAwB,OAAe;EACrC,MAAM,UAAU,KAAK,IAAI;EACzB,IAAI,EAAE,SAAS,UAAU;EACzB,MAAM,OAAO,EAAE,GAAG,QAAQ;EAC1B,OAAO,KAAK;EACZ,KAAK,IAAI,IAAI,IAAI;CACnB;CAEA,aAAqB,OAAe;EAClC,MAAM,UAAU,KAAK,UAAU,QAAO,aAAY,SAAS,SAAS,KAAK;EACzE,QAAQ,SAAQ,QAAO,IAAI,cAAc,YAAY,CAAC;EACtD,IAAI,QAAQ,SAAS,GACnB,KAAK,YAAY,KAAK,UAAU,QAAO,aAAY,SAAS,SAAS,KAAK;CAE9E;CAEA,iBAAyB,KAAuB;EAC9C,OAAO,KAAK,aAAa;CAC3B;CAEA,aAAqB,KAAuB,WAAgB;EAC1D,OAAO,KAAK,QAAQ,KAAK,MAAM,WAAW,QAAQ,WAAW;CAC/D;CAEA,sBAA8B,KAAuB,WAAgB;EACnE,OAAO,CAAC,EAAE,KAAK,kBAAkB,KAAK,qBAAqB,WAAW,kBAAkB,WAAW;CACrG;CAEA,WAAmB,KAAkB,UAAU,IAAI,QAAQ,GAAG,OAAO,IAAI,KAAK,GAAa;EACzF,OAAO;GACL,MAAM,IAAI;GACV,WAAW,IAAI;GACf;GACA;GACA,QAAQ,IAAI;GACZ,gBAAgB,IAAI,kBAAkB;EACxC;CACF;CAEA,oBAA4B,OAAe;EACzC,KAAK,eAAe,OAAO,KAAK;CAClC;CAEA,cAAsB,OAAe,MAAW,SAAsB;EACpE,MAAM,WAAW,KAAK,mBAAmB,IAAI,KAAK;EAClD,IAAI,CAAC,YAAY,SAAS,WAAW,GAAG,OAAO;EAC/C,IAAI,OAAO;EACX,KAAK,MAAM,UAAU,SACnB,KAAK,MAAM,WAAW,UAAU;GAC9B,MAAM,UAAU,QAAQ,MAAM,MAAM;GACpC,IAAI,YAAY,KAAA,KAAa,YAAY,QAAQ,YAAY,MAC3D,OAAO;EAEX;EAEF,OAAO;CACT;CAEA,sBAA8B,QAAmB;EAC/C,MAAM,cAAc,KAAK,IAAI,OAAO,KAAK;EACzC,IAAI,CAAC,aAAa;EAClB,MAAM,WAAW,KAAK,mBAAmB,IAAI,OAAO,KAAK;EACzD,IAAI,CAAC,YAAY,SAAS,WAAW,GAAG;EACxC,MAAM,cAAc,YAAY,KAAK;EACrC,MAAM,WAAW,KAAK,cAAc,OAAO,OAAO,aAAa,CAAC,MAAM,CAAC;EACvE,IAAI,aAAa,aAAa;EAC9B,YAAY,KAAK,IAAI,QAAQ;EAC7B,MAAM,UAAU,KAAK,eAAe,IAAI,OAAO,KAAK,KAAK,CAAC;EAC1D,QAAQ,KAAK,MAAM;EACnB,KAAK,eAAe,IAAI,OAAO,OAAO,OAAO;EAC7C,IAAI,KAAK,eAAe,OAAO,KAAK,GAClC,KAAK,cAAc,OAAO,OAAO,YAAY,QAAQ,GAAG,QAAQ;CAEpE;CAEA,kBAA0B,OAAe,MAAW,gBAAyB;EAC3E,MAAM,cAAc,KAAK,IAAI,KAAK;EAClC,IAAI,CAAC,aAAa;EAClB,IAAI,UAAU,KAAK,eAAe,IAAI,KAAK,KAAK,CAAC;EACjD,IAAI,gBACF,UAAU,QAAQ,QAAO,WAAU,OAAO,mBAAmB,cAAc;OAE3E,UAAU,CAAC;EAEb,IAAI,WAAW;EACf,IAAI,QAAQ,QACV,WAAW,KAAK,cAAc,OAAO,UAAU,OAAO;EAExD,YAAY,KAAK,IAAI,QAAQ;EAC7B,KAAK,eAAe,IAAI,OAAO,OAAO;EACtC,IAAI,KAAK,eAAe,KAAK,GAC3B,KAAK,cAAc,OAAO,YAAY,QAAQ,GAAG,QAAQ;CAE7D;AACF"}
1
+ {"version":3,"file":"Gui.js","names":[],"sources":["../../src/Gui/Gui.ts"],"sourcesContent":["import { Context, inject } from \"@signe/di\";\nimport { signal, Signal, WritableSignal } from \"canvasengine\";\nimport { AbstractWebsocket, WebSocketToken } from \"../services/AbstractSocket\";\nimport { DialogboxComponent, ShopComponent, SaveLoadComponent, MainMenuComponent, NotificationComponent, TitleScreenComponent, GameoverComponent, InputComponent } from \"../components/gui\";\nimport { combineLatest, Subscription } from \"rxjs\";\nimport { PrebuiltGui, type RpgContext } from \"@rpgjs/common\";\n\ninterface GuiOptions {\n name?: string;\n id?: string;\n component?: any;\n display?: boolean;\n data?: any;\n /**\n * Auto display the GUI when added to the system\n * @default false\n */\n autoDisplay?: boolean;\n /**\n * Function that returns an array of Signal dependencies\n * The GUI will only display when all dependencies are resolved (!= undefined)\n * @returns Array of Signal dependencies\n */\n dependencies?: () => Signal[];\n /**\n * Attach the GUI to sprites instead of displaying globally\n * When true, the GUI will be rendered in character.ce for each sprite\n * @default false\n */\n attachToSprite?: boolean;\n /**\n * Vue v4 compatibility flag. Prefer attachToSprite in v5 projects.\n */\n rpgAttachToSprite?: boolean;\n}\n\nexport interface GuiInstance {\n name: string;\n component: any;\n display: WritableSignal<boolean>;\n data: WritableSignal<any>;\n openId?: string;\n autoDisplay: boolean;\n dependencies?: Signal[];\n subscription?: Subscription;\n attachToSprite?: boolean;\n}\n\ntype GuiState = {\n name: string;\n component: any;\n display: boolean;\n data: any;\n openId?: string;\n attachToSprite: boolean;\n};\n\ntype VueGuiBridge = {\n updateGuiState?: (state: GuiState) => void;\n initializeGuiStates?: (states: GuiState[]) => void;\n};\n\ninterface GuiAction {\n guiId: string;\n name: string;\n data: any;\n clientActionId: string;\n}\n\ntype OptimisticReducer = (data: any, action: GuiAction) => any;\n\nconst throwError = (id: string) => {\n throw `The GUI named ${id} is non-existent. Please add the component in the gui property of the decorator @RpgClient`;\n};\n\nconst updateItemQuantity = (items: any[], id: string) => {\n const index = items.findIndex((item) => item?.id === id);\n if (index === -1) return items;\n const item = items[index];\n if (item?.usable === false) return items;\n if (item?.consumable === false) return items;\n const quantity = typeof item?.quantity === \"number\" ? item.quantity : 1;\n const nextQuantity = Math.max(0, quantity - 1);\n if (nextQuantity === quantity) return items;\n if (nextQuantity <= 0) {\n return items.filter((_, idx) => idx !== index);\n }\n const nextItems = items.slice();\n nextItems[index] = { ...item, quantity: nextQuantity };\n return nextItems;\n};\n\nconst updateEquippedFlag = (items: any[], id: string, equip: boolean) => {\n const index = items.findIndex((item) => item?.id === id);\n if (index === -1) return items;\n const item = items[index];\n if (item?.equipped === equip) return items;\n const nextItems = items.slice();\n nextItems[index] = { ...item, equipped: equip };\n return nextItems;\n};\n\nconst mainMenuOptimisticReducer: OptimisticReducer = (data, action) => {\n if (!data || typeof data !== \"object\") return data;\n if (action.name === \"useItem\") {\n if (!Array.isArray(data.items)) return data;\n const id = action.data?.id;\n if (!id) return data;\n const nextItems = updateItemQuantity(data.items, id);\n if (nextItems === data.items) return data;\n return { ...data, items: nextItems };\n }\n if (action.name === \"equipItem\") {\n const id = action.data?.id;\n if (!id || typeof action.data?.equip !== \"boolean\") return data;\n const equip = action.data.equip;\n let nextItems = data.items;\n let nextEquips = data.equips;\n if (Array.isArray(data.items)) {\n nextItems = updateEquippedFlag(data.items, id, equip);\n }\n if (Array.isArray(data.equips)) {\n nextEquips = updateEquippedFlag(data.equips, id, equip);\n }\n if (nextItems === data.items && nextEquips === data.equips) return data;\n return {\n ...data,\n ...(nextItems !== data.items ? { items: nextItems } : {}),\n ...(nextEquips !== data.equips ? { equips: nextEquips } : {})\n };\n }\n return data;\n};\n\nexport class RpgGui {\n private webSocket: AbstractWebsocket;\n gui = signal<Record<string, GuiInstance>>({});\n extraGuis: GuiInstance[] = [];\n private vueGuiInstance: VueGuiBridge | null = null;\n private optimisticReducers = new Map<string, OptimisticReducer[]>();\n private pendingActions = new Map<string, GuiAction[]>();\n /**\n * Signal tracking which player IDs should display attached GUIs\n * Key: player ID, Value: boolean (true = show, false = hide)\n */\n attachedGuiDisplayState = signal<Record<string, boolean>>({});\n\n constructor(private context: RpgContext) {\n this.webSocket = inject(context as Context, WebSocketToken);\n this.add({\n name: \"rpg-dialog\",\n component: DialogboxComponent,\n });\n this.add({\n name: PrebuiltGui.MainMenu,\n component: MainMenuComponent,\n });\n this.add({\n name: PrebuiltGui.Shop,\n component: ShopComponent,\n });\n this.add({\n name: PrebuiltGui.Notification,\n component: NotificationComponent,\n autoDisplay: true,\n });\n this.add({\n name: PrebuiltGui.Save,\n component: SaveLoadComponent,\n });\n this.add({\n name: PrebuiltGui.TitleScreen,\n component: TitleScreenComponent,\n });\n this.add({\n name: PrebuiltGui.Gameover,\n component: GameoverComponent,\n });\n this.add({\n name: PrebuiltGui.Input,\n component: InputComponent,\n });\n\n this.registerOptimisticReducer(PrebuiltGui.MainMenu, mainMenuOptimisticReducer);\n }\n\n async _initialize() {\n this.webSocket.on(\"gui.open\", (data: { guiId: string; data: any; guiOpenId?: string }) => {\n this.clearPendingActions(data.guiId);\n this.display(data.guiId, data.data, [], data.guiOpenId);\n });\n\n this.webSocket.on(\"gui.exit\", (payload: string | { guiId: string; guiOpenId?: string }) => {\n const guiId = typeof payload === \"string\" ? payload : payload.guiId;\n const guiOpenId = typeof payload === \"string\" ? undefined : payload.guiOpenId;\n const current = this.get(guiId);\n if (guiOpenId && current?.openId && current.openId !== guiOpenId) {\n return;\n }\n this.hide(guiId);\n });\n\n this.webSocket.on(\"gui.update\", (payload: { guiId: string; data: any; clientActionId?: string }) => {\n this.applyServerUpdate(payload.guiId, payload.data, payload.clientActionId);\n });\n\n /**\n * Listen for tooltip display state changes from server\n * This is triggered by showAttachedGui/hideAttachedGui on the server\n */\n this.webSocket.on(\"gui.tooltip\", (data: { players: string[]; display: boolean }) => {\n const currentState = { ...this.attachedGuiDisplayState() };\n data.players.forEach((playerId) => {\n currentState[playerId] = data.display;\n });\n this.attachedGuiDisplayState.set(currentState);\n });\n }\n\n /**\n * Set the VueGui instance reference for Vue component management\n * This is called by VueGui when it's initialized\n * \n * @param vueGuiInstance - The VueGui instance\n */\n _setVueGuiInstance(vueGuiInstance: any) {\n this.vueGuiInstance = vueGuiInstance;\n this._initializeVueComponents();\n }\n\n /**\n * Notify VueGui about GUI state changes\n * This synchronizes the Vue component display state\n * \n * @param guiId - The GUI component ID\n * @param display - Display state\n * @param data - Component data\n */\n private _notifyVueGui(guiId: string, display: boolean, data: any = {}) {\n const extraGui = this.extraGuis.find(gui => gui.name === guiId);\n if (!extraGui) return;\n this.vueGuiInstance?.updateGuiState?.(this.toGuiState(extraGui, display, data));\n }\n\n /**\n * Initialize Vue components in the VueGui instance\n * This should be called after VueGui is mounted\n */\n _initializeVueComponents() {\n this.vueGuiInstance?.initializeGuiStates?.(\n this.extraGuis.map(gui => this.toGuiState(gui))\n );\n }\n\n guiInteraction(guiId: string, name: string, data: any) {\n const clientActionId = globalThis.crypto?.randomUUID?.() || `${Date.now()}-${Math.random()}`;\n const actionData = { ...(data || {}), clientActionId };\n this.applyOptimisticAction({\n guiId,\n name,\n data: actionData,\n clientActionId\n });\n this.webSocket.emit(\"gui.interaction\", {\n guiId,\n name,\n data: actionData,\n });\n }\n\n guiClose(guiId: string, data?: any, guiOpenId?: unknown) {\n const normalizedOpenId =\n typeof guiOpenId === \"string\" && guiOpenId.length > 0 ? guiOpenId : undefined;\n this.webSocket.emit(\"gui.exit\", {\n guiId,\n guiOpenId: normalizedOpenId,\n data,\n });\n }\n\n /**\n * Add a GUI component to the system\n * \n * By default, only CanvasEngine components (.ce files) are accepted.\n * Vue components should be handled by the @rpgjs/vue package.\n * \n * @param gui - GUI configuration options\n * @param gui.name - Name or ID of the GUI component\n * @param gui.id - Alternative ID if name is not provided\n * @param gui.component - The component to render (must be a CanvasEngine component)\n * @param gui.display - Initial display state (default: false)\n * @param gui.data - Initial data for the component\n * @param gui.autoDisplay - Auto display when added (default: false)\n * @param gui.dependencies - Function returning Signal dependencies\n * @param gui.attachToSprite - Attach GUI to sprites instead of global display (default: false)\n * \n * @example\n * ```ts\n * gui.add({\n * name: 'inventory',\n * component: InventoryComponent, // Must be a .ce component\n * autoDisplay: true,\n * dependencies: () => [playerSignal, inventorySignal]\n * });\n * \n * // Attach to sprites\n * gui.add({\n * name: 'tooltip',\n * component: TooltipComponent,\n * attachToSprite: true\n * });\n * ```\n */\n add(gui: GuiOptions | any) {\n const component = this.resolveComponent(gui);\n const guiId = this.resolveGuiId(gui, component);\n if (!guiId) {\n throw new Error(\"GUI must have a name or id\");\n }\n const attachToSprite = this.resolveAttachToSprite(gui, component);\n const guiInstance: GuiInstance = {\n name: guiId,\n component,\n display: signal<boolean>(gui.display || false),\n data: signal<any>(gui.data || {}),\n openId: undefined,\n autoDisplay: gui.autoDisplay || false,\n dependencies: gui.dependencies ? gui.dependencies() : [],\n attachToSprite,\n };\n\n if (this.isVueComponentInstance(guiInstance)) {\n this.removeCanvasGui(guiId);\n const existingIndex = this.extraGuis.findIndex(existing => existing.name === guiId);\n if (existingIndex >= 0) {\n this.extraGuis[existingIndex].subscription?.unsubscribe();\n this.extraGuis[existingIndex] = guiInstance;\n } else {\n this.extraGuis.push(guiInstance);\n }\n\n this._initializeVueComponents();\n \n if (guiInstance.autoDisplay) {\n this.display(guiId, gui.data);\n } else {\n this._notifyVueGui(guiId, guiInstance.display(), guiInstance.data());\n }\n return;\n }\n\n this.removeVueGui(guiId);\n this.gui()[guiId] = guiInstance;\n this._initializeVueComponents();\n\n // Auto display if enabled and it's a CanvasEngine component\n if (guiInstance.autoDisplay && typeof gui.component === 'function') {\n this.display(guiId, gui.data);\n }\n }\n\n registerOptimisticReducer(guiId: string, reducer: OptimisticReducer) {\n const existing = this.optimisticReducers.get(guiId) || [];\n this.optimisticReducers.set(guiId, existing.concat(reducer));\n }\n\n /**\n * Get all attached GUI components (attachToSprite: true)\n * \n * Returns all GUI instances that are configured to be attached to sprites.\n * These GUIs should be rendered in character.ce instead of canvas.ce.\n * \n * @returns Array of GUI instances with attachToSprite: true\n * \n * @example\n * ```ts\n * const attachedGuis = gui.getAttachedGuis();\n * // Use in character.ce to render tooltips\n * ```\n */\n getAttachedGuis(): GuiInstance[] {\n return Object.values(this.gui()).filter(gui => gui.attachToSprite === true);\n }\n\n getVueGuis(): GuiInstance[] {\n return [...this.extraGuis];\n }\n\n getAttachedVueGuis(): GuiInstance[] {\n return this.extraGuis.filter(gui => gui.attachToSprite === true);\n }\n\n /**\n * Check if a player should display attached GUIs\n * \n * @param playerId - The player ID to check\n * @returns true if attached GUIs should be displayed for this player\n */\n shouldDisplayAttachedGui(playerId: string): boolean {\n return this.attachedGuiDisplayState()[playerId] === true;\n }\n\n get(id: string): GuiInstance | undefined {\n // Check CanvasEngine GUIs first\n const canvasGui = this.gui()[id];\n if (canvasGui) {\n return canvasGui;\n }\n \n // Check Vue GUIs in extraGuis\n return this.extraGuis.find(gui => gui.name === id);\n }\n\n exists(id: string): boolean {\n return !!this.get(id);\n }\n\n getAll(): Record<string, GuiInstance> {\n const allGuis = { ...this.gui() };\n \n // Add extraGuis to the result\n this.extraGuis.forEach(gui => {\n allGuis[gui.name] = gui;\n });\n \n return allGuis;\n }\n\n /**\n * Display a GUI component\n * \n * Displays the GUI immediately if no dependencies are configured,\n * or waits for all dependencies to be resolved if dependencies are present.\n * Automatically manages subscriptions to prevent memory leaks.\n * Works with both CanvasEngine components and Vue components.\n * \n * @param id - The GUI component ID\n * @param data - Data to pass to the component\n * @param dependencies - Optional runtime dependencies (overrides config dependencies)\n * \n * @example\n * ```ts\n * // Display immediately\n * gui.display('inventory', { items: [] });\n * \n * // Display with runtime dependencies\n * gui.display('shop', { shopId: 1 }, [playerSignal, shopSignal]);\n * ```\n */\n display(id: string, data = {}, dependencies: Signal[] = [], openId?: string) {\n if (!this.exists(id)) {\n throw throwError(id);\n }\n\n const guiInstance = this.get(id)!;\n const isVueComponent = this.extraGuis.some(gui => gui.name === id);\n\n if (guiInstance.subscription) {\n guiInstance.subscription.unsubscribe();\n guiInstance.subscription = undefined;\n }\n\n const show = () => {\n guiInstance.openId = openId;\n guiInstance.data.set(data);\n guiInstance.display.set(true);\n if (isVueComponent) {\n this._notifyVueGui(id, true, data);\n }\n };\n\n const deps = dependencies.length > 0\n ? dependencies\n : (guiInstance.dependencies ?? []);\n\n if (deps.length > 0) {\n const values = deps.map(dependency => dependency());\n const subscription = new Subscription();\n const showIfReady = () => {\n if (values.every(value => value !== undefined)) {\n show();\n }\n };\n\n deps.forEach((dependency, index) => {\n subscription.add(dependency.observable.subscribe((value) => {\n values[index] = value;\n showIfReady();\n }));\n });\n\n guiInstance.subscription = subscription;\n showIfReady();\n return;\n }\n\n show();\n }\n\n isDisplaying(id: string): boolean {\n const guiInstance = this.get(id);\n if (!guiInstance) return false;\n return guiInstance.display();\n }\n\n /**\n * Handle Vue component display logic\n * \n * @param id - GUI component ID\n * @param data - Component data\n * @param dependencies - Runtime dependencies\n * @param guiInstance - GUI instance\n */\n private _handleVueComponentDisplay(id: string, data: any, dependencies: Signal[], guiInstance: GuiInstance, openId?: string) {\n // Unsubscribe from previous subscription if exists\n if (guiInstance.subscription) {\n guiInstance.subscription.unsubscribe();\n guiInstance.subscription = undefined;\n }\n\n // Use runtime dependencies or config dependencies\n const deps = dependencies.length > 0 \n ? dependencies \n : (guiInstance.dependencies ?? []);\n\n if (deps.length > 0) {\n const values = deps.map(dependency => dependency());\n const subscription = new Subscription();\n const showIfReady = () => {\n if (values.every(value => value !== undefined)) {\n guiInstance.openId = openId;\n guiInstance.data.set(data);\n guiInstance.display.set(true);\n this._notifyVueGui(id, true, data);\n }\n };\n\n deps.forEach((dependency, index) => {\n subscription.add(dependency.observable.subscribe((value) => {\n values[index] = value;\n showIfReady();\n }));\n });\n\n guiInstance.subscription = subscription;\n showIfReady();\n return;\n }\n\n // No dependencies, display immediately\n guiInstance.openId = openId;\n guiInstance.data.set(data);\n guiInstance.display.set(true);\n this._notifyVueGui(id, true, data);\n }\n\n /**\n * Hide a GUI component\n * \n * Hides the GUI and cleans up any active subscriptions.\n * Works with both CanvasEngine components and Vue components.\n * \n * @param id - The GUI component ID\n * \n * @example\n * ```ts\n * gui.hide('inventory');\n * ```\n */\n hide(id: string) {\n if (!this.exists(id)) {\n throw throwError(id);\n }\n\n const guiInstance = this.get(id)!;\n \n // Unsubscribe if there's an active subscription\n if (guiInstance.subscription) {\n guiInstance.subscription.unsubscribe();\n guiInstance.subscription = undefined;\n }\n\n guiInstance.display.set(false)\n guiInstance.openId = undefined;\n \n // Check if it's a Vue component and notify VueGui\n const isVueComponent = this.extraGuis.some(gui => gui.name === id);\n if (isVueComponent) {\n this._notifyVueGui(id, false);\n }\n }\n\n private isVueComponent(id: string) {\n return this.extraGuis.some(gui => gui.name === id);\n }\n\n private isVueComponentInstance(gui: GuiInstance) {\n return typeof gui.component !== \"function\";\n }\n\n private removeCanvasGui(guiId: string) {\n const current = this.gui();\n if (!(guiId in current)) return;\n const next = { ...current };\n delete next[guiId];\n this.gui.set(next);\n }\n\n private removeVueGui(guiId: string) {\n const removed = this.extraGuis.filter(existing => existing.name === guiId);\n removed.forEach(gui => gui.subscription?.unsubscribe());\n if (removed.length > 0) {\n this.extraGuis = this.extraGuis.filter(existing => existing.name !== guiId);\n }\n }\n\n private resolveComponent(gui: GuiOptions | any) {\n return gui?.component ?? gui;\n }\n\n private resolveGuiId(gui: GuiOptions | any, component: any) {\n return gui?.name || gui?.id || component?.name || component?.__name;\n }\n\n private resolveAttachToSprite(gui: GuiOptions | any, component: any) {\n return !!(gui?.attachToSprite || gui?.rpgAttachToSprite || component?.attachToSprite || component?.rpgAttachToSprite);\n }\n\n private toGuiState(gui: GuiInstance, display = gui.display(), data = gui.data()): GuiState {\n return {\n name: gui.name,\n component: gui.component,\n display,\n data,\n openId: gui.openId,\n attachToSprite: gui.attachToSprite || false,\n };\n }\n\n private clearPendingActions(guiId: string) {\n this.pendingActions.delete(guiId);\n }\n\n private applyReducers(guiId: string, data: any, actions: GuiAction[]) {\n const reducers = this.optimisticReducers.get(guiId);\n if (!reducers || reducers.length === 0) return data;\n let next = data;\n for (const action of actions) {\n for (const reducer of reducers) {\n const updated = reducer(next, action);\n if (updated !== undefined && updated !== null && updated !== next) {\n next = updated;\n }\n }\n }\n return next;\n }\n\n private applyOptimisticAction(action: GuiAction) {\n const guiInstance = this.get(action.guiId);\n if (!guiInstance) return;\n const reducers = this.optimisticReducers.get(action.guiId);\n if (!reducers || reducers.length === 0) return;\n const currentData = guiInstance.data();\n const nextData = this.applyReducers(action.guiId, currentData, [action]);\n if (nextData === currentData) return;\n guiInstance.data.set(nextData);\n const pending = this.pendingActions.get(action.guiId) || [];\n pending.push(action);\n this.pendingActions.set(action.guiId, pending);\n if (this.isVueComponent(action.guiId)) {\n this._notifyVueGui(action.guiId, guiInstance.display(), nextData);\n }\n }\n\n private applyServerUpdate(guiId: string, data: any, clientActionId?: string) {\n const guiInstance = this.get(guiId);\n if (!guiInstance) return;\n let pending = this.pendingActions.get(guiId) || [];\n if (clientActionId) {\n pending = pending.filter(action => action.clientActionId !== clientActionId);\n } else {\n pending = [];\n }\n let nextData = data;\n if (pending.length) {\n nextData = this.applyReducers(guiId, nextData, pending);\n }\n guiInstance.data.set(nextData);\n this.pendingActions.set(guiId, pending);\n if (this.isVueComponent(guiId)) {\n this._notifyVueGui(guiId, guiInstance.display(), nextData);\n }\n }\n}\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
- import { Context } from '@signe/di';
2
- export declare let context: Context | null;
3
- export declare function inject<T>(service: (new (...args: any[]) => T) | string, _context?: Context): T;
4
- export declare function setInject(_context: Context): void;
1
+ import { RpgContext } from '@rpgjs/common';
2
+ export declare let context: RpgContext | null;
3
+ export declare function inject<T>(service: (new (...args: any[]) => T) | string, _context?: RpgContext): T;
4
+ export declare function setInject(_context: RpgContext): void;
5
5
  export declare function clearInject(): void;
@@ -1 +1 @@
1
- {"version":3,"file":"inject.js","names":[],"sources":["../../src/core/inject.ts"],"sourcesContent":["import { Context, inject as injector } from \"@signe/di\";\n\nexport let context: Context | null = null\n\nexport function inject<T>(service: (new (...args: any[]) => T) | string, _context?: Context): T {\n const c = _context ?? context\n if (!c) throw new Error(\"Context is not set. use setInject() to set the context\");\n return injector(c, service);\n}\n\nexport function setInject(_context: Context) {\n context = _context;\n}\n\nexport function clearInject() {\n context = null\n}"],"mappings":";;AAEA,IAAW,UAA0B;AAErC,SAAgB,OAAU,SAA+C,UAAuB;CAC5F,MAAM,IAAI,YAAY;CACtB,IAAI,CAAC,GAAG,MAAM,IAAI,MAAM,wDAAwD;CAChF,OAAO,SAAS,GAAG,OAAO;AAC9B;AAEA,SAAgB,UAAU,UAAmB;CACzC,UAAU;AACd;AAEA,SAAgB,cAAc;CAC1B,UAAU;AACd"}
1
+ {"version":3,"file":"inject.js","names":[],"sources":["../../src/core/inject.ts"],"sourcesContent":["import { Context, inject as injector } from \"@signe/di\";\nimport type { RpgContext } from \"@rpgjs/common\";\n\nexport let context: RpgContext | null = null\n\nexport function inject<T>(service: (new (...args: any[]) => T) | string, _context?: RpgContext): T {\n const c = _context ?? context\n if (!c) throw new Error(\"Context is not set. use setInject() to set the context\");\n return injector(c as Context, service);\n}\n\nexport function setInject(_context: RpgContext) {\n context = _context;\n}\n\nexport function clearInject() {\n context = null\n}\n"],"mappings":";;AAGA,IAAW,UAA6B;AAExC,SAAgB,OAAU,SAA+C,UAA0B;CAC/F,MAAM,IAAI,YAAY;CACtB,IAAI,CAAC,GAAG,MAAM,IAAI,MAAM,wDAAwD;CAChF,OAAO,SAAS,GAAc,OAAO;AACzC;AAEA,SAAgB,UAAU,UAAsB;CAC5C,UAAU;AACd;AAEA,SAAgB,cAAc;CAC1B,UAAU;AACd"}
@@ -1,6 +1,6 @@
1
- import { Context, Providers } from '@signe/di';
1
+ import { RpgContext, RpgProviders } from '@rpgjs/common';
2
2
  interface SetupOptions {
3
- providers: Providers;
3
+ providers: RpgProviders;
4
4
  }
5
- export declare function startGame(options: SetupOptions): Promise<Context<any, any, Record<string, any>>>;
5
+ export declare function startGame(options: SetupOptions): Promise<RpgContext>;
6
6
  export {};
@@ -6,7 +6,7 @@ async function startGame(options) {
6
6
  const context = new Context();
7
7
  context["side"] = "client";
8
8
  setInject(context);
9
- await injector(context, options.providers);
9
+ await injector(context, options.providers.flat(Infinity));
10
10
  await inject(context, RpgClientEngine).start();
11
11
  return context;
12
12
  }
@@ -1 +1 @@
1
- {"version":3,"file":"setup.js","names":[],"sources":["../../src/core/setup.ts"],"sourcesContent":["import { Context, FactoryProvider, findProvider, findProviders, inject, injector, Providers } from \"@signe/di\";\nimport { RpgClientEngine } from \"../RpgClientEngine\";\nimport { setInject } from \"./inject\";\n\ninterface SetupOptions {\n providers: Providers;\n}\n\n\nexport async function startGame(options: SetupOptions) {\n const context = new Context();\n context['side'] = 'client'\n setInject(context);\n\n await injector(context, options.providers);\n\n const engine = inject<RpgClientEngine>(context, RpgClientEngine);\n await engine.start();\n return context;\n}\n"],"mappings":";;;;AASA,eAAsB,UAAU,SAAuB;CACrD,MAAM,UAAU,IAAI,QAAQ;CAC5B,QAAQ,UAAU;CAClB,UAAU,OAAO;CAEjB,MAAM,SAAS,SAAS,QAAQ,SAAS;CAGzC,MADe,OAAwB,SAAS,eAC1C,EAAO,MAAM;CACnB,OAAO;AACT"}
1
+ {"version":3,"file":"setup.js","names":[],"sources":["../../src/core/setup.ts"],"sourcesContent":["import { Context, inject, injector, type Providers } from \"@signe/di\";\nimport type { RpgContext, RpgProviders } from \"@rpgjs/common\";\nimport { RpgClientEngine } from \"../RpgClientEngine\";\nimport { setInject } from \"./inject\";\n\ninterface SetupOptions {\n providers: RpgProviders;\n}\n\n\nexport async function startGame(options: SetupOptions): Promise<RpgContext> {\n const context = new Context();\n context['side'] = 'client'\n setInject(context as unknown as RpgContext);\n\n const providers = (options.providers as unknown[]).flat(Infinity) as Providers;\n await injector(context, providers);\n\n const engine = inject<RpgClientEngine>(context, RpgClientEngine);\n await engine.start();\n return context as unknown as RpgContext;\n}\n"],"mappings":";;;;AAUA,eAAsB,UAAU,SAA4C;CAC1E,MAAM,UAAU,IAAI,QAAQ;CAC5B,QAAQ,UAAU;CAClB,UAAU,OAAgC;CAG1C,MAAM,SAAS,SADI,QAAQ,UAAwB,KAAK,QAChC,CAAS;CAGjC,MADe,OAAwB,SAAS,eAC1C,EAAO,MAAM;CACnB,OAAO;AACT"}
@@ -0,0 +1 @@
1
+ export {};
package/dist/i18n.d.ts CHANGED
@@ -64,4 +64,4 @@ export declare const RpgClientBuiltinI18n: {
64
64
  "rpg.shop.total": string;
65
65
  };
66
66
  };
67
- export declare function provideI18n(config?: I18nConfig): import('@signe/di').FactoryProvider;
67
+ export declare function provideI18n(config?: I18nConfig): import('@rpgjs/common').RpgFactoryProvider<import('@rpgjs/common').I18nService>;
package/dist/index.d.ts CHANGED
@@ -22,10 +22,10 @@ export * from './Sound';
22
22
  export * from './Resource';
23
23
  export * from './decorators/spritesheet';
24
24
  export * from './utils/getEntityProp';
25
- export { Context } from '@signe/di';
26
25
  export { KeyboardControls, Input } from 'canvasengine';
27
26
  export { Control } from './services/keyboardControls';
28
27
  export { defineModule } from '@rpgjs/common';
28
+ export type { RpgClassProvider, RpgContext, RpgExistingProvider, RpgFactoryProvider, RpgProvider, RpgProviders, RpgProviderToken, RpgReadableSignal, RpgValueProvider, RpgWritableSignal, } from '@rpgjs/common';
29
29
  export { RpgClientObject } from './Game/Object';
30
30
  export { RpgClientPlayer } from './Game/Player';
31
31
  export { RpgClientEvent } from './Game/Event';
package/dist/index.js CHANGED
@@ -1,4 +1,3 @@
1
- import { Context } from "./node_modules/.pnpm/@signe_di@3.1.0/node_modules/@signe/di/dist/index.js";
2
1
  import { clearInject, context, inject, setInject } from "./core/inject.js";
3
2
  import { AbstractWebsocket, WebSocketToken } from "./services/AbstractSocket.js";
4
3
  import { getKeyboardControlBind, isKeyboardActionConfig, keyboardEventMatchesBind, normalizeActionInput, resolveKeyboardActionInput, resolveKeyboardDirectionInput } from "./services/actionInput.js";
@@ -53,4 +52,4 @@ import { Spritesheet } from "./decorators/spritesheet.js";
53
52
  import { withMobile } from "./components/gui/mobile/index.js";
54
53
  import { Input, KeyboardControls } from "canvasengine";
55
54
  import { defineModule } from "@rpgjs/common";
56
- export { AbstractWebsocket, __ce_component as BoxComponent, BridgeWebsocket, __ce_component$1 as CharacterComponent, ClientVisualRegistry, Context, Control, __ce_component$2 as DialogboxComponent, __ce_component$3 as EquipMenuComponent, __ce_component$4 as EventLayerComponent, __ce_component$5 as ExitMenuComponent, __ce_component$6 as GameoverComponent, GlobalConfigToken, __ce_component$7 as HpBar, __ce_component$8 as HudComponent, Input, __ce_component$9 as InputComponent, __ce_component$10 as InputFieldComponent, __ce_component$11 as ItemsMenuComponent, KeyboardControls, __ce_component$12 as LightHalo, LoadMapService, LoadMapToken, __ce_component$13 as MainMenuComponent, MapStreamClientController, __ce_component$14 as NotificationComponent, __ce_component$15 as OptionsMenuComponent, PrebuiltComponentAnimations, Presets, ProjectileManager, RpgClientBuiltinI18n, RpgClientEngine, RpgClientEvent, RpgClientInteractions, RpgClientObject, RpgClientPlayer, RpgGui, RpgResource, RpgSound, SaveClientService, SaveClientToken, __ce_component$16 as SaveLoadComponent, __ce_component$17 as SceneMap, __ce_component$18 as ShopComponent, __ce_component$19 as SkillsMenuComponent, Sound, Spritesheet, __ce_component$20 as TitleScreenComponent, WebSocketToken, clearInject, context, createClientPointerContext, defineModule, dragToTile, draggable, getEntityProp, getKeyboardControlBind, getSoundMetadata, hoverPopover, inject, isKeyboardActionConfig, keyboardEventMatchesBind, normalizeActionInput, provideClientGlobalConfig, provideClientMapStreaming, provideClientModules, provideGlobalConfig, provideI18n, provideLoadMap, provideMmorpg, provideRpg, provideSaveClient, resolveKeyboardActionInput, resolveKeyboardDirectionInput, selectable, setInject, startGame, withMobile };
55
+ export { AbstractWebsocket, __ce_component as BoxComponent, BridgeWebsocket, __ce_component$1 as CharacterComponent, ClientVisualRegistry, Control, __ce_component$2 as DialogboxComponent, __ce_component$3 as EquipMenuComponent, __ce_component$4 as EventLayerComponent, __ce_component$5 as ExitMenuComponent, __ce_component$6 as GameoverComponent, GlobalConfigToken, __ce_component$7 as HpBar, __ce_component$8 as HudComponent, Input, __ce_component$9 as InputComponent, __ce_component$10 as InputFieldComponent, __ce_component$11 as ItemsMenuComponent, KeyboardControls, __ce_component$12 as LightHalo, LoadMapService, LoadMapToken, __ce_component$13 as MainMenuComponent, MapStreamClientController, __ce_component$14 as NotificationComponent, __ce_component$15 as OptionsMenuComponent, PrebuiltComponentAnimations, Presets, ProjectileManager, RpgClientBuiltinI18n, RpgClientEngine, RpgClientEvent, RpgClientInteractions, RpgClientObject, RpgClientPlayer, RpgGui, RpgResource, RpgSound, SaveClientService, SaveClientToken, __ce_component$16 as SaveLoadComponent, __ce_component$17 as SceneMap, __ce_component$18 as ShopComponent, __ce_component$19 as SkillsMenuComponent, Sound, Spritesheet, __ce_component$20 as TitleScreenComponent, WebSocketToken, clearInject, context, createClientPointerContext, defineModule, dragToTile, draggable, getEntityProp, getKeyboardControlBind, getSoundMetadata, hoverPopover, inject, isKeyboardActionConfig, keyboardEventMatchesBind, normalizeActionInput, provideClientGlobalConfig, provideClientMapStreaming, provideClientModules, provideGlobalConfig, provideI18n, provideLoadMap, provideMmorpg, provideRpg, provideSaveClient, resolveKeyboardActionInput, resolveKeyboardDirectionInput, selectable, setInject, startGame, withMobile };
package/dist/module.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { FactoryProvider } from '@signe/di';
1
+ import { RpgFactoryProvider } from '@rpgjs/common';
2
2
  import { RpgClient } from './RpgClient';
3
3
  /**
4
4
  * Type for client modules that can be either:
@@ -41,7 +41,7 @@ export type RpgClientModule = RpgClient | (new () => any);
41
41
  * provideClientModules([MyClientModule])
42
42
  * ```
43
43
  */
44
- export declare function provideClientModules(modules: RpgClientModule[]): FactoryProvider;
44
+ export declare function provideClientModules(modules: RpgClientModule[]): RpgFactoryProvider;
45
45
  export declare const GlobalConfigToken = "GlobalConfigToken";
46
46
  export declare function provideGlobalConfig(config: any): {
47
47
  provide: string;
@@ -1 +1 @@
1
- {"version":3,"file":"module.js","names":[],"sources":["../src/module.ts"],"sourcesContent":["import { findModules, provideModules, registerI18nMessages } from \"@rpgjs/common\";\nimport { FactoryProvider } from \"@signe/di\";\nimport { RpgClientEngine } from \"./RpgClientEngine\";\nimport { RpgClient } from \"./RpgClient\";\nimport { inject } from \"@signe/di\";\nimport { RpgGui } from \"./Gui/Gui\";\nimport { getSoundMetadata } from \"./Sound\";\n\n/**\n * Type for client modules that can be either:\n * - An object implementing RpgClient interface\n * - A class decorated with @RpgModule decorator\n */\nexport type RpgClientModule = RpgClient | (new () => any);\n\n/**\n * Provides client modules configuration to Dependency Injection\n * \n * This function accepts an array of client modules that can be either:\n * - Objects implementing the RpgClient interface\n * - Classes decorated with the @RpgModule decorator (which will be instantiated)\n * \n * @param modules - Array of client modules (objects or classes)\n * @returns FactoryProvider configuration for DI\n * @example\n * ```ts\n * // Using an object\n * provideClientModules([\n * {\n * engine: {\n * onConnected(engine) {\n * console.log('Client connected')\n * }\n * }\n * }\n * ])\n * \n * // Using a decorated class\n * @RpgModule<RpgClient>({\n * engine: {\n * onStart(engine) {\n * console.log('Client started')\n * }\n * }\n * })\n * class MyClientModule {}\n * \n * provideClientModules([MyClientModule])\n * ```\n */\nexport function provideClientModules(modules: RpgClientModule[]): FactoryProvider {\n return provideModules(modules, \"client\", (modules, context) => {\n const mainModuleClient = findModules(context, 'Client')\n modules = [...mainModuleClient, ...modules]\n modules = modules.map((module) => {\n // If module is a class (constructor function), instantiate it\n // The RpgModule decorator adds properties to the prototype, which will be accessible via the instance\n if (typeof module === 'function') {\n const instance = new module() as any;\n // Copy all enumerable properties (including from prototype) to a plain object\n const moduleObj: any = {};\n for (const key in instance) {\n moduleObj[key] = instance[key];\n }\n module = moduleObj;\n }\n if ('client' in module) {\n module = module.client as any;\n }\n if (module.i18n) {\n registerI18nMessages(context, module.i18n, \"client-module\", 10);\n }\n if (module.spritesheets) {\n const spritesheets = [...module.spritesheets];\n module.spritesheets = {\n load: (engine: RpgClientEngine) => {\n spritesheets.forEach((spritesheet) => {\n engine.addSpriteSheet(spritesheet);\n });\n },\n };\n }\n if (module.spritesheetResolver) {\n const resolver = module.spritesheetResolver;\n module.spritesheetResolver = {\n load: (engine: RpgClientEngine) => {\n engine.setSpritesheetResolver(resolver);\n },\n };\n }\n if (module.sounds) {\n const sounds = [...module.sounds];\n module.sounds = {\n load: (engine: RpgClientEngine) => {\n sounds.forEach((sound) => {\n // Check if it's a class decorated with @Sound\n if (typeof sound === 'function' || (sound && sound.constructor && sound.constructor !== Object)) {\n const metadata = getSoundMetadata(sound);\n if (metadata) {\n // Handle single sound\n if (metadata.id && metadata.sound) {\n engine.addSound({\n id: metadata.id,\n src: metadata.sound,\n loop: metadata.loop,\n volume: metadata.volume,\n });\n }\n // Handle multiple sounds\n if (metadata.sounds) {\n Object.entries(metadata.sounds).forEach(([soundId, soundSrc]) => {\n engine.addSound({\n id: soundId,\n src: soundSrc,\n loop: metadata.loop,\n volume: metadata.volume,\n });\n });\n }\n } else {\n // Not a decorated class, treat as regular sound object\n engine.addSound(sound);\n }\n } else {\n // Regular sound object\n engine.addSound(sound);\n }\n });\n },\n };\n }\n if (module.soundResolver) {\n const resolver = module.soundResolver;\n module.soundResolver = {\n load: (engine: RpgClientEngine) => {\n engine.setSoundResolver(resolver);\n },\n };\n }\n if (module.gui) {\n const gui = [...module.gui];\n module.gui = {\n load: (engine: RpgClientEngine) => {\n const guiService = inject(engine.context, RpgGui) as RpgGui;\n gui.forEach((gui) => {\n guiService.add(gui);\n });\n },\n };\n }\n if (module.componentAnimations) {\n const componentAnimations = [...module.componentAnimations];\n module.componentAnimations = {\n load: (engine: RpgClientEngine) => {\n componentAnimations.forEach((componentAnimation) => {\n engine.addComponentAnimation(componentAnimation);\n });\n },\n };\n }\n if (module.clientVisuals) {\n const clientVisuals = { ...module.clientVisuals };\n module.clientVisuals = {\n load: (engine: RpgClientEngine) => {\n engine.registerClientVisuals(clientVisuals);\n },\n };\n }\n if (module.projectiles) {\n const projectiles = { ...module.projectiles };\n module.projectiles = {\n ...projectiles,\n load: (engine: RpgClientEngine) => {\n if (projectiles.components) {\n Object.entries(projectiles.components).forEach(([type, component]) => {\n engine.registerProjectileComponent(type, component);\n });\n }\n },\n };\n }\n if (module.interactions) {\n const interactions = module.interactions;\n module.interactions = {\n ...interactions,\n load: (engine: RpgClientEngine) => {\n if (typeof interactions === \"function\") {\n interactions(engine);\n return;\n }\n interactions.load?.(engine);\n interactions.setup?.(engine);\n if (Array.isArray(interactions.use)) {\n interactions.use.forEach(([matcher, behavior]) => {\n engine.interactions.use(matcher, behavior);\n });\n }\n },\n };\n }\n if (module.transitions) {\n const transitions = [...module.transitions];\n module.transitions = {\n load: (engine: RpgClientEngine) => {\n const guiService = inject(engine.context, RpgGui) as RpgGui;\n transitions.forEach((transition) => {\n guiService.add({\n name: transition.id,\n component: transition.component,\n data: transition.props || {}\n });\n });\n },\n };\n }\n if (module.particles) {\n const particles = [...module.particles];\n module.particles = {\n load: (engine: RpgClientEngine) => {\n particles.forEach((particle) => {\n engine.addParticle(particle);\n });\n },\n };\n }\n if (module.sprite) {\n const sprite = {...module.sprite};\n module.sprite = {\n ...sprite,\n load: (engine: RpgClientEngine) => {\n if (sprite.componentsBehind) {\n sprite.componentsBehind.forEach((component) => {\n engine.addSpriteComponentBehind(component);\n });\n }\n if (sprite.componentsInFront) {\n sprite.componentsInFront.forEach((component) => {\n engine.addSpriteComponentInFront(component);\n });\n }\n if (sprite.components) {\n Object.entries(sprite.components).forEach(([id, component]) => {\n engine.registerSpriteComponent(id, component);\n });\n }\n if (sprite.eventComponent) {\n engine.addEventComponentResolver(sprite.eventComponent);\n }\n },\n };\n }\n return module;\n });\n return modules\n });\n}\n\nexport const GlobalConfigToken = \"GlobalConfigToken\";\n\nexport function provideGlobalConfig(config: any) {\n return {\n provide: GlobalConfigToken,\n useValue: config ?? {},\n };\n}\n\nexport function provideClientGlobalConfig(config: any = {}) {\n config.keyboardControls = {\n up: 'up',\n down: 'down',\n left: 'left',\n right: 'right',\n action: 'space',\n dash: 'shift',\n escape: 'escape',\n ...(config.keyboardControls ?? {}),\n }\n return provideGlobalConfig(config)\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"module.js","names":[],"sources":["../src/module.ts"],"sourcesContent":["import { findModules, provideModules, registerI18nMessages } from \"@rpgjs/common\";\nimport type { RpgFactoryProvider } from \"@rpgjs/common\";\nimport { RpgClientEngine } from \"./RpgClientEngine\";\nimport { RpgClient } from \"./RpgClient\";\nimport { inject } from \"@signe/di\";\nimport { RpgGui } from \"./Gui/Gui\";\nimport { getSoundMetadata } from \"./Sound\";\n\n/**\n * Type for client modules that can be either:\n * - An object implementing RpgClient interface\n * - A class decorated with @RpgModule decorator\n */\nexport type RpgClientModule = RpgClient | (new () => any);\n\n/**\n * Provides client modules configuration to Dependency Injection\n * \n * This function accepts an array of client modules that can be either:\n * - Objects implementing the RpgClient interface\n * - Classes decorated with the @RpgModule decorator (which will be instantiated)\n * \n * @param modules - Array of client modules (objects or classes)\n * @returns FactoryProvider configuration for DI\n * @example\n * ```ts\n * // Using an object\n * provideClientModules([\n * {\n * engine: {\n * onConnected(engine) {\n * console.log('Client connected')\n * }\n * }\n * }\n * ])\n * \n * // Using a decorated class\n * @RpgModule<RpgClient>({\n * engine: {\n * onStart(engine) {\n * console.log('Client started')\n * }\n * }\n * })\n * class MyClientModule {}\n * \n * provideClientModules([MyClientModule])\n * ```\n */\nexport function provideClientModules(modules: RpgClientModule[]): RpgFactoryProvider {\n return provideModules(modules, \"client\", (modules, context) => {\n const mainModuleClient = findModules(context, 'Client')\n modules = [...mainModuleClient, ...modules]\n modules = modules.map((module) => {\n // If module is a class (constructor function), instantiate it\n // The RpgModule decorator adds properties to the prototype, which will be accessible via the instance\n if (typeof module === 'function') {\n const instance = new module() as any;\n // Copy all enumerable properties (including from prototype) to a plain object\n const moduleObj: any = {};\n for (const key in instance) {\n moduleObj[key] = instance[key];\n }\n module = moduleObj;\n }\n if ('client' in module) {\n module = module.client as any;\n }\n if (module.i18n) {\n registerI18nMessages(context, module.i18n, \"client-module\", 10);\n }\n if (module.spritesheets) {\n const spritesheets = [...module.spritesheets];\n module.spritesheets = {\n load: (engine: RpgClientEngine) => {\n spritesheets.forEach((spritesheet) => {\n engine.addSpriteSheet(spritesheet);\n });\n },\n };\n }\n if (module.spritesheetResolver) {\n const resolver = module.spritesheetResolver;\n module.spritesheetResolver = {\n load: (engine: RpgClientEngine) => {\n engine.setSpritesheetResolver(resolver);\n },\n };\n }\n if (module.sounds) {\n const sounds = [...module.sounds];\n module.sounds = {\n load: (engine: RpgClientEngine) => {\n sounds.forEach((sound) => {\n // Check if it's a class decorated with @Sound\n if (typeof sound === 'function' || (sound && sound.constructor && sound.constructor !== Object)) {\n const metadata = getSoundMetadata(sound);\n if (metadata) {\n // Handle single sound\n if (metadata.id && metadata.sound) {\n engine.addSound({\n id: metadata.id,\n src: metadata.sound,\n loop: metadata.loop,\n volume: metadata.volume,\n });\n }\n // Handle multiple sounds\n if (metadata.sounds) {\n Object.entries(metadata.sounds).forEach(([soundId, soundSrc]) => {\n engine.addSound({\n id: soundId,\n src: soundSrc,\n loop: metadata.loop,\n volume: metadata.volume,\n });\n });\n }\n } else {\n // Not a decorated class, treat as regular sound object\n engine.addSound(sound);\n }\n } else {\n // Regular sound object\n engine.addSound(sound);\n }\n });\n },\n };\n }\n if (module.soundResolver) {\n const resolver = module.soundResolver;\n module.soundResolver = {\n load: (engine: RpgClientEngine) => {\n engine.setSoundResolver(resolver);\n },\n };\n }\n if (module.gui) {\n const gui = [...module.gui];\n module.gui = {\n load: (engine: RpgClientEngine) => {\n const guiService = inject(engine.context, RpgGui) as RpgGui;\n gui.forEach((gui) => {\n guiService.add(gui);\n });\n },\n };\n }\n if (module.componentAnimations) {\n const componentAnimations = [...module.componentAnimations];\n module.componentAnimations = {\n load: (engine: RpgClientEngine) => {\n componentAnimations.forEach((componentAnimation) => {\n engine.addComponentAnimation(componentAnimation);\n });\n },\n };\n }\n if (module.clientVisuals) {\n const clientVisuals = { ...module.clientVisuals };\n module.clientVisuals = {\n load: (engine: RpgClientEngine) => {\n engine.registerClientVisuals(clientVisuals);\n },\n };\n }\n if (module.projectiles) {\n const projectiles = { ...module.projectiles };\n module.projectiles = {\n ...projectiles,\n load: (engine: RpgClientEngine) => {\n if (projectiles.components) {\n Object.entries(projectiles.components).forEach(([type, component]) => {\n engine.registerProjectileComponent(type, component);\n });\n }\n },\n };\n }\n if (module.interactions) {\n const interactions = module.interactions;\n module.interactions = {\n ...interactions,\n load: (engine: RpgClientEngine) => {\n if (typeof interactions === \"function\") {\n interactions(engine);\n return;\n }\n interactions.load?.(engine);\n interactions.setup?.(engine);\n if (Array.isArray(interactions.use)) {\n interactions.use.forEach(([matcher, behavior]) => {\n engine.interactions.use(matcher, behavior);\n });\n }\n },\n };\n }\n if (module.transitions) {\n const transitions = [...module.transitions];\n module.transitions = {\n load: (engine: RpgClientEngine) => {\n const guiService = inject(engine.context, RpgGui) as RpgGui;\n transitions.forEach((transition) => {\n guiService.add({\n name: transition.id,\n component: transition.component,\n data: transition.props || {}\n });\n });\n },\n };\n }\n if (module.particles) {\n const particles = [...module.particles];\n module.particles = {\n load: (engine: RpgClientEngine) => {\n particles.forEach((particle) => {\n engine.addParticle(particle);\n });\n },\n };\n }\n if (module.sprite) {\n const sprite = {...module.sprite};\n module.sprite = {\n ...sprite,\n load: (engine: RpgClientEngine) => {\n if (sprite.componentsBehind) {\n sprite.componentsBehind.forEach((component) => {\n engine.addSpriteComponentBehind(component);\n });\n }\n if (sprite.componentsInFront) {\n sprite.componentsInFront.forEach((component) => {\n engine.addSpriteComponentInFront(component);\n });\n }\n if (sprite.components) {\n Object.entries(sprite.components).forEach(([id, component]) => {\n engine.registerSpriteComponent(id, component);\n });\n }\n if (sprite.eventComponent) {\n engine.addEventComponentResolver(sprite.eventComponent);\n }\n },\n };\n }\n return module;\n });\n return modules\n });\n}\n\nexport const GlobalConfigToken = \"GlobalConfigToken\";\n\nexport function provideGlobalConfig(config: any) {\n return {\n provide: GlobalConfigToken,\n useValue: config ?? {},\n };\n}\n\nexport function provideClientGlobalConfig(config: any = {}) {\n config.keyboardControls = {\n up: 'up',\n down: 'down',\n left: 'left',\n right: 'right',\n action: 'space',\n dash: 'shift',\n escape: 'escape',\n ...(config.keyboardControls ?? {}),\n }\n return provideGlobalConfig(config)\n}\n"],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -1,4 +1,4 @@
1
- import { Context } from '@signe/di';
1
+ import { RpgContext } from '@rpgjs/common';
2
2
  export declare const WebSocketToken = "websocket";
3
3
  export type SocketQueryValue = string | null | undefined;
4
4
  export type SocketQuery = Record<string, SocketQueryValue>;
@@ -9,9 +9,9 @@ export type SocketUpdateProperties = {
9
9
  };
10
10
  export type WebSocketMode = "standalone" | "mmorpg";
11
11
  export declare abstract class AbstractWebsocket {
12
- protected context: Context;
12
+ protected context: RpgContext;
13
13
  readonly mode?: WebSocketMode;
14
- constructor(context: Context);
14
+ constructor(context: RpgContext);
15
15
  abstract connection(listeners?: (data: any) => void): Promise<void>;
16
16
  abstract emit(event: string, data: any): void;
17
17
  abstract on(event: string, callback: (data: any) => void): void;
@@ -1 +1 @@
1
- {"version":3,"file":"AbstractSocket.js","names":[],"sources":["../../src/services/AbstractSocket.ts"],"sourcesContent":["import { Context } from \"@signe/di\";\n\nexport const WebSocketToken = \"websocket\";\n\nexport type SocketQueryValue = string | null | undefined;\nexport type SocketQuery = Record<string, SocketQueryValue>;\nexport type SocketUpdateProperties = {\n room: string;\n host?: string;\n query?: SocketQuery;\n};\nexport type WebSocketMode = \"standalone\" | \"mmorpg\";\n\nexport abstract class AbstractWebsocket {\n readonly mode?: WebSocketMode;\n\n constructor(protected context: Context) {}\n\n abstract connection(listeners?: (data: any) => void): Promise<void>;\n abstract emit(event: string, data: any): void;\n abstract on(event: string, callback: (data: any) => void): void;\n abstract off(event: string, callback: (data: any) => void): void;\n abstract updateProperties(params: SocketUpdateProperties): void;\n abstract reconnect(listeners?: (data: any) => void): Promise<void>;\n}\n"],"mappings":";AAEA,IAAa,iBAAiB;AAW9B,IAAsB,oBAAtB,MAAwC;CAGtC,YAAY,SAA4B;EAAlB,KAAA,UAAA;CAAmB;AAQ3C"}
1
+ {"version":3,"file":"AbstractSocket.js","names":[],"sources":["../../src/services/AbstractSocket.ts"],"sourcesContent":["import type { RpgContext } from \"@rpgjs/common\";\n\nexport const WebSocketToken = \"websocket\";\n\nexport type SocketQueryValue = string | null | undefined;\nexport type SocketQuery = Record<string, SocketQueryValue>;\nexport type SocketUpdateProperties = {\n room: string;\n host?: string;\n query?: SocketQuery;\n};\nexport type WebSocketMode = \"standalone\" | \"mmorpg\";\n\nexport abstract class AbstractWebsocket {\n readonly mode?: WebSocketMode;\n\n constructor(protected context: RpgContext) {}\n\n abstract connection(listeners?: (data: any) => void): Promise<void>;\n abstract emit(event: string, data: any): void;\n abstract on(event: string, callback: (data: any) => void): void;\n abstract off(event: string, callback: (data: any) => void): void;\n abstract updateProperties(params: SocketUpdateProperties): void;\n abstract reconnect(listeners?: (data: any) => void): Promise<void>;\n}\n"],"mappings":";AAEA,IAAa,iBAAiB;AAW9B,IAAsB,oBAAtB,MAAwC;CAGtC,YAAY,SAA+B;EAArB,KAAA,UAAA;CAAsB;AAQ9C"}
@@ -1,5 +1,4 @@
1
- import { Context } from '@signe/di';
2
- import { LightingState } from '@rpgjs/common';
1
+ import { LightingState, RpgContext, RpgProvider } from '@rpgjs/common';
3
2
  import { RpgClientMap } from '../Game/Map';
4
3
  export declare const LoadMapToken = "LoadMapToken";
5
4
  /**
@@ -51,7 +50,7 @@ export declare class LoadMapService {
51
50
  private context;
52
51
  private options;
53
52
  private updateMapService;
54
- constructor(context: Context, options: LoadMapOptions);
53
+ constructor(context: RpgContext, options: LoadMapOptions);
55
54
  initialize(): void;
56
55
  load(mapId: string): Promise<MapData>;
57
56
  }
@@ -146,7 +145,4 @@ export declare class LoadMapService {
146
145
  * @see {@link LoadMapOptions} for callback function signature
147
146
  * @see {@link MapData} for return data structure
148
147
  */
149
- export declare function provideLoadMap(options: LoadMapOptions): {
150
- provide: string;
151
- useFactory: (context: Context) => void;
152
- }[];
148
+ export declare function provideLoadMap(options: LoadMapOptions): RpgProvider[];
@@ -6,7 +6,7 @@ var LoadMapService = class {
6
6
  constructor(context, options) {
7
7
  this.context = context;
8
8
  this.options = options;
9
- if (context["side"] === "server") return;
9
+ if (context.side === "server") return;
10
10
  }
11
11
  initialize() {}
12
12
  async load(mapId) {
@@ -111,7 +111,7 @@ function provideLoadMap(options) {
111
111
  return [{
112
112
  provide: UpdateMapToken,
113
113
  useFactory: (context) => {
114
- if (context["side"] === "client") console.warn("UpdateMapToken is not overridden");
114
+ if (context.side === "client") console.warn("UpdateMapToken is not overridden");
115
115
  }
116
116
  }, {
117
117
  provide: LoadMapToken,
@@ -1 +1 @@
1
- {"version":3,"file":"loadMap.js","names":[],"sources":["../../src/services/loadMap.ts"],"sourcesContent":["import { Context, inject } from \"@signe/di\";\nimport { UpdateMapToken, UpdateMapService, type LightingState } from \"@rpgjs/common\";\nimport type { RpgClientMap } from \"../Game/Map\";\n\nexport const LoadMapToken = 'LoadMapToken'\n\n/**\n * Represents the structure of map data that should be returned by the load map callback.\n * This interface defines all the properties that can be provided when loading a custom map.\n * \n * @interface MapData\n */\nexport type MapData = {\n /** Raw map data that will be passed to the map component */\n data: any;\n /** CanvasEngine component that will render the map */\n component: any;\n /** Optional map width in pixels, used for viewport calculations */\n width?: number;\n /** Optional map height in pixels, used for viewport calculations */\n height?: number;\n /** Optional map events data (NPCs, interactive objects, etc.) */\n events?: any;\n /** Optional named positions, for example Tiled point objects used by changeMap(\"map\", \"name\") */\n positions?: Record<string, { x: number; y: number; z?: number }>;\n /** Optional map identifier, defaults to the mapId parameter if not provided */\n id?: string;\n /** Optional initial lighting state for the loaded map */\n lighting?: LightingState | null;\n /** Optional render parameters passed to the map component. */\n params?: Record<string, unknown>;\n /** Internal controller used by a progressive map provider. */\n streamController?: { attach(map: RpgClientMap): void; detach(): void };\n}\n\n/**\n * Callback function type for loading map data.\n * This function receives a map ID and should return either a MapData object directly\n * or a Promise that resolves to a MapData object.\n * \n * @callback LoadMapOptions\n * @param {string} mapId - The identifier of the map to load\n * @returns {Promise<MapData> | MapData} The map data object or a promise resolving to it\n */\nexport type LoadMapOptions = (mapId: string) => Promise<MapData> | MapData \n\nexport class LoadMapService {\n private updateMapService: UpdateMapService;\n\n constructor(private context: Context, private options: LoadMapOptions) {\n if (context['side'] === 'server') {\n return\n }\n }\n\n initialize(): void {}\n\n async load(mapId: string) {\n this.updateMapService ??= inject(this.context, UpdateMapToken);\n const map = await this.options(mapId.replace('map-', ''))\n await this.updateMapService.update(map);\n return map;\n }\n}\n\n/**\n * Creates a dependency injection configuration for custom map loading on the client side.\n * \n * This function allows you to customize how maps are loaded and displayed by providing\n * a callback that defines custom map data and rendering components. It's designed to work\n * with the RPG-JS dependency injection system and enables integration of custom map formats\n * like Tiled TMX files or any other map data structure.\n * \n * The function sets up the necessary service providers for map loading, including:\n * - UpdateMapToken: Handles map updates in the client context\n * - LoadMapToken: Provides the LoadMapService with your custom loading logic\n * \n * **Design Concept:**\n * The function follows the provider pattern, creating a modular way to inject custom\n * map loading behavior into the RPG-JS client engine. It separates the concern of\n * map data fetching from map rendering, allowing developers to focus on their specific\n * map format while leveraging the engine's rendering capabilities.\n * \n * @param {LoadMapOptions} options - Callback function that handles map loading logic\n * @returns {Array<Object>} Array of dependency injection provider configurations\n * \n * @example\n * ```typescript\n * import { provideLoadMap } from '@rpgjs/client'\n * import { createModule } from '@rpgjs/common'\n * import MyTiledMapComponent from './MyTiledMapComponent.ce'\n * \n * // Basic usage with JSON map data\n * export function provideCustomMap() {\n * return createModule(\"CustomMap\", [\n * provideLoadMap(async (mapId) => {\n * const response = await fetch(`/maps/${mapId}.json`)\n * const mapData = await response.json()\n * \n * return {\n * data: mapData,\n * component: MyTiledMapComponent,\n * width: mapData.width,\n * height: mapData.height,\n * events: mapData.events\n * }\n * })\n * ])\n * }\n * \n * // Advanced usage with Tiled TMX files\n * export function provideTiledMap() {\n * return createModule(\"TiledMap\", [\n * provideLoadMap(async (mapId) => {\n * // Load TMX file\n * const tmxResponse = await fetch(`/tiled/${mapId}.tmx`)\n * const tmxData = await tmxResponse.text()\n * \n * // Parse TMX data (using a TMX parser)\n * const parsedMap = parseTMX(tmxData)\n * \n * return {\n * data: parsedMap,\n * component: TiledMapRenderer,\n * width: parsedMap.width * parsedMap.tilewidth,\n * height: parsedMap.height * parsedMap.tileheight,\n * events: parsedMap.objectGroups?.find(g => g.name === 'events')?.objects\n * }\n * })\n * ])\n * }\n * \n * // Synchronous usage for static maps\n * export function provideStaticMap() {\n * return createModule(\"StaticMap\", [\n * provideLoadMap((mapId) => {\n * const staticMaps = {\n * 'town': { tiles: [...], npcs: [...] },\n * 'dungeon': { tiles: [...], monsters: [...] }\n * }\n * \n * return {\n * data: staticMaps[mapId],\n * component: StaticMapComponent,\n * width: 800,\n * height: 600\n * }\n * })\n * ])\n * }\n * ```\n * \n * @since 4.0.0\n * @see {@link LoadMapOptions} for callback function signature\n * @see {@link MapData} for return data structure\n */\nexport function provideLoadMap(options: LoadMapOptions) {\n return [\n {\n provide: UpdateMapToken,\n useFactory: (context: Context) => {\n if (context['side'] === 'client') {\n console.warn('UpdateMapToken is not overridden')\n }\n return\n },\n },\n {\n provide: LoadMapToken,\n useFactory: (context: Context) => new LoadMapService(context, options),\n },\n ];\n}\n"],"mappings":";;;AAIA,IAAa,eAAe;AA0C5B,IAAa,iBAAb,MAA4B;CAG1B,YAAY,SAA0B,SAAiC;EAAnD,KAAA,UAAA;EAA0B,KAAA,UAAA;EAC5C,IAAI,QAAQ,YAAY,UACtB;CAEJ;CAEA,aAAmB,CAAC;CAEpB,MAAM,KAAK,OAAe;EACxB,KAAK,qBAAqB,OAAO,KAAK,SAAS,cAAc;EAC7D,MAAM,MAAM,MAAM,KAAK,QAAQ,MAAM,QAAQ,QAAQ,EAAE,CAAC;EACxD,MAAM,KAAK,iBAAiB,OAAO,GAAG;EACtC,OAAO;CACT;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6FA,SAAgB,eAAe,SAAyB;CACtD,OAAO,CACL;EACE,SAAS;EACT,aAAa,YAAqB;GAChC,IAAI,QAAQ,YAAY,UACtB,QAAQ,KAAK,kCAAkC;EAGnD;CACF,GACA;EACE,SAAS;EACT,aAAa,YAAqB,IAAI,eAAe,SAAS,OAAO;CACvE,CACF;AACF"}
1
+ {"version":3,"file":"loadMap.js","names":[],"sources":["../../src/services/loadMap.ts"],"sourcesContent":["import { Context, inject } from \"@signe/di\";\nimport { UpdateMapToken, UpdateMapService, type LightingState, type RpgContext, type RpgProvider } from \"@rpgjs/common\";\nimport type { RpgClientMap } from \"../Game/Map\";\n\nexport const LoadMapToken = 'LoadMapToken'\n\n/**\n * Represents the structure of map data that should be returned by the load map callback.\n * This interface defines all the properties that can be provided when loading a custom map.\n * \n * @interface MapData\n */\nexport type MapData = {\n /** Raw map data that will be passed to the map component */\n data: any;\n /** CanvasEngine component that will render the map */\n component: any;\n /** Optional map width in pixels, used for viewport calculations */\n width?: number;\n /** Optional map height in pixels, used for viewport calculations */\n height?: number;\n /** Optional map events data (NPCs, interactive objects, etc.) */\n events?: any;\n /** Optional named positions, for example Tiled point objects used by changeMap(\"map\", \"name\") */\n positions?: Record<string, { x: number; y: number; z?: number }>;\n /** Optional map identifier, defaults to the mapId parameter if not provided */\n id?: string;\n /** Optional initial lighting state for the loaded map */\n lighting?: LightingState | null;\n /** Optional render parameters passed to the map component. */\n params?: Record<string, unknown>;\n /** Internal controller used by a progressive map provider. */\n streamController?: { attach(map: RpgClientMap): void; detach(): void };\n}\n\n/**\n * Callback function type for loading map data.\n * This function receives a map ID and should return either a MapData object directly\n * or a Promise that resolves to a MapData object.\n * \n * @callback LoadMapOptions\n * @param {string} mapId - The identifier of the map to load\n * @returns {Promise<MapData> | MapData} The map data object or a promise resolving to it\n */\nexport type LoadMapOptions = (mapId: string) => Promise<MapData> | MapData \n\nexport class LoadMapService {\n private updateMapService: UpdateMapService;\n\n constructor(private context: RpgContext, private options: LoadMapOptions) {\n if (context.side === 'server') {\n return\n }\n }\n\n initialize(): void {}\n\n async load(mapId: string) {\n this.updateMapService ??= inject(this.context as Context, UpdateMapToken);\n const map = await this.options(mapId.replace('map-', ''))\n await this.updateMapService.update(map);\n return map;\n }\n}\n\n/**\n * Creates a dependency injection configuration for custom map loading on the client side.\n * \n * This function allows you to customize how maps are loaded and displayed by providing\n * a callback that defines custom map data and rendering components. It's designed to work\n * with the RPG-JS dependency injection system and enables integration of custom map formats\n * like Tiled TMX files or any other map data structure.\n * \n * The function sets up the necessary service providers for map loading, including:\n * - UpdateMapToken: Handles map updates in the client context\n * - LoadMapToken: Provides the LoadMapService with your custom loading logic\n * \n * **Design Concept:**\n * The function follows the provider pattern, creating a modular way to inject custom\n * map loading behavior into the RPG-JS client engine. It separates the concern of\n * map data fetching from map rendering, allowing developers to focus on their specific\n * map format while leveraging the engine's rendering capabilities.\n * \n * @param {LoadMapOptions} options - Callback function that handles map loading logic\n * @returns {Array<Object>} Array of dependency injection provider configurations\n * \n * @example\n * ```typescript\n * import { provideLoadMap } from '@rpgjs/client'\n * import { createModule } from '@rpgjs/common'\n * import MyTiledMapComponent from './MyTiledMapComponent.ce'\n * \n * // Basic usage with JSON map data\n * export function provideCustomMap() {\n * return createModule(\"CustomMap\", [\n * provideLoadMap(async (mapId) => {\n * const response = await fetch(`/maps/${mapId}.json`)\n * const mapData = await response.json()\n * \n * return {\n * data: mapData,\n * component: MyTiledMapComponent,\n * width: mapData.width,\n * height: mapData.height,\n * events: mapData.events\n * }\n * })\n * ])\n * }\n * \n * // Advanced usage with Tiled TMX files\n * export function provideTiledMap() {\n * return createModule(\"TiledMap\", [\n * provideLoadMap(async (mapId) => {\n * // Load TMX file\n * const tmxResponse = await fetch(`/tiled/${mapId}.tmx`)\n * const tmxData = await tmxResponse.text()\n * \n * // Parse TMX data (using a TMX parser)\n * const parsedMap = parseTMX(tmxData)\n * \n * return {\n * data: parsedMap,\n * component: TiledMapRenderer,\n * width: parsedMap.width * parsedMap.tilewidth,\n * height: parsedMap.height * parsedMap.tileheight,\n * events: parsedMap.objectGroups?.find(g => g.name === 'events')?.objects\n * }\n * })\n * ])\n * }\n * \n * // Synchronous usage for static maps\n * export function provideStaticMap() {\n * return createModule(\"StaticMap\", [\n * provideLoadMap((mapId) => {\n * const staticMaps = {\n * 'town': { tiles: [...], npcs: [...] },\n * 'dungeon': { tiles: [...], monsters: [...] }\n * }\n * \n * return {\n * data: staticMaps[mapId],\n * component: StaticMapComponent,\n * width: 800,\n * height: 600\n * }\n * })\n * ])\n * }\n * ```\n * \n * @since 4.0.0\n * @see {@link LoadMapOptions} for callback function signature\n * @see {@link MapData} for return data structure\n */\nexport function provideLoadMap(options: LoadMapOptions): RpgProvider[] {\n return [\n {\n provide: UpdateMapToken,\n useFactory: (context: RpgContext) => {\n if (context.side === 'client') {\n console.warn('UpdateMapToken is not overridden')\n }\n return\n },\n },\n {\n provide: LoadMapToken,\n useFactory: (context: RpgContext) => new LoadMapService(context, options),\n },\n ];\n}\n"],"mappings":";;;AAIA,IAAa,eAAe;AA0C5B,IAAa,iBAAb,MAA4B;CAG1B,YAAY,SAA6B,SAAiC;EAAtD,KAAA,UAAA;EAA6B,KAAA,UAAA;EAC/C,IAAI,QAAQ,SAAS,UACnB;CAEJ;CAEA,aAAmB,CAAC;CAEpB,MAAM,KAAK,OAAe;EACxB,KAAK,qBAAqB,OAAO,KAAK,SAAoB,cAAc;EACxE,MAAM,MAAM,MAAM,KAAK,QAAQ,MAAM,QAAQ,QAAQ,EAAE,CAAC;EACxD,MAAM,KAAK,iBAAiB,OAAO,GAAG;EACtC,OAAO;CACT;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6FA,SAAgB,eAAe,SAAwC;CACrE,OAAO,CACL;EACE,SAAS;EACT,aAAa,YAAwB;GACnC,IAAI,QAAQ,SAAS,UACnB,QAAQ,KAAK,kCAAkC;EAGnD;CACF,GACA;EACE,SAAS;EACT,aAAa,YAAwB,IAAI,eAAe,SAAS,OAAO;CAC1E,CACF;AACF"}