@rpgjs/client 5.0.0-alpha.23 → 5.0.0-alpha.24

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 (79) hide show
  1. package/dist/RpgClientEngine.d.ts +35 -115
  2. package/dist/components/gui/mobile/index.d.ts +1 -1
  3. package/dist/index10.js +1 -1
  4. package/dist/index11.js +2 -2
  5. package/dist/index12.js +2 -2
  6. package/dist/index14.js +1 -2
  7. package/dist/index14.js.map +1 -1
  8. package/dist/index15.js +4 -4
  9. package/dist/index16.js.map +1 -1
  10. package/dist/index17.js.map +1 -1
  11. package/dist/index2.js +85 -159
  12. package/dist/index2.js.map +1 -1
  13. package/dist/index20.js.map +1 -1
  14. package/dist/index22.js +1 -1
  15. package/dist/index25.js +2 -2
  16. package/dist/index25.js.map +1 -1
  17. package/dist/index26.js +2 -3
  18. package/dist/index26.js.map +1 -1
  19. package/dist/index28.js +2 -2
  20. package/dist/index28.js.map +1 -1
  21. package/dist/index3.js +1 -1
  22. package/dist/index31.js +2622 -42
  23. package/dist/index31.js.map +1 -1
  24. package/dist/index32.js +88 -2607
  25. package/dist/index32.js.map +1 -1
  26. package/dist/index33.js +64 -107
  27. package/dist/index33.js.map +1 -1
  28. package/dist/index34.js +12 -62
  29. package/dist/index34.js.map +1 -1
  30. package/dist/index35.js +24 -18
  31. package/dist/index35.js.map +1 -1
  32. package/dist/index36.js +87 -19
  33. package/dist/index36.js.map +1 -1
  34. package/dist/index37.js +20 -74
  35. package/dist/index37.js.map +1 -1
  36. package/dist/index38.js +18 -35
  37. package/dist/index38.js.map +1 -1
  38. package/dist/index39.js +137 -20
  39. package/dist/index39.js.map +1 -1
  40. package/dist/index4.js +1 -1
  41. package/dist/index40.js +9 -133
  42. package/dist/index40.js.map +1 -1
  43. package/dist/index41.js +3 -41
  44. package/dist/index41.js.map +1 -1
  45. package/dist/index42.js +536 -1
  46. package/dist/index42.js.map +1 -1
  47. package/dist/index43.js +30 -183
  48. package/dist/index43.js.map +1 -1
  49. package/dist/index44.js +9 -501
  50. package/dist/index44.js.map +1 -1
  51. package/dist/index45.js +6 -334
  52. package/dist/index45.js.map +1 -1
  53. package/dist/index46.js +325 -30
  54. package/dist/index46.js.map +1 -1
  55. package/dist/index47.js +3680 -67
  56. package/dist/index47.js.map +1 -1
  57. package/dist/index48.js +75 -10
  58. package/dist/index48.js.map +1 -1
  59. package/dist/index49.js +186 -6
  60. package/dist/index49.js.map +1 -1
  61. package/dist/index50.js +499 -3685
  62. package/dist/index50.js.map +1 -1
  63. package/dist/index53.js +1 -1
  64. package/dist/index8.js +6 -1
  65. package/dist/index8.js.map +1 -1
  66. package/dist/index9.js +1 -2
  67. package/dist/index9.js.map +1 -1
  68. package/package.json +9 -9
  69. package/src/Gui/Gui.ts +0 -1
  70. package/src/RpgClientEngine.ts +106 -176
  71. package/src/components/character.ce +2 -3
  72. package/src/components/gui/mobile/index.ts +1 -1
  73. package/src/components/gui/mobile/mobile.ce +73 -88
  74. package/src/components/prebuilt/light-halo.ce +2 -71
  75. package/src/components/scenes/canvas.ce +0 -10
  76. package/src/components/scenes/event-layer.ce +1 -0
  77. package/src/module.ts +6 -1
  78. package/dist/Game/TransitionManager.d.ts +0 -56
  79. package/src/Game/TransitionManager.ts +0 -75
package/dist/index2.js CHANGED
@@ -10,8 +10,7 @@ import { load } from './index28.js';
10
10
  import { RpgClientMap } from './index29.js';
11
11
  import { RpgGui } from './index9.js';
12
12
  import { AnimationManager } from './index30.js';
13
- import { TransitionManager } from './index31.js';
14
- import { lastValueFrom } from 'rxjs';
13
+ import { BehaviorSubject, lastValueFrom, combineLatest, filter, take, switchMap } from 'rxjs';
15
14
  import { GlobalConfigToken } from './index8.js';
16
15
  import * as PIXI from 'pixi.js';
17
16
  import { PrebuiltComponentAnimations } from './index12.js';
@@ -25,7 +24,6 @@ class RpgClientEngine {
25
24
  this.spritesheets = /* @__PURE__ */ new Map();
26
25
  this.sounds = /* @__PURE__ */ new Map();
27
26
  this.componentAnimations = [];
28
- this.transitions = [];
29
27
  this.particleSettings = {
30
28
  emitters: []
31
29
  };
@@ -36,6 +34,7 @@ class RpgClientEngine {
36
34
  this.cameraFollowTargetId = signal(null);
37
35
  /** Trigger for map shake animation */
38
36
  this.mapShakeTrigger = trigger();
37
+ this.controlsReady = signal(void 0);
39
38
  this.predictionEnabled = false;
40
39
  this.SERVER_CORRECTION_THRESHOLD = 30;
41
40
  this.inputFrameCounter = 0;
@@ -47,6 +46,11 @@ class RpgClientEngine {
47
46
  this.PING_INTERVAL_MS = 5e3;
48
47
  // Send ping every 5 seconds
49
48
  this.lastInputTime = 0;
49
+ // Track map loading state for onAfterLoading hook using RxJS
50
+ this.mapLoadCompleted$ = new BehaviorSubject(false);
51
+ this.playerIdReceived$ = new BehaviorSubject(false);
52
+ this.playersReceived$ = new BehaviorSubject(false);
53
+ this.eventsReceived$ = new BehaviorSubject(false);
50
54
  this.webSocket = inject(WebSocketToken);
51
55
  this.guiService = inject(RpgGui);
52
56
  this.loadMapService = inject(LoadMapToken);
@@ -110,6 +114,7 @@ class RpgClientEngine {
110
114
  ...currentValues,
111
115
  values: /* @__PURE__ */ new Map([["__default__", controlInstance]])
112
116
  };
117
+ this.controlsReady.set(true);
113
118
  }
114
119
  async start() {
115
120
  this.sceneMap = new RpgClientMap();
@@ -133,7 +138,6 @@ class RpgClientEngine {
133
138
  this.hooks.callHooks("client-gui-load", this).subscribe();
134
139
  this.hooks.callHooks("client-particles-load", this).subscribe();
135
140
  this.hooks.callHooks("client-componentAnimations-load", this).subscribe();
136
- this.hooks.callHooks("client-transitions-load", this).subscribe();
137
141
  this.hooks.callHooks("client-sprite-load", this).subscribe();
138
142
  await lastValueFrom(this.hooks.callHooks("client-engine-onStart", this));
139
143
  window.addEventListener("resize", () => {
@@ -154,9 +158,20 @@ class RpgClientEngine {
154
158
  }
155
159
  initListeners() {
156
160
  this.webSocket.on("sync", (data) => {
157
- if (data.pId) this.playerIdSignal.set(data.pId);
161
+ if (data.pId) {
162
+ this.playerIdSignal.set(data.pId);
163
+ this.playerIdReceived$.next(true);
164
+ }
158
165
  this.hooks.callHooks("client-sceneMap-onChanges", this.sceneMap, { partial: data }).subscribe();
159
166
  load(this.sceneMap, data, true);
167
+ const players = data.players || this.sceneMap.players();
168
+ if (players && Object.keys(players).length > 0) {
169
+ this.playersReceived$.next(true);
170
+ }
171
+ const events = data.events || this.sceneMap.events();
172
+ if (events !== void 0) {
173
+ this.eventsReceived$.next(true);
174
+ }
160
175
  });
161
176
  this.webSocket.on("pong", (data) => {
162
177
  const now = Date.now();
@@ -292,8 +307,16 @@ class RpgClientEngine {
292
307
  });
293
308
  }
294
309
  async loadScene(mapId) {
295
- this.hooks.callHooks("client-sceneMap-onBeforeLoading", this.sceneMap).subscribe();
310
+ await lastValueFrom(this.hooks.callHooks("client-sceneMap-onBeforeLoading", this.sceneMap));
296
311
  this.clearClientPredictionStates();
312
+ this.mapLoadCompleted$.next(false);
313
+ this.playerIdReceived$.next(false);
314
+ this.playersReceived$.next(false);
315
+ this.eventsReceived$.next(false);
316
+ if (this.onAfterLoadingSubscription) {
317
+ this.onAfterLoadingSubscription.unsubscribe();
318
+ }
319
+ this.setupOnAfterLoadingObserver();
297
320
  this.webSocket.updateProperties({ room: mapId });
298
321
  await this.webSocket.reconnect(() => {
299
322
  this.initListeners();
@@ -301,7 +324,18 @@ class RpgClientEngine {
301
324
  });
302
325
  const res = await this.loadMapService.load(mapId);
303
326
  this.sceneMap.data.set(res);
304
- this.hooks.callHooks("client-sceneMap-onAfterLoading", this.sceneMap).subscribe();
327
+ if (this.playerIdSignal()) {
328
+ this.playerIdReceived$.next(true);
329
+ }
330
+ const players = this.sceneMap.players();
331
+ if (players && Object.keys(players).length > 0) {
332
+ this.playersReceived$.next(true);
333
+ }
334
+ const events = this.sceneMap.events();
335
+ if (events !== void 0) {
336
+ this.eventsReceived$.next(true);
337
+ }
338
+ this.mapLoadCompleted$.next(true);
305
339
  this.sceneMap.loadPhysic();
306
340
  }
307
341
  addSpriteSheet(spritesheetClass, id) {
@@ -788,147 +822,13 @@ class RpgClientEngine {
788
822
  }
789
823
  return componentAnimation.instance;
790
824
  }
791
- /**
792
- * Add a transition to the engine
793
- *
794
- * Transitions are screen effects that can be displayed during scene changes,
795
- * map loading, or any other moment where a visual transition is needed.
796
- * They are displayed on top of the entire canvas and can have custom props
797
- * that can be functions (similar to ComponentAnimation).
798
- *
799
- * @param transition - The transition configuration
800
- * @param transition.id - Unique identifier for the transition
801
- * @param transition.component - The component function to render
802
- * @param transition.props - Optional props to pass to the component (can be a function)
803
- * @returns The added transition configuration
804
- *
805
- * @example
806
- * ```ts
807
- * // Add a fade transition
808
- * engine.addTransition({
809
- * id: 'fade',
810
- * component: FadeComponent
811
- * });
812
- *
813
- * // Add a transition with props
814
- * engine.addTransition({
815
- * id: 'slide',
816
- * component: SlideComponent,
817
- * props: { direction: 'left', duration: 500 }
818
- * });
819
- *
820
- * // Add a transition with function props
821
- * engine.addTransition({
822
- * id: 'custom',
823
- * component: CustomTransition,
824
- * props: (engine) => ({ width: engine.width(), height: engine.height() })
825
- * });
826
- * ```
827
- */
828
- addTransition(transition) {
829
- const instance = new TransitionManager();
830
- this.transitions.push({
831
- id: transition.id,
832
- component: transition.component,
833
- props: transition.props,
834
- instance,
835
- current: instance.current
836
- });
837
- return transition;
838
- }
839
- /**
840
- * Remove a transition from the engine
841
- *
842
- * Removes a transition by its ID. This will not affect any currently
843
- * running transitions, only prevent new ones from being started.
844
- *
845
- * @param id - The unique identifier of the transition to remove
846
- * @returns true if the transition was found and removed, false otherwise
847
- *
848
- * @example
849
- * ```ts
850
- * // Remove a transition
851
- * engine.removeTransition('fade');
852
- * ```
853
- */
854
- removeTransition(id) {
855
- const index = this.transitions.findIndex((transition) => transition.id === id);
856
- if (index !== -1) {
857
- this.transitions.splice(index, 1);
858
- return true;
859
- }
860
- return false;
861
- }
862
- /**
863
- * Modify an existing transition
864
- *
865
- * Updates the component or props of an existing transition. This will
866
- * not affect any currently running transitions, only future ones.
867
- *
868
- * @param id - The unique identifier of the transition to modify
869
- * @param updates - The updates to apply (component and/or props)
870
- * @returns true if the transition was found and modified, false otherwise
871
- *
872
- * @example
873
- * ```ts
874
- * // Update transition props
875
- * engine.modifyTransition('fade', {
876
- * props: { duration: 2000, color: 'white' }
877
- * });
878
- *
879
- * // Update transition component
880
- * engine.modifyTransition('fade', {
881
- * component: NewFadeComponent
882
- * });
883
- * ```
884
- */
885
- modifyTransition(id, updates) {
886
- const transition = this.transitions.find((transition2) => transition2.id === id);
887
- if (!transition) {
888
- return false;
889
- }
890
- if (updates.component !== void 0) {
891
- transition.component = updates.component;
892
- }
893
- if (updates.props !== void 0) {
894
- transition.props = updates.props;
895
- }
896
- return true;
897
- }
898
- /**
899
- * Get a transition by its ID
900
- *
901
- * Retrieves the TransitionManager instance for a specific transition,
902
- * which can be used to start the transition.
903
- *
904
- * @param id - The unique identifier of the transition
905
- * @returns The TransitionManager instance for the transition
906
- * @throws Error if the transition is not found
907
- *
908
- * @example
909
- * ```ts
910
- * // Get a transition and start it
911
- * const fadeTransition = engine.getTransition('fade');
912
- * fadeTransition.start({ duration: 1000 });
913
- * ```
914
- */
915
- getTransition(id) {
916
- const transition = this.transitions.find((transition2) => transition2.id === id);
917
- if (!transition) {
918
- throw new Error(`Transition with id ${id} not found`);
919
- }
920
- return transition.instance;
921
- }
922
825
  /**
923
826
  * Start a transition
924
827
  *
925
- * Convenience method to start a transition by its ID. This combines
926
- * getTransition and start into a single call. The transition will
927
- * automatically receive an onFinish callback to remove itself when done.
828
+ * Convenience method to display a transition by its ID using the GUI system.
928
829
  *
929
830
  * @param id - The unique identifier of the transition to start
930
- * @param props - Additional props to pass to the transition component
931
- * @returns The created transition object
831
+ * @param props - Props to pass to the transition component
932
832
  *
933
833
  * @example
934
834
  * ```ts
@@ -943,23 +843,10 @@ class RpgClientEngine {
943
843
  * ```
944
844
  */
945
845
  startTransition(id, props = {}) {
946
- const transition = this.transitions.find((t) => t.id === id);
947
- if (!transition) {
948
- throw new Error(`Transition with id ${id} not found`);
949
- }
950
- let baseProps = {};
951
- if (transition.props) {
952
- if (typeof transition.props === "function") {
953
- baseProps = transition.props(this);
954
- } else {
955
- baseProps = transition.props;
956
- }
846
+ if (!this.guiService.exists(id)) {
847
+ throw new Error(`Transition with id ${id} not found. Make sure to add it using engine.addTransition() or in your module's transitions property.`);
957
848
  }
958
- const finalProps = {
959
- ...baseProps,
960
- ...props
961
- };
962
- return transition.instance.start(finalProps);
849
+ this.guiService.display(id, props);
963
850
  }
964
851
  async processInput({ input }) {
965
852
  const timestamp = Date.now();
@@ -1050,6 +937,45 @@ class RpgClientEngine {
1050
937
  getCurrentPlayer() {
1051
938
  return this.sceneMap.getCurrentPlayer();
1052
939
  }
940
+ /**
941
+ * Setup RxJS observer to wait for all conditions before calling onAfterLoading hook
942
+ *
943
+ * This method uses RxJS `combineLatest` to wait for all conditions to be met,
944
+ * regardless of the order in which they arrive:
945
+ * 1. The map loading is completed (loadMapService.load is finished)
946
+ * 2. We received a player ID (pId)
947
+ * 3. Players array has at least one element
948
+ * 4. Events property is present in the sync data
949
+ *
950
+ * Once all conditions are met, it uses `switchMap` to call the onAfterLoading hook once.
951
+ *
952
+ * ## Design
953
+ *
954
+ * Uses BehaviorSubjects to track each condition state, allowing events to arrive
955
+ * in any order. The `combineLatest` operator waits until all observables emit `true`,
956
+ * then `take(1)` ensures the hook is called only once, and `switchMap` handles
957
+ * the hook execution.
958
+ *
959
+ * @example
960
+ * ```ts
961
+ * // Called automatically in loadScene to setup the observer
962
+ * this.setupOnAfterLoadingObserver();
963
+ * ```
964
+ */
965
+ setupOnAfterLoadingObserver() {
966
+ this.onAfterLoadingSubscription = combineLatest([
967
+ this.mapLoadCompleted$.pipe(filter((completed) => completed === true)),
968
+ this.playerIdReceived$.pipe(filter((received) => received === true)),
969
+ this.playersReceived$.pipe(filter((received) => received === true)),
970
+ this.eventsReceived$.pipe(filter((received) => received === true))
971
+ ]).pipe(
972
+ take(1),
973
+ // Only execute once when all conditions are met
974
+ switchMap(() => {
975
+ return this.hooks.callHooks("client-sceneMap-onAfterLoading", this.sceneMap);
976
+ })
977
+ ).subscribe();
978
+ }
1053
979
  /**
1054
980
  * Clear client prediction states for cleanup
1055
981
  *