@rpgjs/client 5.0.0-alpha.21 → 5.0.0-alpha.22

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 (72) hide show
  1. package/dist/Game/Object.d.ts +109 -0
  2. package/dist/RpgClientEngine.d.ts +111 -0
  3. package/dist/index.d.ts +2 -1
  4. package/dist/index.js +2 -1
  5. package/dist/index.js.map +1 -1
  6. package/dist/index10.js +1 -1
  7. package/dist/index15.js +42 -3
  8. package/dist/index15.js.map +1 -1
  9. package/dist/index19.js.map +1 -1
  10. package/dist/index2.js +142 -2
  11. package/dist/index2.js.map +1 -1
  12. package/dist/index20.js +15 -2
  13. package/dist/index20.js.map +1 -1
  14. package/dist/index23.js.map +1 -1
  15. package/dist/index26.js +2 -2
  16. package/dist/index26.js.map +1 -1
  17. package/dist/index27.js +2 -2
  18. package/dist/index27.js.map +1 -1
  19. package/dist/index34.js.map +1 -1
  20. package/dist/index35.js.map +1 -1
  21. package/dist/index36.js +8 -1
  22. package/dist/index36.js.map +1 -1
  23. package/dist/index37.js +1 -1
  24. package/dist/index38.js +1 -1
  25. package/dist/index40.js +6 -6
  26. package/dist/index41.js +320 -3681
  27. package/dist/index41.js.map +1 -1
  28. package/dist/index42.js +3686 -183
  29. package/dist/index42.js.map +1 -1
  30. package/dist/index43.js +71 -498
  31. package/dist/index43.js.map +1 -1
  32. package/dist/index44.js +182 -72
  33. package/dist/index44.js.map +1 -1
  34. package/dist/index45.js +500 -2
  35. package/dist/index45.js.map +1 -1
  36. package/dist/index46.js +3 -17
  37. package/dist/index46.js.map +1 -1
  38. package/dist/index47.js +16 -142
  39. package/dist/index47.js.map +1 -1
  40. package/dist/index48.js +206 -8
  41. package/dist/index48.js.map +1 -1
  42. package/dist/index49.js +7 -108
  43. package/dist/index49.js.map +1 -1
  44. package/dist/index50.js +104 -127
  45. package/dist/index50.js.map +1 -1
  46. package/dist/index51.js +122 -123
  47. package/dist/index51.js.map +1 -1
  48. package/dist/index52.js +123 -98
  49. package/dist/index52.js.map +1 -1
  50. package/dist/index53.js +107 -136
  51. package/dist/index53.js.map +1 -1
  52. package/dist/index54.js +139 -7
  53. package/dist/index54.js.map +1 -1
  54. package/dist/index55.js +7 -52
  55. package/dist/index55.js.map +1 -1
  56. package/dist/index56.js +54 -0
  57. package/dist/index56.js.map +1 -0
  58. package/dist/index8.js +8 -0
  59. package/dist/index8.js.map +1 -1
  60. package/dist/module.d.ts +43 -4
  61. package/dist/services/keyboardControls.d.ts +13 -2
  62. package/dist/services/mmorpg.d.ts +1 -1
  63. package/dist/services/standalone.d.ts +1 -1
  64. package/package.json +11 -10
  65. package/src/Game/Object.ts +82 -8
  66. package/src/RpgClientEngine.ts +173 -2
  67. package/src/components/character.ce +67 -4
  68. package/src/components/prebuilt/index.ts +1 -0
  69. package/src/components/scenes/draw-map.ce +12 -3
  70. package/src/index.ts +2 -1
  71. package/src/module.ts +56 -2
  72. package/src/services/keyboardControls.ts +13 -1
package/dist/index2.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import component from './index21.js';
2
2
  import { inject } from './index19.js';
3
- import { signal, bootstrapCanvas, Howl } from 'canvasengine';
3
+ import { signal, trigger, KeyboardControls, bootstrapCanvas, Howl } from 'canvasengine';
4
4
  import { WebSocketToken } from './index22.js';
5
5
  import { LoadMapToken } from './index7.js';
6
6
  import { RpgSound } from './index17.js';
@@ -14,7 +14,6 @@ import { lastValueFrom } from 'rxjs';
14
14
  import { GlobalConfigToken } from './index8.js';
15
15
  import * as PIXI from 'pixi.js';
16
16
  import { PrebuiltComponentAnimations } from './index12.js';
17
- import { KeyboardControls } from './index20.js';
18
17
 
19
18
  class RpgClientEngine {
20
19
  constructor(context) {
@@ -31,6 +30,10 @@ class RpgClientEngine {
31
30
  this.playerIdSignal = signal(null);
32
31
  this.spriteComponentsBehind = signal([]);
33
32
  this.spriteComponentsInFront = signal([]);
33
+ /** ID of the sprite that the camera should follow. null means follow the current player */
34
+ this.cameraFollowTargetId = signal(null);
35
+ /** Trigger for map shake animation */
36
+ this.mapShakeTrigger = trigger();
34
37
  this.predictionEnabled = false;
35
38
  this.SERVER_CORRECTION_THRESHOLD = 30;
36
39
  this.inputFrameCounter = 0;
@@ -164,6 +167,7 @@ class RpgClientEngine {
164
167
  });
165
168
  this.webSocket.on("changeMap", (data) => {
166
169
  this.sceneMap.reset();
170
+ this.cameraFollowTargetId.set(null);
167
171
  this.loadScene(data.mapId);
168
172
  });
169
173
  this.webSocket.on("showComponentAnimation", (data) => {
@@ -187,6 +191,29 @@ class RpgClientEngine {
187
191
  const { soundId } = data;
188
192
  this.stopSound(soundId);
189
193
  });
194
+ this.webSocket.on("stopAllSounds", () => {
195
+ this.stopAllSounds();
196
+ });
197
+ this.webSocket.on("cameraFollow", (data) => {
198
+ const { targetId, smoothMove } = data;
199
+ this.setCameraFollow(targetId, smoothMove);
200
+ });
201
+ this.webSocket.on("flash", (data) => {
202
+ const { object, type, duration, cycles, alpha, tint } = data;
203
+ const sprite = object ? this.sceneMap.getObjectById(object) : void 0;
204
+ if (sprite && typeof sprite.flash === "function") {
205
+ sprite.flash({ type, duration, cycles, alpha, tint });
206
+ }
207
+ });
208
+ this.webSocket.on("shakeMap", (data) => {
209
+ const { intensity, duration, frequency, direction } = data || {};
210
+ this.mapShakeTrigger.start({
211
+ intensity,
212
+ duration,
213
+ frequency,
214
+ direction
215
+ });
216
+ });
190
217
  this.webSocket.on("open", () => {
191
218
  this.hooks.callHooks("client-engine-onConnected", this, this.socket).subscribe();
192
219
  });
@@ -552,6 +579,64 @@ class RpgClientEngine {
552
579
  console.warn(`Sound with id "${soundId}" not found or cannot be stopped`);
553
580
  }
554
581
  }
582
+ /**
583
+ * Stop all currently playing sounds
584
+ *
585
+ * This method stops all sounds that are currently playing.
586
+ * Useful when changing maps to prevent sound overlap.
587
+ *
588
+ * @example
589
+ * ```ts
590
+ * // Stop all sounds
591
+ * engine.stopAllSounds();
592
+ * ```
593
+ */
594
+ stopAllSounds() {
595
+ this.sounds.forEach((sound) => {
596
+ if (sound && sound.stop) {
597
+ sound.stop();
598
+ }
599
+ });
600
+ }
601
+ /**
602
+ * Set the camera to follow a specific sprite
603
+ *
604
+ * This method changes which sprite the camera viewport should follow.
605
+ * The camera will smoothly animate to the target sprite if smoothMove options are provided.
606
+ *
607
+ * ## Design
608
+ *
609
+ * The camera follow target is stored in a signal that is read by sprite components.
610
+ * Each sprite checks if it should be followed by comparing its ID with the target ID.
611
+ * When smoothMove options are provided, the viewport animation is handled by CanvasEngine's
612
+ * viewport system.
613
+ *
614
+ * @param targetId - The ID of the sprite to follow. Set to null to follow the current player
615
+ * @param smoothMove - Animation options. Can be a boolean (default: true) or an object with time and ease
616
+ * @param smoothMove.time - Duration of the animation in milliseconds (optional)
617
+ * @param smoothMove.ease - Easing function name from https://easings.net (optional)
618
+ *
619
+ * @example
620
+ * ```ts
621
+ * // Follow another player with default smooth animation
622
+ * engine.setCameraFollow(otherPlayerId, true);
623
+ *
624
+ * // Follow an event with custom smooth animation
625
+ * engine.setCameraFollow(eventId, {
626
+ * time: 1000,
627
+ * ease: "easeInOutQuad"
628
+ * });
629
+ *
630
+ * // Follow without animation (instant)
631
+ * engine.setCameraFollow(targetId, false);
632
+ *
633
+ * // Return to following current player
634
+ * engine.setCameraFollow(null);
635
+ * ```
636
+ */
637
+ setCameraFollow(targetId, smoothMove) {
638
+ this.cameraFollowTargetId.set(targetId);
639
+ }
555
640
  addParticle(particle) {
556
641
  this.particleSettings.emitters.push(particle);
557
642
  return particle;
@@ -756,6 +841,61 @@ class RpgClientEngine {
756
841
  this.frameOffset = 0;
757
842
  this.inputFrameCounter = 0;
758
843
  }
844
+ /**
845
+ * Trigger a flash animation on a sprite
846
+ *
847
+ * This method allows you to trigger a flash effect on any sprite from client-side code.
848
+ * The flash can be configured with various options including type (alpha, tint, or both),
849
+ * duration, cycles, and color.
850
+ *
851
+ * ## Design
852
+ *
853
+ * The flash is applied directly to the sprite object using its flash trigger.
854
+ * This is useful for client-side visual feedback, UI interactions, or local effects
855
+ * that don't need to be synchronized with the server.
856
+ *
857
+ * @param spriteId - The ID of the sprite to flash. If not provided, flashes the current player
858
+ * @param options - Flash configuration options
859
+ * @param options.type - Type of flash effect: 'alpha' (opacity), 'tint' (color), or 'both' (default: 'alpha')
860
+ * @param options.duration - Duration of the flash animation in milliseconds (default: 300)
861
+ * @param options.cycles - Number of flash cycles (flash on/off) (default: 1)
862
+ * @param options.alpha - Alpha value when flashing, from 0 to 1 (default: 0.3)
863
+ * @param options.tint - Tint color when flashing as hex value or color name (default: 0xffffff - white)
864
+ *
865
+ * @example
866
+ * ```ts
867
+ * // Flash the current player with default settings
868
+ * engine.flash();
869
+ *
870
+ * // Flash a specific sprite with red tint
871
+ * engine.flash('sprite-id', { type: 'tint', tint: 0xff0000 });
872
+ *
873
+ * // Flash with both alpha and tint for dramatic effect
874
+ * engine.flash(undefined, {
875
+ * type: 'both',
876
+ * alpha: 0.5,
877
+ * tint: 0xff0000,
878
+ * duration: 200,
879
+ * cycles: 2
880
+ * });
881
+ *
882
+ * // Quick damage flash on current player
883
+ * engine.flash(undefined, {
884
+ * type: 'tint',
885
+ * tint: 'red',
886
+ * duration: 150,
887
+ * cycles: 1
888
+ * });
889
+ * ```
890
+ */
891
+ flash(spriteId, options) {
892
+ const targetId = spriteId || this.playerId;
893
+ if (!targetId) return;
894
+ const sprite = this.sceneMap.getObjectById(targetId);
895
+ if (sprite && typeof sprite.flash === "function") {
896
+ sprite.flash(options);
897
+ }
898
+ }
759
899
  applyServerAck(ack) {
760
900
  if (this.predictionEnabled && this.prediction) {
761
901
  this.prediction.applyServerAck({
@@ -1 +1 @@
1
- {"version":3,"file":"index2.js","sources":["../src/RpgClientEngine.ts"],"sourcesContent":["import Canvas from \"./components/scenes/canvas.ce\";\nimport { Context, inject } from \"@signe/di\";\nimport { signal, bootstrapCanvas, Howler, Howl } from \"canvasengine\";\nimport { AbstractWebsocket, WebSocketToken } from \"./services/AbstractSocket\";\nimport { LoadMapService, LoadMapToken } from \"./services/loadMap\";\nimport { RpgSound } from \"./Sound\";\nimport { RpgResource } from \"./Resource\";\nimport { Hooks, ModulesToken, Direction } from \"@rpgjs/common\";\n\ntype DirectionValue = \"up\" | \"down\" | \"left\" | \"right\";\nimport { load } from \"@signe/sync\";\nimport { RpgClientMap } from \"./Game/Map\"\nimport { RpgGui } from \"./Gui/Gui\";\nimport { AnimationManager } from \"./Game/AnimationManager\";\nimport { lastValueFrom, Observable } from \"rxjs\";\nimport { GlobalConfigToken } from \"./module\";\nimport * as PIXI from \"pixi.js\";\nimport { PrebuiltComponentAnimations } from \"./components/animations\";\nimport {\n PredictionController,\n type PredictionState,\n} from \"@rpgjs/common\";\nimport { KeyboardControls } from \"./services/keyboardControls\";\n\nexport class RpgClientEngine<T = any> {\n private guiService: RpgGui;\n private webSocket: AbstractWebsocket;\n private loadMapService: LoadMapService;\n private hooks: Hooks;\n private sceneMap: RpgClientMap\n private selector: HTMLElement;\n public globalConfig: T;\n public sceneComponent: any;\n stopProcessingInput = false;\n width = signal(\"100%\");\n height = signal(\"100%\");\n spritesheets: Map<string, any> = new Map();\n sounds: Map<string, any> = new Map();\n componentAnimations: any[] = [];\n private spritesheetResolver?: (id: string) => any | Promise<any>;\n private soundResolver?: (id: string) => any | Promise<any>;\n particleSettings: {\n emitters: any[]\n } = {\n emitters: []\n }\n renderer: PIXI.Renderer;\n tick: Observable<number>;\n playerIdSignal = signal<string | null>(null);\n spriteComponentsBehind = signal<any[]>([]);\n spriteComponentsInFront = signal<any[]>([]);\n\n private predictionEnabled = false;\n private prediction?: PredictionController<Direction>;\n private readonly SERVER_CORRECTION_THRESHOLD = 30;\n private inputFrameCounter = 0;\n private frameOffset = 0;\n // Ping/Pong for RTT measurement\n private rtt: number = 0; // Round-trip time in ms\n private pingInterval: any = null;\n private readonly PING_INTERVAL_MS = 5000; // Send ping every 5 seconds\n private lastInputTime = 0;\n\n constructor(public context: Context) {\n this.webSocket = inject(context, WebSocketToken);\n this.guiService = inject(context, RpgGui);\n this.loadMapService = inject(context, LoadMapToken);\n this.hooks = inject<Hooks>(context, ModulesToken);\n this.globalConfig = inject(context, GlobalConfigToken)\n\n if (!this.globalConfig) {\n this.globalConfig = {} as T\n }\n if (!(this.globalConfig as any).box) {\n (this.globalConfig as any).box = {\n styles: {\n backgroundColor: \"#1a1a2e\",\n backgroundOpacity: 0.9\n },\n sounds: {}\n }\n }\n\n this.addComponentAnimation({\n id: \"animation\",\n component: PrebuiltComponentAnimations.Animation\n })\n\n this.predictionEnabled = (this.globalConfig as any)?.prediction?.enabled !== false;\n this.initializePredictionController();\n }\n\n /**\n * Assigns a CanvasEngine KeyboardControls instance to the dependency injection context\n * \n * This method registers a KeyboardControls instance from CanvasEngine into the DI container,\n * making it available for injection throughout the application. The particularity is that\n * this method is automatically called when a sprite is displayed on the map, allowing the\n * controls to be automatically associated with the active sprite.\n * \n * ## Design\n * \n * - The instance is stored in the DI context under the `KeyboardControls` token\n * - It's automatically assigned when a sprite component mounts (in `character.ce`)\n * - The controls instance comes from the CanvasEngine component's directives\n * - Once registered, it can be retrieved using `inject(KeyboardControls)` from anywhere\n * \n * @param controlInstance - The CanvasEngine KeyboardControls instance to register\n * \n * @example\n * ```ts\n * // The method is automatically called when a sprite is displayed:\n * // client.setKeyboardControls(element.directives.controls)\n * \n * // Later, retrieve and use the controls instance:\n * import { Input, inject, KeyboardControls } from '@rpgjs/client'\n * \n * const controls = inject(KeyboardControls)\n * const control = controls.getControl(Input.Enter)\n * \n * if (control) {\n * console.log(control.actionName) // 'action'\n * }\n * ```\n */\n setKeyboardControls(controlInstance: any) {\n const currentValues = this.context.values['inject:' + KeyboardControls]\n this.context.values['inject:' + KeyboardControls] = {\n ...currentValues,\n values: new Map([['__default__', controlInstance]])\n }\n }\n\n async start() {\n this.sceneMap = new RpgClientMap()\n this.selector = document.body.querySelector(\"#rpg\") as HTMLElement;\n\n const { app, canvasElement } = await bootstrapCanvas(this.selector, Canvas);\n this.renderer = app.renderer as PIXI.Renderer;\n this.tick = canvasElement?.propObservables?.context['tick'].observable\n\n this.tick.subscribe(() => {\n if (Date.now() - this.lastInputTime > 100) {\n const player = this.getCurrentPlayer();\n if (!player) return;\n (this.sceneMap as any).stopMovement(player);\n }\n })\n\n\n this.hooks.callHooks(\"client-spritesheets-load\", this).subscribe();\n this.hooks.callHooks(\"client-spritesheetResolver-load\", this).subscribe();\n this.hooks.callHooks(\"client-sounds-load\", this).subscribe();\n this.hooks.callHooks(\"client-soundResolver-load\", this).subscribe();\n \n RpgSound.init(this);\n RpgResource.init(this);\n this.hooks.callHooks(\"client-gui-load\", this).subscribe();\n this.hooks.callHooks(\"client-particles-load\", this).subscribe();\n this.hooks.callHooks(\"client-componentAnimations-load\", this).subscribe();\n this.hooks.callHooks(\"client-sprite-load\", this).subscribe();\n\n await lastValueFrom(this.hooks.callHooks(\"client-engine-onStart\", this));\n\n // wondow is resize\n window.addEventListener('resize', () => {\n this.hooks.callHooks(\"client-engine-onWindowResize\", this).subscribe();\n })\n\n this.tick.subscribe((tick) => {\n this.hooks.callHooks(\"client-engine-onStep\", this, tick).subscribe();\n\n // Clean up old prediction states and input history every 60 ticks (approximately every second at 60fps)\n if (tick % 60 === 0) {\n const now = Date.now();\n this.prediction?.cleanup(now);\n this.prediction?.tryApplyPendingSnapshot();\n }\n })\n\n await this.webSocket.connection(() => {\n this.initListeners()\n this.guiService._initialize()\n });\n }\n\n private initListeners() {\n this.webSocket.on(\"sync\", (data) => {\n if (data.pId) this.playerIdSignal.set(data.pId)\n\n // Apply client-side prediction filtering and server reconciliation\n this.hooks.callHooks(\"client-sceneMap-onChanges\", this.sceneMap, { partial: data }).subscribe();\n\n load(this.sceneMap, data, true);\n });\n\n // Handle pong responses for RTT measurement\n this.webSocket.on(\"pong\", (data: { serverTick: number; clientFrame: number; clientTime: number }) => {\n const now = Date.now();\n this.rtt = now - data.clientTime;\n\n // Calculate frame offset: how many ticks ahead the server is compared to our frame counter\n // This helps us estimate which server tick corresponds to each client input frame\n const estimatedTicksInFlight = Math.floor(this.rtt / 2 / (1000 / 60)); // Estimate ticks during half RTT\n const estimatedServerTickNow = data.serverTick + estimatedTicksInFlight;\n\n // Update frame offset (only if we have inputs to calibrate with)\n if (this.inputFrameCounter > 0) {\n this.frameOffset = estimatedServerTickNow - data.clientFrame;\n }\n\n console.debug(`[Ping/Pong] RTT: ${this.rtt}ms, ServerTick: ${data.serverTick}, FrameOffset: ${this.frameOffset}`);\n });\n\n this.webSocket.on(\"changeMap\", (data) => {\n this.sceneMap.reset()\n this.loadScene(data.mapId);\n });\n\n this.webSocket.on(\"showComponentAnimation\", (data) => {\n const { params, object, position, id } = data;\n if (!object && position === undefined) {\n throw new Error(\"Please provide an object or x and y coordinates\");\n }\n const player = object ? this.sceneMap.getObjectById(object) : undefined;\n this.getComponentAnimation(id).displayEffect(params, player || position)\n });\n\n this.webSocket.on(\"setAnimation\", (data) => {\n const { animationName, nbTimes, object } = data;\n const player = this.sceneMap.getObjectById(object);\n player.setAnimation(animationName, nbTimes)\n })\n\n this.webSocket.on(\"playSound\", (data) => {\n const { soundId, volume, loop } = data;\n this.playSound(soundId, { volume, loop });\n });\n\n this.webSocket.on(\"stopSound\", (data) => {\n const { soundId } = data;\n this.stopSound(soundId);\n });\n\n this.webSocket.on('open', () => {\n this.hooks.callHooks(\"client-engine-onConnected\", this, this.socket).subscribe();\n // Start ping/pong for synchronization\n })\n\n this.webSocket.on('close', () => {\n this.hooks.callHooks(\"client-engine-onDisconnected\", this, this.socket).subscribe();\n // Stop ping/pong when disconnected\n this.stopPingPong();\n })\n\n this.webSocket.on('error', (error) => {\n this.hooks.callHooks(\"client-engine-onConnectError\", this, error, this.socket).subscribe();\n })\n }\n\n /**\n * Start periodic ping/pong for client-server synchronization\n * \n * Sends ping requests to the server to measure round-trip time (RTT) and\n * calculate the frame offset between client and server ticks.\n * \n * ## Design\n * \n * - Sends ping every 5 seconds\n * - Measures RTT for latency compensation\n * - Calculates frame offset to map client frames to server ticks\n * - Used for accurate server reconciliation\n * \n * @example\n * ```ts\n * // Called automatically when connection opens\n * this.startPingPong();\n * ```\n */\n private startPingPong(): void {\n // Stop existing interval if any\n this.stopPingPong();\n\n // Send initial ping immediately\n this.sendPing();\n\n // Set up periodic pings\n this.pingInterval = setInterval(() => {\n this.sendPing();\n }, this.PING_INTERVAL_MS);\n }\n\n /**\n * Stop periodic ping/pong\n * \n * Stops the ping interval when disconnecting or changing maps.\n * \n * @example\n * ```ts\n * // Called automatically when connection closes\n * this.stopPingPong();\n * ```\n */\n private stopPingPong(): void {\n if (this.pingInterval) {\n clearInterval(this.pingInterval);\n this.pingInterval = null;\n }\n }\n\n /**\n * Send a ping request to the server\n * \n * Sends current client time and frame counter to the server,\n * which will respond with its server tick for synchronization.\n * \n * @example\n * ```ts\n * // Send a ping to measure RTT\n * this.sendPing();\n * ```\n */\n private sendPing(): void {\n const clientTime = Date.now();\n const clientFrame = this.getPhysicsTick();\n\n this.webSocket.emit('ping', {\n clientTime,\n clientFrame\n });\n }\n\n private async loadScene(mapId: string) {\n this.hooks.callHooks(\"client-sceneMap-onBeforeLoading\", this.sceneMap).subscribe();\n\n // Clear client prediction states when changing maps\n this.clearClientPredictionStates();\n\n this.webSocket.updateProperties({ room: mapId })\n await this.webSocket.reconnect(() => {\n this.initListeners()\n this.guiService._initialize()\n })\n const res = await this.loadMapService.load(mapId)\n this.sceneMap.data.set(res)\n this.hooks.callHooks(\"client-sceneMap-onAfterLoading\", this.sceneMap).subscribe();\n this.sceneMap.loadPhysic()\n }\n\n addSpriteSheet<T = any>(spritesheetClass: any, id?: string): any {\n this.spritesheets.set(id || spritesheetClass.id, spritesheetClass);\n return spritesheetClass as any;\n }\n\n /**\n * Set a resolver function for spritesheets\n * \n * The resolver is called when a spritesheet is requested but not found in the cache.\n * It can be synchronous (returns directly) or asynchronous (returns a Promise).\n * The resolved spritesheet is automatically cached for future use.\n * \n * @param resolver - Function that takes a spritesheet ID and returns a spritesheet or Promise of spritesheet\n * \n * @example\n * ```ts\n * // Synchronous resolver\n * engine.setSpritesheetResolver((id) => {\n * if (id === 'dynamic-sprite') {\n * return { id: 'dynamic-sprite', image: 'path/to/image.png', framesWidth: 32, framesHeight: 32 };\n * }\n * return undefined;\n * });\n * \n * // Asynchronous resolver (loading from API)\n * engine.setSpritesheetResolver(async (id) => {\n * const response = await fetch(`/api/spritesheets/${id}`);\n * const data = await response.json();\n * return data;\n * });\n * ```\n */\n setSpritesheetResolver(resolver: (id: string) => any | Promise<any>): void {\n this.spritesheetResolver = resolver;\n }\n\n /**\n * Get a spritesheet by ID, using resolver if not found in cache\n * \n * This method first checks if the spritesheet exists in the cache.\n * If not found and a resolver is set, it calls the resolver to create the spritesheet.\n * The resolved spritesheet is automatically cached for future use.\n * \n * @param id - The spritesheet ID to retrieve\n * @returns The spritesheet if found or created, or undefined if not found and no resolver\n * @returns Promise<any> if the resolver is asynchronous\n * \n * @example\n * ```ts\n * // Synchronous usage\n * const spritesheet = engine.getSpriteSheet('my-sprite');\n * \n * // Asynchronous usage (when resolver returns Promise)\n * const spritesheet = await engine.getSpriteSheet('dynamic-sprite');\n * ```\n */\n getSpriteSheet(id: string): any | Promise<any> {\n // Check cache first\n if (this.spritesheets.has(id)) {\n return this.spritesheets.get(id);\n }\n\n // If not in cache and resolver exists, use it\n if (this.spritesheetResolver) {\n const result = this.spritesheetResolver(id);\n\n // Check if result is a Promise\n if (result instanceof Promise) {\n return result.then((spritesheet) => {\n if (spritesheet) {\n // Cache the resolved spritesheet\n this.spritesheets.set(id, spritesheet);\n }\n return spritesheet;\n });\n } else {\n // Synchronous result\n if (result) {\n // Cache the resolved spritesheet\n this.spritesheets.set(id, result);\n }\n return result;\n }\n }\n\n // No resolver and not in cache\n return undefined;\n }\n\n /**\n * Add a sound to the engine\n * \n * Adds a sound to the engine's sound cache. The sound can be:\n * - A simple object with `id` and `src` properties\n * - A Howler instance\n * - An object with a `play()` method\n * \n * If the sound has a `src` property, a Howler instance will be created automatically.\n * \n * @param sound - The sound object or Howler instance\n * @param id - Optional sound ID (if not provided, uses sound.id)\n * @returns The added sound\n * \n * @example\n * ```ts\n * // Simple sound object\n * engine.addSound({ id: 'click', src: 'click.mp3' });\n * \n * // With explicit ID\n * engine.addSound({ src: 'music.mp3' }, 'background-music');\n * ```\n */\n addSound(sound: any, id?: string): any {\n const soundId = id || sound.id;\n \n if (!soundId) {\n console.warn('Sound added without an ID. It will not be retrievable.');\n return sound;\n }\n\n // If sound has a src property, create a Howler instance\n if (sound.src && typeof sound.src === 'string') {\n const howlOptions: any = {\n src: [sound.src],\n loop: sound.loop || false,\n volume: sound.volume !== undefined ? sound.volume : 1.0,\n };\n\n const howl = new (Howl as any).Howl(howlOptions);\n this.sounds.set(soundId, howl);\n return howl;\n }\n\n // If sound already has a play method (Howler instance or custom), use it directly\n if (sound && typeof sound.play === 'function') {\n this.sounds.set(soundId, sound);\n return sound;\n }\n\n // Otherwise, store as-is\n this.sounds.set(soundId, sound);\n return sound;\n }\n\n /**\n * Set a resolver function for sounds\n * \n * The resolver is called when a sound is requested but not found in the cache.\n * It can be synchronous (returns directly) or asynchronous (returns a Promise).\n * The resolved sound is automatically cached for future use.\n * \n * @param resolver - Function that takes a sound ID and returns a sound or Promise of sound\n * \n * @example\n * ```ts\n * // Synchronous resolver\n * engine.setSoundResolver((id) => {\n * if (id === 'dynamic-sound') {\n * return { id: 'dynamic-sound', src: 'path/to/sound.mp3' };\n * }\n * return undefined;\n * });\n * \n * // Asynchronous resolver (loading from API)\n * engine.setSoundResolver(async (id) => {\n * const response = await fetch(`/api/sounds/${id}`);\n * const data = await response.json();\n * return data;\n * });\n * ```\n */\n setSoundResolver(resolver: (id: string) => any | Promise<any>): void {\n this.soundResolver = resolver;\n }\n\n /**\n * Get a sound by ID, using resolver if not found in cache\n * \n * This method first checks if the sound exists in the cache.\n * If not found and a resolver is set, it calls the resolver to create the sound.\n * The resolved sound is automatically cached for future use.\n * \n * @param id - The sound ID to retrieve\n * @returns The sound if found or created, or undefined if not found and no resolver\n * @returns Promise<any> if the resolver is asynchronous\n * \n * @example\n * ```ts\n * // Synchronous usage\n * const sound = engine.getSound('my-sound');\n * \n * // Asynchronous usage (when resolver returns Promise)\n * const sound = await engine.getSound('dynamic-sound');\n * ```\n */\n getSound(id: string): any | Promise<any> {\n // Check cache first\n if (this.sounds.has(id)) {\n return this.sounds.get(id);\n }\n\n // If not in cache and resolver exists, use it\n if (this.soundResolver) {\n const result = this.soundResolver(id);\n\n // Check if result is a Promise\n if (result instanceof Promise) {\n return result.then((sound) => {\n if (sound) {\n // Cache the resolved sound\n this.sounds.set(id, sound);\n }\n return sound;\n });\n } else {\n // Synchronous result\n if (result) {\n // Cache the resolved sound\n this.sounds.set(id, result);\n }\n return result;\n }\n }\n\n // No resolver and not in cache\n return undefined;\n }\n\n /**\n * Play a sound by its ID\n * \n * This method retrieves a sound from the cache or resolver and plays it.\n * If the sound is not found, it will attempt to resolve it using the soundResolver.\n * Uses Howler.js for audio playback instead of native Audio elements.\n * \n * @param soundId - The sound ID to play\n * @param options - Optional sound configuration\n * @param options.volume - Volume level (0.0 to 1.0, overrides sound default)\n * @param options.loop - Whether the sound should loop (overrides sound default)\n * \n * @example\n * ```ts\n * // Play a sound synchronously\n * engine.playSound('item-pickup');\n * \n * // Play a sound with volume and loop\n * engine.playSound('background-music', { volume: 0.5, loop: true });\n * \n * // Play a sound asynchronously (when resolver returns Promise)\n * await engine.playSound('dynamic-sound', { volume: 0.8 });\n * ```\n */\n async playSound(soundId: string, options?: { volume?: number; loop?: boolean }): Promise<void> {\n const sound = await this.getSound(soundId);\n if (sound && sound.play) {\n // Sound is already a Howler instance or has a play method\n const howlSoundId = sound._sounds?.[0]?._id;\n \n // Apply volume if provided\n if (options?.volume !== undefined) {\n if (howlSoundId !== undefined) {\n sound.volume(Math.max(0, Math.min(1, options.volume)), howlSoundId);\n } else {\n sound.volume(Math.max(0, Math.min(1, options.volume)));\n }\n }\n \n // Apply loop if provided\n if (options?.loop !== undefined) {\n if (howlSoundId !== undefined) {\n sound.loop(options.loop, howlSoundId);\n } else {\n sound.loop(options.loop);\n }\n }\n \n if (howlSoundId !== undefined) {\n sound.play(howlSoundId);\n } else {\n sound.play();\n }\n } else if (sound && sound.src) {\n // If sound is just a source URL, create a Howler instance and cache it\n const howlOptions: any = {\n src: [sound.src],\n loop: options?.loop !== undefined ? options.loop : (sound.loop || false),\n volume: options?.volume !== undefined ? Math.max(0, Math.min(1, options.volume)) : (sound.volume !== undefined ? sound.volume : 1.0),\n };\n\n const howl = new (Howl as any).Howl(howlOptions);\n \n // Cache the Howler instance for future use\n this.sounds.set(soundId, howl);\n \n // Play the sound\n howl.play();\n } else {\n console.warn(`Sound with id \"${soundId}\" not found or cannot be played`);\n }\n }\n\n /**\n * Stop a sound that is currently playing\n * \n * This method stops a sound that was previously started with `playSound()`.\n * \n * @param soundId - The sound ID to stop\n * \n * @example\n * ```ts\n * // Start a looping sound\n * engine.playSound('background-music', { loop: true });\n * \n * // Later, stop it\n * engine.stopSound('background-music');\n * ```\n */\n stopSound(soundId: string): void {\n const sound = this.sounds.get(soundId);\n if (sound && sound.stop) {\n sound.stop();\n } else {\n console.warn(`Sound with id \"${soundId}\" not found or cannot be stopped`);\n }\n }\n\n addParticle(particle: any) {\n this.particleSettings.emitters.push(particle)\n return particle;\n }\n\n /**\n * Add a component to render behind sprites\n * Components added with this method will be displayed with a lower z-index than the sprite\n * \n * @param component - The component to add behind sprites\n * @returns The added component\n * \n * @example\n * ```ts\n * // Add a shadow component behind all sprites\n * engine.addSpriteComponentBehind(ShadowComponent);\n * ```\n */\n addSpriteComponentBehind(component: any) {\n this.spriteComponentsBehind.update((components: any[]) => [...components, component])\n return component\n }\n\n /**\n * Add a component to render in front of sprites\n * Components added with this method will be displayed with a higher z-index than the sprite\n * \n * @param component - The component to add in front of sprites\n * @returns The added component\n * \n * @example\n * ```ts\n * // Add a health bar component in front of all sprites\n * engine.addSpriteComponentInFront(HealthBarComponent);\n * ```\n */\n addSpriteComponentInFront(component: any) {\n this.spriteComponentsInFront.update((components: any[]) => [...components, component])\n return component\n }\n\n /**\n * Add a component animation to the engine\n * \n * Component animations are temporary visual effects that can be displayed\n * on sprites or objects, such as hit indicators, spell effects, or status animations.\n * \n * @param componentAnimation - The component animation configuration\n * @param componentAnimation.id - Unique identifier for the animation\n * @param componentAnimation.component - The component function to render\n * @returns The added component animation configuration\n * \n * @example\n * ```ts\n * // Add a hit animation component\n * engine.addComponentAnimation({\n * id: 'hit',\n * component: HitComponent\n * });\n * \n * // Add an explosion effect component\n * engine.addComponentAnimation({\n * id: 'explosion',\n * component: ExplosionComponent\n * });\n * ```\n */\n addComponentAnimation(componentAnimation: {\n component: any,\n id: string\n }) {\n const instance = new AnimationManager()\n this.componentAnimations.push({\n id: componentAnimation.id,\n component: componentAnimation.component,\n instance: instance,\n current: instance.current\n })\n return componentAnimation;\n }\n\n /**\n * Get a component animation by its ID\n * \n * Retrieves the EffectManager instance for a specific component animation,\n * which can be used to display the animation on sprites or objects.\n * \n * @param id - The unique identifier of the component animation\n * @returns The EffectManager instance for the animation\n * @throws Error if the component animation is not found\n * \n * @example\n * ```ts\n * // Get the hit animation and display it\n * const hitAnimation = engine.getComponentAnimation('hit');\n * hitAnimation.displayEffect({ text: \"Critical!\" }, player);\n * ```\n */\n getComponentAnimation(id: string): AnimationManager {\n const componentAnimation = this.componentAnimations.find((componentAnimation) => componentAnimation.id === id)\n if (!componentAnimation) {\n throw new Error(`Component animation with id ${id} not found`)\n }\n return componentAnimation.instance\n }\n\n async processInput({ input }: { input: Direction }) {\n const timestamp = Date.now();\n let frame: number;\n let tick: number;\n if (this.predictionEnabled && this.prediction) {\n const meta = this.prediction.recordInput(input, timestamp);\n frame = meta.frame;\n tick = meta.tick;\n } else {\n frame = ++this.inputFrameCounter;\n tick = this.getPhysicsTick();\n }\n this.hooks.callHooks(\"client-engine-onInput\", this, { input, playerId: this.playerId }).subscribe();\n\n this.webSocket.emit('move', {\n input,\n timestamp,\n frame,\n tick,\n });\n\n const currentPlayer = this.sceneMap.getCurrentPlayer();\n if (currentPlayer) {\n (this.sceneMap as any).moveBody(currentPlayer, input);\n }\n this.lastInputTime = Date.now();\n const myId = this.playerIdSignal();\n\n }\n\n processAction({ action }: { action: number }) {\n if (this.stopProcessingInput) return;\n this.hooks.callHooks(\"client-engine-onInput\", this, { input: 'action', playerId: this.playerId }).subscribe();\n this.webSocket.emit('action', { action })\n }\n\n get PIXI() {\n return PIXI\n }\n\n get socket() {\n return this.webSocket\n }\n\n get playerId() {\n return this.playerIdSignal()\n }\n\n get scene() {\n return this.sceneMap\n }\n\n private getPhysicsTick(): number {\n return this.sceneMap?.getTick?.() ?? 0;\n }\n\n private getLocalPlayerState(): PredictionState<Direction> {\n const currentPlayer = this.sceneMap?.getCurrentPlayer();\n if (!currentPlayer) {\n return { x: 0, y: 0, direction: Direction.Down };\n }\n const topLeft = this.sceneMap.getBodyPosition(currentPlayer.id, \"top-left\");\n const x = topLeft?.x ?? currentPlayer.x();\n const y = topLeft?.y ?? currentPlayer.y();\n const direction = currentPlayer.direction();\n return { x, y, direction };\n }\n\n private applyAuthoritativeState(state: PredictionState<Direction>): void {\n const player = this.sceneMap?.getCurrentPlayer();\n if (!player) return;\n const hitbox = typeof player.hitbox === \"function\" ? player.hitbox() : player.hitbox;\n const width = hitbox?.w ?? 0;\n const height = hitbox?.h ?? 0;\n const updated = this.sceneMap.updateHitbox(player.id, state.x, state.y, width, height);\n if (!updated) {\n this.sceneMap.setBodyPosition(player.id, state.x, state.y, \"top-left\");\n }\n player.x.set(Math.round(state.x));\n player.y.set(Math.round(state.y));\n if (state.direction) {\n player.changeDirection(state.direction);\n }\n }\n\n private initializePredictionController(): void {\n if (!this.predictionEnabled) {\n this.prediction = undefined;\n return;\n }\n this.prediction = new PredictionController<Direction>({\n correctionThreshold: (this.globalConfig as any)?.prediction?.correctionThreshold ?? this.SERVER_CORRECTION_THRESHOLD,\n historyTtlMs: (this.globalConfig as any)?.prediction?.historyTtlMs ?? 2000,\n getPhysicsTick: () => this.getPhysicsTick(),\n getCurrentState: () => this.getLocalPlayerState(),\n setAuthoritativeState: (state) => this.applyAuthoritativeState(state),\n });\n }\n\n getCurrentPlayer() {\n return this.sceneMap.getCurrentPlayer()\n }\n\n /**\n * Clear client prediction states for cleanup\n * \n * Removes old prediction states and input history to prevent memory leaks.\n * Should be called when changing maps or disconnecting.\n * \n * @example\n * ```ts\n * // Clear prediction states when changing maps\n * engine.clearClientPredictionStates();\n * ```\n */\n clearClientPredictionStates() {\n this.initializePredictionController();\n this.frameOffset = 0;\n this.inputFrameCounter = 0;\n }\n\n private applyServerAck(ack: { frame: number; serverTick?: number; x?: number; y?: number; direction?: Direction }) {\n if (this.predictionEnabled && this.prediction) {\n this.prediction.applyServerAck({\n frame: ack.frame,\n serverTick: ack.serverTick,\n state:\n typeof ack.x === \"number\" && typeof ack.y === \"number\"\n ? { x: ack.x, y: ack.y, direction: ack.direction }\n : undefined,\n });\n return;\n }\n\n if (typeof ack.x !== \"number\" || typeof ack.y !== \"number\") {\n return;\n }\n const player = this.getCurrentPlayer();\n const myId = this.playerIdSignal();\n if (!player || !myId) {\n return;\n }\n const hitbox = typeof player.hitbox === \"function\" ? player.hitbox() : player.hitbox;\n const width = hitbox?.w ?? 0;\n const height = hitbox?.h ?? 0;\n const updated = this.sceneMap.updateHitbox(myId, ack.x, ack.y, width, height);\n if (!updated) {\n this.sceneMap.setBodyPosition(myId, ack.x, ack.y, \"top-left\");\n }\n player.x.set(Math.round(ack.x));\n player.y.set(Math.round(ack.y));\n if (ack.direction) {\n player.changeDirection(ack.direction);\n }\n }\n\n /**\n * Replay unacknowledged inputs from a given frame to resimulate client prediction\n * after applying server authority at a certain frame.\n * \n * @param startFrame - The last server-acknowledged frame\n * \n * @example\n * ```ts\n * // After applying a server correction at frame N\n * this.replayUnackedInputsFromFrame(N);\n * ```\n */\n private async replayUnackedInputsFromFrame(_startFrame: number): Promise<void> {\n // Prediction controller handles replay internally. Kept for backwards compatibility.\n }\n}\n"],"names":["Canvas","componentAnimation"],"mappings":"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"}
1
+ {"version":3,"file":"index2.js","sources":["../src/RpgClientEngine.ts"],"sourcesContent":["import Canvas from \"./components/scenes/canvas.ce\";\nimport { Context, inject } from \"@signe/di\";\nimport { signal, bootstrapCanvas, KeyboardControls, Howl, trigger } from \"canvasengine\";\nimport { AbstractWebsocket, WebSocketToken } from \"./services/AbstractSocket\";\nimport { LoadMapService, LoadMapToken } from \"./services/loadMap\";\nimport { RpgSound } from \"./Sound\";\nimport { RpgResource } from \"./Resource\";\nimport { Hooks, ModulesToken, Direction } from \"@rpgjs/common\";\n\ntype DirectionValue = \"up\" | \"down\" | \"left\" | \"right\";\nimport { load } from \"@signe/sync\";\nimport { RpgClientMap } from \"./Game/Map\"\nimport { RpgGui } from \"./Gui/Gui\";\nimport { AnimationManager } from \"./Game/AnimationManager\";\nimport { lastValueFrom, Observable } from \"rxjs\";\nimport { GlobalConfigToken } from \"./module\";\nimport * as PIXI from \"pixi.js\";\nimport { PrebuiltComponentAnimations } from \"./components/animations\";\nimport {\n PredictionController,\n type PredictionState,\n} from \"@rpgjs/common\";\n\nexport class RpgClientEngine<T = any> {\n private guiService: RpgGui;\n private webSocket: AbstractWebsocket;\n private loadMapService: LoadMapService;\n private hooks: Hooks;\n private sceneMap: RpgClientMap\n private selector: HTMLElement;\n public globalConfig: T;\n public sceneComponent: any;\n stopProcessingInput = false;\n width = signal(\"100%\");\n height = signal(\"100%\");\n spritesheets: Map<string, any> = new Map();\n sounds: Map<string, any> = new Map();\n componentAnimations: any[] = [];\n private spritesheetResolver?: (id: string) => any | Promise<any>;\n private soundResolver?: (id: string) => any | Promise<any>;\n particleSettings: {\n emitters: any[]\n } = {\n emitters: []\n }\n renderer: PIXI.Renderer;\n tick: Observable<number>;\n playerIdSignal = signal<string | null>(null);\n spriteComponentsBehind = signal<any[]>([]);\n spriteComponentsInFront = signal<any[]>([]);\n /** ID of the sprite that the camera should follow. null means follow the current player */\n cameraFollowTargetId = signal<string | null>(null);\n /** Trigger for map shake animation */\n mapShakeTrigger = trigger();\n\n private predictionEnabled = false;\n private prediction?: PredictionController<Direction>;\n private readonly SERVER_CORRECTION_THRESHOLD = 30;\n private inputFrameCounter = 0;\n private frameOffset = 0;\n // Ping/Pong for RTT measurement\n private rtt: number = 0; // Round-trip time in ms\n private pingInterval: any = null;\n private readonly PING_INTERVAL_MS = 5000; // Send ping every 5 seconds\n private lastInputTime = 0;\n\n constructor(public context: Context) {\n this.webSocket = inject(context, WebSocketToken);\n this.guiService = inject(context, RpgGui);\n this.loadMapService = inject(context, LoadMapToken);\n this.hooks = inject<Hooks>(context, ModulesToken);\n this.globalConfig = inject(context, GlobalConfigToken)\n\n if (!this.globalConfig) {\n this.globalConfig = {} as T\n }\n if (!(this.globalConfig as any).box) {\n (this.globalConfig as any).box = {\n styles: {\n backgroundColor: \"#1a1a2e\",\n backgroundOpacity: 0.9\n },\n sounds: {}\n }\n }\n\n this.addComponentAnimation({\n id: \"animation\",\n component: PrebuiltComponentAnimations.Animation\n })\n\n this.predictionEnabled = (this.globalConfig as any)?.prediction?.enabled !== false;\n this.initializePredictionController();\n }\n\n /**\n * Assigns a CanvasEngine KeyboardControls instance to the dependency injection context\n * \n * This method registers a KeyboardControls instance from CanvasEngine into the DI container,\n * making it available for injection throughout the application. The particularity is that\n * this method is automatically called when a sprite is displayed on the map, allowing the\n * controls to be automatically associated with the active sprite.\n * \n * ## Design\n * \n * - The instance is stored in the DI context under the `KeyboardControls` token\n * - It's automatically assigned when a sprite component mounts (in `character.ce`)\n * - The controls instance comes from the CanvasEngine component's directives\n * - Once registered, it can be retrieved using `inject(KeyboardControls)` from anywhere\n * \n * @param controlInstance - The CanvasEngine KeyboardControls instance to register\n * \n * @example\n * ```ts\n * // The method is automatically called when a sprite is displayed:\n * // client.setKeyboardControls(element.directives.controls)\n * \n * // Later, retrieve and use the controls instance:\n * import { Input, inject, KeyboardControls } from '@rpgjs/client'\n * \n * const controls = inject(KeyboardControls)\n * const control = controls.getControl(Input.Enter)\n * \n * if (control) {\n * console.log(control.actionName) // 'action'\n * }\n * ```\n */\n setKeyboardControls(controlInstance: any) {\n const currentValues = this.context.values['inject:' + KeyboardControls]\n this.context.values['inject:' + KeyboardControls] = {\n ...currentValues,\n values: new Map([['__default__', controlInstance]])\n }\n }\n\n async start() {\n this.sceneMap = new RpgClientMap()\n this.selector = document.body.querySelector(\"#rpg\") as HTMLElement;\n\n const { app, canvasElement } = await bootstrapCanvas(this.selector, Canvas);\n this.renderer = app.renderer as PIXI.Renderer;\n this.tick = canvasElement?.propObservables?.context['tick'].observable\n\n this.tick.subscribe(() => {\n if (Date.now() - this.lastInputTime > 100) {\n const player = this.getCurrentPlayer();\n if (!player) return;\n (this.sceneMap as any).stopMovement(player);\n }\n })\n\n\n this.hooks.callHooks(\"client-spritesheets-load\", this).subscribe();\n this.hooks.callHooks(\"client-spritesheetResolver-load\", this).subscribe();\n this.hooks.callHooks(\"client-sounds-load\", this).subscribe();\n this.hooks.callHooks(\"client-soundResolver-load\", this).subscribe();\n \n RpgSound.init(this);\n RpgResource.init(this);\n this.hooks.callHooks(\"client-gui-load\", this).subscribe();\n this.hooks.callHooks(\"client-particles-load\", this).subscribe();\n this.hooks.callHooks(\"client-componentAnimations-load\", this).subscribe();\n this.hooks.callHooks(\"client-sprite-load\", this).subscribe();\n\n await lastValueFrom(this.hooks.callHooks(\"client-engine-onStart\", this));\n\n // wondow is resize\n window.addEventListener('resize', () => {\n this.hooks.callHooks(\"client-engine-onWindowResize\", this).subscribe();\n })\n\n this.tick.subscribe((tick) => {\n this.hooks.callHooks(\"client-engine-onStep\", this, tick).subscribe();\n\n // Clean up old prediction states and input history every 60 ticks (approximately every second at 60fps)\n if (tick % 60 === 0) {\n const now = Date.now();\n this.prediction?.cleanup(now);\n this.prediction?.tryApplyPendingSnapshot();\n }\n })\n\n await this.webSocket.connection(() => {\n this.initListeners()\n this.guiService._initialize()\n });\n }\n\n private initListeners() {\n this.webSocket.on(\"sync\", (data) => {\n if (data.pId) this.playerIdSignal.set(data.pId)\n\n // Apply client-side prediction filtering and server reconciliation\n this.hooks.callHooks(\"client-sceneMap-onChanges\", this.sceneMap, { partial: data }).subscribe();\n\n load(this.sceneMap, data, true);\n });\n\n // Handle pong responses for RTT measurement\n this.webSocket.on(\"pong\", (data: { serverTick: number; clientFrame: number; clientTime: number }) => {\n const now = Date.now();\n this.rtt = now - data.clientTime;\n\n // Calculate frame offset: how many ticks ahead the server is compared to our frame counter\n // This helps us estimate which server tick corresponds to each client input frame\n const estimatedTicksInFlight = Math.floor(this.rtt / 2 / (1000 / 60)); // Estimate ticks during half RTT\n const estimatedServerTickNow = data.serverTick + estimatedTicksInFlight;\n\n // Update frame offset (only if we have inputs to calibrate with)\n if (this.inputFrameCounter > 0) {\n this.frameOffset = estimatedServerTickNow - data.clientFrame;\n }\n\n console.debug(`[Ping/Pong] RTT: ${this.rtt}ms, ServerTick: ${data.serverTick}, FrameOffset: ${this.frameOffset}`);\n });\n\n this.webSocket.on(\"changeMap\", (data) => {\n this.sceneMap.reset()\n // Reset camera follow to default (follow current player) when changing maps\n this.cameraFollowTargetId.set(null);\n this.loadScene(data.mapId);\n });\n\n this.webSocket.on(\"showComponentAnimation\", (data) => {\n const { params, object, position, id } = data;\n if (!object && position === undefined) {\n throw new Error(\"Please provide an object or x and y coordinates\");\n }\n const player = object ? this.sceneMap.getObjectById(object) : undefined;\n this.getComponentAnimation(id).displayEffect(params, player || position)\n });\n\n this.webSocket.on(\"setAnimation\", (data) => {\n const { animationName, nbTimes, object } = data;\n const player = this.sceneMap.getObjectById(object);\n player.setAnimation(animationName, nbTimes)\n })\n\n this.webSocket.on(\"playSound\", (data) => {\n const { soundId, volume, loop } = data;\n this.playSound(soundId, { volume, loop });\n });\n\n this.webSocket.on(\"stopSound\", (data) => {\n const { soundId } = data;\n this.stopSound(soundId);\n });\n\n this.webSocket.on(\"stopAllSounds\", () => {\n this.stopAllSounds();\n });\n\n this.webSocket.on(\"cameraFollow\", (data) => {\n const { targetId, smoothMove } = data;\n this.setCameraFollow(targetId, smoothMove);\n });\n\n this.webSocket.on(\"flash\", (data) => {\n const { object, type, duration, cycles, alpha, tint } = data;\n const sprite = object ? this.sceneMap.getObjectById(object) : undefined;\n if (sprite && typeof sprite.flash === 'function') {\n sprite.flash({ type, duration, cycles, alpha, tint });\n }\n });\n\n this.webSocket.on(\"shakeMap\", (data) => {\n const { intensity, duration, frequency, direction } = data || {};\n this.mapShakeTrigger.start({\n intensity,\n duration,\n frequency,\n direction\n });\n });\n\n this.webSocket.on('open', () => {\n this.hooks.callHooks(\"client-engine-onConnected\", this, this.socket).subscribe();\n // Start ping/pong for synchronization\n })\n\n this.webSocket.on('close', () => {\n this.hooks.callHooks(\"client-engine-onDisconnected\", this, this.socket).subscribe();\n // Stop ping/pong when disconnected\n this.stopPingPong();\n })\n\n this.webSocket.on('error', (error) => {\n this.hooks.callHooks(\"client-engine-onConnectError\", this, error, this.socket).subscribe();\n })\n }\n\n /**\n * Start periodic ping/pong for client-server synchronization\n * \n * Sends ping requests to the server to measure round-trip time (RTT) and\n * calculate the frame offset between client and server ticks.\n * \n * ## Design\n * \n * - Sends ping every 5 seconds\n * - Measures RTT for latency compensation\n * - Calculates frame offset to map client frames to server ticks\n * - Used for accurate server reconciliation\n * \n * @example\n * ```ts\n * // Called automatically when connection opens\n * this.startPingPong();\n * ```\n */\n private startPingPong(): void {\n // Stop existing interval if any\n this.stopPingPong();\n\n // Send initial ping immediately\n this.sendPing();\n\n // Set up periodic pings\n this.pingInterval = setInterval(() => {\n this.sendPing();\n }, this.PING_INTERVAL_MS);\n }\n\n /**\n * Stop periodic ping/pong\n * \n * Stops the ping interval when disconnecting or changing maps.\n * \n * @example\n * ```ts\n * // Called automatically when connection closes\n * this.stopPingPong();\n * ```\n */\n private stopPingPong(): void {\n if (this.pingInterval) {\n clearInterval(this.pingInterval);\n this.pingInterval = null;\n }\n }\n\n /**\n * Send a ping request to the server\n * \n * Sends current client time and frame counter to the server,\n * which will respond with its server tick for synchronization.\n * \n * @example\n * ```ts\n * // Send a ping to measure RTT\n * this.sendPing();\n * ```\n */\n private sendPing(): void {\n const clientTime = Date.now();\n const clientFrame = this.getPhysicsTick();\n\n this.webSocket.emit('ping', {\n clientTime,\n clientFrame\n });\n }\n\n private async loadScene(mapId: string) {\n this.hooks.callHooks(\"client-sceneMap-onBeforeLoading\", this.sceneMap).subscribe();\n\n // Clear client prediction states when changing maps\n this.clearClientPredictionStates();\n\n this.webSocket.updateProperties({ room: mapId })\n await this.webSocket.reconnect(() => {\n this.initListeners()\n this.guiService._initialize()\n })\n const res = await this.loadMapService.load(mapId)\n this.sceneMap.data.set(res)\n this.hooks.callHooks(\"client-sceneMap-onAfterLoading\", this.sceneMap).subscribe();\n this.sceneMap.loadPhysic()\n }\n\n addSpriteSheet<T = any>(spritesheetClass: any, id?: string): any {\n this.spritesheets.set(id || spritesheetClass.id, spritesheetClass);\n return spritesheetClass as any;\n }\n\n /**\n * Set a resolver function for spritesheets\n * \n * The resolver is called when a spritesheet is requested but not found in the cache.\n * It can be synchronous (returns directly) or asynchronous (returns a Promise).\n * The resolved spritesheet is automatically cached for future use.\n * \n * @param resolver - Function that takes a spritesheet ID and returns a spritesheet or Promise of spritesheet\n * \n * @example\n * ```ts\n * // Synchronous resolver\n * engine.setSpritesheetResolver((id) => {\n * if (id === 'dynamic-sprite') {\n * return { id: 'dynamic-sprite', image: 'path/to/image.png', framesWidth: 32, framesHeight: 32 };\n * }\n * return undefined;\n * });\n * \n * // Asynchronous resolver (loading from API)\n * engine.setSpritesheetResolver(async (id) => {\n * const response = await fetch(`/api/spritesheets/${id}`);\n * const data = await response.json();\n * return data;\n * });\n * ```\n */\n setSpritesheetResolver(resolver: (id: string) => any | Promise<any>): void {\n this.spritesheetResolver = resolver;\n }\n\n /**\n * Get a spritesheet by ID, using resolver if not found in cache\n * \n * This method first checks if the spritesheet exists in the cache.\n * If not found and a resolver is set, it calls the resolver to create the spritesheet.\n * The resolved spritesheet is automatically cached for future use.\n * \n * @param id - The spritesheet ID to retrieve\n * @returns The spritesheet if found or created, or undefined if not found and no resolver\n * @returns Promise<any> if the resolver is asynchronous\n * \n * @example\n * ```ts\n * // Synchronous usage\n * const spritesheet = engine.getSpriteSheet('my-sprite');\n * \n * // Asynchronous usage (when resolver returns Promise)\n * const spritesheet = await engine.getSpriteSheet('dynamic-sprite');\n * ```\n */\n getSpriteSheet(id: string): any | Promise<any> {\n // Check cache first\n if (this.spritesheets.has(id)) {\n return this.spritesheets.get(id);\n }\n\n // If not in cache and resolver exists, use it\n if (this.spritesheetResolver) {\n const result = this.spritesheetResolver(id);\n\n // Check if result is a Promise\n if (result instanceof Promise) {\n return result.then((spritesheet) => {\n if (spritesheet) {\n // Cache the resolved spritesheet\n this.spritesheets.set(id, spritesheet);\n }\n return spritesheet;\n });\n } else {\n // Synchronous result\n if (result) {\n // Cache the resolved spritesheet\n this.spritesheets.set(id, result);\n }\n return result;\n }\n }\n\n // No resolver and not in cache\n return undefined;\n }\n\n /**\n * Add a sound to the engine\n * \n * Adds a sound to the engine's sound cache. The sound can be:\n * - A simple object with `id` and `src` properties\n * - A Howler instance\n * - An object with a `play()` method\n * \n * If the sound has a `src` property, a Howler instance will be created automatically.\n * \n * @param sound - The sound object or Howler instance\n * @param id - Optional sound ID (if not provided, uses sound.id)\n * @returns The added sound\n * \n * @example\n * ```ts\n * // Simple sound object\n * engine.addSound({ id: 'click', src: 'click.mp3' });\n * \n * // With explicit ID\n * engine.addSound({ src: 'music.mp3' }, 'background-music');\n * ```\n */\n addSound(sound: any, id?: string): any {\n const soundId = id || sound.id;\n \n if (!soundId) {\n console.warn('Sound added without an ID. It will not be retrievable.');\n return sound;\n }\n\n // If sound has a src property, create a Howler instance\n if (sound.src && typeof sound.src === 'string') {\n const howlOptions: any = {\n src: [sound.src],\n loop: sound.loop || false,\n volume: sound.volume !== undefined ? sound.volume : 1.0,\n };\n\n const howl = new (Howl as any).Howl(howlOptions);\n this.sounds.set(soundId, howl);\n return howl;\n }\n\n // If sound already has a play method (Howler instance or custom), use it directly\n if (sound && typeof sound.play === 'function') {\n this.sounds.set(soundId, sound);\n return sound;\n }\n\n // Otherwise, store as-is\n this.sounds.set(soundId, sound);\n return sound;\n }\n\n /**\n * Set a resolver function for sounds\n * \n * The resolver is called when a sound is requested but not found in the cache.\n * It can be synchronous (returns directly) or asynchronous (returns a Promise).\n * The resolved sound is automatically cached for future use.\n * \n * @param resolver - Function that takes a sound ID and returns a sound or Promise of sound\n * \n * @example\n * ```ts\n * // Synchronous resolver\n * engine.setSoundResolver((id) => {\n * if (id === 'dynamic-sound') {\n * return { id: 'dynamic-sound', src: 'path/to/sound.mp3' };\n * }\n * return undefined;\n * });\n * \n * // Asynchronous resolver (loading from API)\n * engine.setSoundResolver(async (id) => {\n * const response = await fetch(`/api/sounds/${id}`);\n * const data = await response.json();\n * return data;\n * });\n * ```\n */\n setSoundResolver(resolver: (id: string) => any | Promise<any>): void {\n this.soundResolver = resolver;\n }\n\n /**\n * Get a sound by ID, using resolver if not found in cache\n * \n * This method first checks if the sound exists in the cache.\n * If not found and a resolver is set, it calls the resolver to create the sound.\n * The resolved sound is automatically cached for future use.\n * \n * @param id - The sound ID to retrieve\n * @returns The sound if found or created, or undefined if not found and no resolver\n * @returns Promise<any> if the resolver is asynchronous\n * \n * @example\n * ```ts\n * // Synchronous usage\n * const sound = engine.getSound('my-sound');\n * \n * // Asynchronous usage (when resolver returns Promise)\n * const sound = await engine.getSound('dynamic-sound');\n * ```\n */\n getSound(id: string): any | Promise<any> {\n // Check cache first\n if (this.sounds.has(id)) {\n return this.sounds.get(id);\n }\n\n // If not in cache and resolver exists, use it\n if (this.soundResolver) {\n const result = this.soundResolver(id);\n\n // Check if result is a Promise\n if (result instanceof Promise) {\n return result.then((sound) => {\n if (sound) {\n // Cache the resolved sound\n this.sounds.set(id, sound);\n }\n return sound;\n });\n } else {\n // Synchronous result\n if (result) {\n // Cache the resolved sound\n this.sounds.set(id, result);\n }\n return result;\n }\n }\n\n // No resolver and not in cache\n return undefined;\n }\n\n /**\n * Play a sound by its ID\n * \n * This method retrieves a sound from the cache or resolver and plays it.\n * If the sound is not found, it will attempt to resolve it using the soundResolver.\n * Uses Howler.js for audio playback instead of native Audio elements.\n * \n * @param soundId - The sound ID to play\n * @param options - Optional sound configuration\n * @param options.volume - Volume level (0.0 to 1.0, overrides sound default)\n * @param options.loop - Whether the sound should loop (overrides sound default)\n * \n * @example\n * ```ts\n * // Play a sound synchronously\n * engine.playSound('item-pickup');\n * \n * // Play a sound with volume and loop\n * engine.playSound('background-music', { volume: 0.5, loop: true });\n * \n * // Play a sound asynchronously (when resolver returns Promise)\n * await engine.playSound('dynamic-sound', { volume: 0.8 });\n * ```\n */\n async playSound(soundId: string, options?: { volume?: number; loop?: boolean }): Promise<void> {\n const sound = await this.getSound(soundId);\n if (sound && sound.play) {\n // Sound is already a Howler instance or has a play method\n const howlSoundId = sound._sounds?.[0]?._id;\n \n // Apply volume if provided\n if (options?.volume !== undefined) {\n if (howlSoundId !== undefined) {\n sound.volume(Math.max(0, Math.min(1, options.volume)), howlSoundId);\n } else {\n sound.volume(Math.max(0, Math.min(1, options.volume)));\n }\n }\n \n // Apply loop if provided\n if (options?.loop !== undefined) {\n if (howlSoundId !== undefined) {\n sound.loop(options.loop, howlSoundId);\n } else {\n sound.loop(options.loop);\n }\n }\n \n if (howlSoundId !== undefined) {\n sound.play(howlSoundId);\n } else {\n sound.play();\n }\n } else if (sound && sound.src) {\n // If sound is just a source URL, create a Howler instance and cache it\n const howlOptions: any = {\n src: [sound.src],\n loop: options?.loop !== undefined ? options.loop : (sound.loop || false),\n volume: options?.volume !== undefined ? Math.max(0, Math.min(1, options.volume)) : (sound.volume !== undefined ? sound.volume : 1.0),\n };\n\n const howl = new (Howl as any).Howl(howlOptions);\n \n // Cache the Howler instance for future use\n this.sounds.set(soundId, howl);\n \n // Play the sound\n howl.play();\n } else {\n console.warn(`Sound with id \"${soundId}\" not found or cannot be played`);\n }\n }\n\n /**\n * Stop a sound that is currently playing\n * \n * This method stops a sound that was previously started with `playSound()`.\n * \n * @param soundId - The sound ID to stop\n * \n * @example\n * ```ts\n * // Start a looping sound\n * engine.playSound('background-music', { loop: true });\n * \n * // Later, stop it\n * engine.stopSound('background-music');\n * ```\n */\n stopSound(soundId: string): void {\n const sound = this.sounds.get(soundId);\n if (sound && sound.stop) {\n sound.stop();\n } else {\n console.warn(`Sound with id \"${soundId}\" not found or cannot be stopped`);\n }\n }\n\n /**\n * Stop all currently playing sounds\n * \n * This method stops all sounds that are currently playing.\n * Useful when changing maps to prevent sound overlap.\n * \n * @example\n * ```ts\n * // Stop all sounds\n * engine.stopAllSounds();\n * ```\n */\n stopAllSounds(): void {\n this.sounds.forEach((sound) => {\n if (sound && sound.stop) {\n sound.stop();\n }\n });\n }\n\n /**\n * Set the camera to follow a specific sprite\n * \n * This method changes which sprite the camera viewport should follow.\n * The camera will smoothly animate to the target sprite if smoothMove options are provided.\n * \n * ## Design\n * \n * The camera follow target is stored in a signal that is read by sprite components.\n * Each sprite checks if it should be followed by comparing its ID with the target ID.\n * When smoothMove options are provided, the viewport animation is handled by CanvasEngine's\n * viewport system.\n * \n * @param targetId - The ID of the sprite to follow. Set to null to follow the current player\n * @param smoothMove - Animation options. Can be a boolean (default: true) or an object with time and ease\n * @param smoothMove.time - Duration of the animation in milliseconds (optional)\n * @param smoothMove.ease - Easing function name from https://easings.net (optional)\n * \n * @example\n * ```ts\n * // Follow another player with default smooth animation\n * engine.setCameraFollow(otherPlayerId, true);\n * \n * // Follow an event with custom smooth animation\n * engine.setCameraFollow(eventId, {\n * time: 1000,\n * ease: \"easeInOutQuad\"\n * });\n * \n * // Follow without animation (instant)\n * engine.setCameraFollow(targetId, false);\n * \n * // Return to following current player\n * engine.setCameraFollow(null);\n * ```\n */\n setCameraFollow(\n targetId: string | null,\n smoothMove?: boolean | { time?: number; ease?: string }\n ): void {\n // Store smoothMove options for potential future use with viewport animation\n // For now, we just set the target ID and let CanvasEngine handle the viewport follow\n // The smoothMove options could be used to configure viewport animation if CanvasEngine supports it\n this.cameraFollowTargetId.set(targetId);\n \n // If smoothMove is an object, we could store it for viewport configuration\n // This would require integration with CanvasEngine's viewport animation system\n if (typeof smoothMove === \"object\" && smoothMove !== null) {\n // Future: Apply smoothMove.time and smoothMove.ease to viewport animation\n // For now, CanvasEngine handles viewport following automatically\n }\n }\n\n addParticle(particle: any) {\n this.particleSettings.emitters.push(particle)\n return particle;\n }\n\n /**\n * Add a component to render behind sprites\n * Components added with this method will be displayed with a lower z-index than the sprite\n * \n * @param component - The component to add behind sprites\n * @returns The added component\n * \n * @example\n * ```ts\n * // Add a shadow component behind all sprites\n * engine.addSpriteComponentBehind(ShadowComponent);\n * ```\n */\n addSpriteComponentBehind(component: any) {\n this.spriteComponentsBehind.update((components: any[]) => [...components, component])\n return component\n }\n\n /**\n * Add a component to render in front of sprites\n * Components added with this method will be displayed with a higher z-index than the sprite\n * \n * @param component - The component to add in front of sprites\n * @returns The added component\n * \n * @example\n * ```ts\n * // Add a health bar component in front of all sprites\n * engine.addSpriteComponentInFront(HealthBarComponent);\n * ```\n */\n addSpriteComponentInFront(component: any) {\n this.spriteComponentsInFront.update((components: any[]) => [...components, component])\n return component\n }\n\n /**\n * Add a component animation to the engine\n * \n * Component animations are temporary visual effects that can be displayed\n * on sprites or objects, such as hit indicators, spell effects, or status animations.\n * \n * @param componentAnimation - The component animation configuration\n * @param componentAnimation.id - Unique identifier for the animation\n * @param componentAnimation.component - The component function to render\n * @returns The added component animation configuration\n * \n * @example\n * ```ts\n * // Add a hit animation component\n * engine.addComponentAnimation({\n * id: 'hit',\n * component: HitComponent\n * });\n * \n * // Add an explosion effect component\n * engine.addComponentAnimation({\n * id: 'explosion',\n * component: ExplosionComponent\n * });\n * ```\n */\n addComponentAnimation(componentAnimation: {\n component: any,\n id: string\n }) {\n const instance = new AnimationManager()\n this.componentAnimations.push({\n id: componentAnimation.id,\n component: componentAnimation.component,\n instance: instance,\n current: instance.current\n })\n return componentAnimation;\n }\n\n /**\n * Get a component animation by its ID\n * \n * Retrieves the EffectManager instance for a specific component animation,\n * which can be used to display the animation on sprites or objects.\n * \n * @param id - The unique identifier of the component animation\n * @returns The EffectManager instance for the animation\n * @throws Error if the component animation is not found\n * \n * @example\n * ```ts\n * // Get the hit animation and display it\n * const hitAnimation = engine.getComponentAnimation('hit');\n * hitAnimation.displayEffect({ text: \"Critical!\" }, player);\n * ```\n */\n getComponentAnimation(id: string): AnimationManager {\n const componentAnimation = this.componentAnimations.find((componentAnimation) => componentAnimation.id === id)\n if (!componentAnimation) {\n throw new Error(`Component animation with id ${id} not found`)\n }\n return componentAnimation.instance\n }\n\n async processInput({ input }: { input: Direction }) {\n const timestamp = Date.now();\n let frame: number;\n let tick: number;\n if (this.predictionEnabled && this.prediction) {\n const meta = this.prediction.recordInput(input, timestamp);\n frame = meta.frame;\n tick = meta.tick;\n } else {\n frame = ++this.inputFrameCounter;\n tick = this.getPhysicsTick();\n }\n this.hooks.callHooks(\"client-engine-onInput\", this, { input, playerId: this.playerId }).subscribe();\n\n this.webSocket.emit('move', {\n input,\n timestamp,\n frame,\n tick,\n });\n\n const currentPlayer = this.sceneMap.getCurrentPlayer();\n if (currentPlayer) {\n (this.sceneMap as any).moveBody(currentPlayer, input);\n }\n this.lastInputTime = Date.now();\n const myId = this.playerIdSignal();\n\n }\n\n processAction({ action }: { action: number }) {\n if (this.stopProcessingInput) return;\n this.hooks.callHooks(\"client-engine-onInput\", this, { input: 'action', playerId: this.playerId }).subscribe();\n this.webSocket.emit('action', { action })\n }\n\n get PIXI() {\n return PIXI\n }\n\n get socket() {\n return this.webSocket\n }\n\n get playerId() {\n return this.playerIdSignal()\n }\n\n get scene() {\n return this.sceneMap\n }\n\n private getPhysicsTick(): number {\n return this.sceneMap?.getTick?.() ?? 0;\n }\n\n private getLocalPlayerState(): PredictionState<Direction> {\n const currentPlayer = this.sceneMap?.getCurrentPlayer();\n if (!currentPlayer) {\n return { x: 0, y: 0, direction: Direction.Down };\n }\n const topLeft = this.sceneMap.getBodyPosition(currentPlayer.id, \"top-left\");\n const x = topLeft?.x ?? currentPlayer.x();\n const y = topLeft?.y ?? currentPlayer.y();\n const direction = currentPlayer.direction();\n return { x, y, direction };\n }\n\n private applyAuthoritativeState(state: PredictionState<Direction>): void {\n const player = this.sceneMap?.getCurrentPlayer();\n if (!player) return;\n const hitbox = typeof player.hitbox === \"function\" ? player.hitbox() : player.hitbox;\n const width = hitbox?.w ?? 0;\n const height = hitbox?.h ?? 0;\n const updated = this.sceneMap.updateHitbox(player.id, state.x, state.y, width, height);\n if (!updated) {\n this.sceneMap.setBodyPosition(player.id, state.x, state.y, \"top-left\");\n }\n player.x.set(Math.round(state.x));\n player.y.set(Math.round(state.y));\n if (state.direction) {\n player.changeDirection(state.direction);\n }\n }\n\n private initializePredictionController(): void {\n if (!this.predictionEnabled) {\n this.prediction = undefined;\n return;\n }\n this.prediction = new PredictionController<Direction>({\n correctionThreshold: (this.globalConfig as any)?.prediction?.correctionThreshold ?? this.SERVER_CORRECTION_THRESHOLD,\n historyTtlMs: (this.globalConfig as any)?.prediction?.historyTtlMs ?? 2000,\n getPhysicsTick: () => this.getPhysicsTick(),\n getCurrentState: () => this.getLocalPlayerState(),\n setAuthoritativeState: (state) => this.applyAuthoritativeState(state),\n });\n }\n\n getCurrentPlayer() {\n return this.sceneMap.getCurrentPlayer()\n }\n\n /**\n * Clear client prediction states for cleanup\n * \n * Removes old prediction states and input history to prevent memory leaks.\n * Should be called when changing maps or disconnecting.\n * \n * @example\n * ```ts\n * // Clear prediction states when changing maps\n * engine.clearClientPredictionStates();\n * ```\n */\n clearClientPredictionStates() {\n this.initializePredictionController();\n this.frameOffset = 0;\n this.inputFrameCounter = 0;\n }\n\n /**\n * Trigger a flash animation on a sprite\n * \n * This method allows you to trigger a flash effect on any sprite from client-side code.\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 is applied directly to the sprite object using its flash trigger.\n * This is useful for client-side visual feedback, UI interactions, or local effects\n * that don't need to be synchronized with the server.\n * \n * @param spriteId - The ID of the sprite to flash. If not provided, flashes the current player\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 * // Flash the current player with default settings\n * engine.flash();\n * \n * // Flash a specific sprite with red tint\n * engine.flash('sprite-id', { type: 'tint', tint: 0xff0000 });\n * \n * // Flash with both alpha and tint for dramatic effect\n * engine.flash(undefined, { \n * type: 'both', \n * alpha: 0.5, \n * tint: 0xff0000,\n * duration: 200,\n * cycles: 2\n * });\n * \n * // Quick damage flash on current player\n * engine.flash(undefined, { \n * type: 'tint', \n * tint: 'red', \n * duration: 150,\n * cycles: 1\n * });\n * ```\n */\n flash(\n spriteId?: string,\n options?: {\n type?: 'alpha' | 'tint' | 'both';\n duration?: number;\n cycles?: number;\n alpha?: number;\n tint?: number | string;\n }\n ): void {\n const targetId = spriteId || this.playerId;\n if (!targetId) return;\n\n const sprite = this.sceneMap.getObjectById(targetId);\n if (sprite && typeof sprite.flash === 'function') {\n sprite.flash(options);\n }\n }\n\n private applyServerAck(ack: { frame: number; serverTick?: number; x?: number; y?: number; direction?: Direction }) {\n if (this.predictionEnabled && this.prediction) {\n this.prediction.applyServerAck({\n frame: ack.frame,\n serverTick: ack.serverTick,\n state:\n typeof ack.x === \"number\" && typeof ack.y === \"number\"\n ? { x: ack.x, y: ack.y, direction: ack.direction }\n : undefined,\n });\n return;\n }\n\n if (typeof ack.x !== \"number\" || typeof ack.y !== \"number\") {\n return;\n }\n const player = this.getCurrentPlayer();\n const myId = this.playerIdSignal();\n if (!player || !myId) {\n return;\n }\n const hitbox = typeof player.hitbox === \"function\" ? player.hitbox() : player.hitbox;\n const width = hitbox?.w ?? 0;\n const height = hitbox?.h ?? 0;\n const updated = this.sceneMap.updateHitbox(myId, ack.x, ack.y, width, height);\n if (!updated) {\n this.sceneMap.setBodyPosition(myId, ack.x, ack.y, \"top-left\");\n }\n player.x.set(Math.round(ack.x));\n player.y.set(Math.round(ack.y));\n if (ack.direction) {\n player.changeDirection(ack.direction);\n }\n }\n\n /**\n * Replay unacknowledged inputs from a given frame to resimulate client prediction\n * after applying server authority at a certain frame.\n * \n * @param startFrame - The last server-acknowledged frame\n * \n * @example\n * ```ts\n * // After applying a server correction at frame N\n * this.replayUnackedInputsFromFrame(N);\n * ```\n */\n private async replayUnackedInputsFromFrame(_startFrame: number): Promise<void> {\n // Prediction controller handles replay internally. Kept for backwards compatibility.\n }\n}\n"],"names":["Canvas","componentAnimation"],"mappings":"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"}
package/dist/index20.js CHANGED
@@ -1,4 +1,17 @@
1
- const KeyboardControls = "KeyboardControlsToken";
1
+ import { KeyboardControls } from 'canvasengine';
2
+
3
+ var Control = /* @__PURE__ */ ((Control2) => {
4
+ Control2["Action"] = "action";
5
+ Control2["Attack"] = "attack";
6
+ Control2["Defense"] = "defense";
7
+ Control2["Skill"] = "skill";
8
+ Control2["Back"] = "back";
9
+ Control2[Control2["Up"] = 1] = "Up";
10
+ Control2[Control2["Down"] = 3] = "Down";
11
+ Control2[Control2["Right"] = 2] = "Right";
12
+ Control2[Control2["Left"] = 4] = "Left";
13
+ return Control2;
14
+ })(Control || {});
2
15
  function provideKeyboardControls() {
3
16
  return {
4
17
  provide: KeyboardControls,
@@ -6,5 +19,5 @@ function provideKeyboardControls() {
6
19
  };
7
20
  }
8
21
 
9
- export { KeyboardControls, provideKeyboardControls };
22
+ export { Control, provideKeyboardControls };
10
23
  //# sourceMappingURL=index20.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index20.js","sources":["../src/services/keyboardControls.ts"],"sourcesContent":["export const KeyboardControls = \"KeyboardControlsToken\";\n\nexport function provideKeyboardControls() {\n return {\n provide: KeyboardControls,\n useValue: null,\n };\n}"],"names":[],"mappings":"AAAO,MAAM,gBAAA,GAAmB;AAEzB,SAAS,uBAAA,GAA0B;AACxC,EAAA,OAAO;AAAA,IACL,OAAA,EAAS,gBAAA;AAAA,IACT,QAAA,EAAU;AAAA,GACZ;AACF;;;;"}
1
+ {"version":3,"file":"index20.js","sources":["../src/services/keyboardControls.ts"],"sourcesContent":["import { KeyboardControls } from \"canvasengine\";\n\nexport enum Control {\n Action = 'action',\n Attack = 'attack',\n Defense = 'defense',\n Skill = 'skill',\n Back = 'back',\n Up = 1,\n Down = 3,\n Right = 2,\n Left = 4\n}\n\nexport function provideKeyboardControls() {\n return {\n provide: KeyboardControls,\n useValue: null,\n };\n}"],"names":["Control"],"mappings":";;AAEO,IAAK,OAAA,qBAAAA,QAAAA,KAAL;AACL,EAAAA,SAAA,QAAA,CAAA,GAAS,QAAA;AACT,EAAAA,SAAA,QAAA,CAAA,GAAS,QAAA;AACT,EAAAA,SAAA,SAAA,CAAA,GAAU,SAAA;AACV,EAAAA,SAAA,OAAA,CAAA,GAAQ,OAAA;AACR,EAAAA,SAAA,MAAA,CAAA,GAAO,MAAA;AACP,EAAAA,QAAAA,CAAAA,QAAAA,CAAA,QAAK,CAAA,CAAA,GAAL,IAAA;AACA,EAAAA,QAAAA,CAAAA,QAAAA,CAAA,UAAO,CAAA,CAAA,GAAP,MAAA;AACA,EAAAA,QAAAA,CAAAA,QAAAA,CAAA,WAAQ,CAAA,CAAA,GAAR,OAAA;AACA,EAAAA,QAAAA,CAAAA,QAAAA,CAAA,UAAO,CAAA,CAAA,GAAP,MAAA;AATU,EAAA,OAAAA,QAAAA;AAAA,CAAA,EAAA,OAAA,IAAA,EAAA;AAYL,SAAS,uBAAA,GAA0B;AACxC,EAAA,OAAO;AAAA,IACL,OAAA,EAAS,gBAAA;AAAA,IACT,QAAA,EAAU;AAAA,GACZ;AACF;;;;"}