@rpgjs/client 5.0.0-alpha.10 → 5.0.0-alpha.12

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 (90) hide show
  1. package/dist/Game/Map.d.ts +4 -0
  2. package/dist/Gui/Gui.d.ts +128 -5
  3. package/dist/RpgClient.d.ts +35 -5
  4. package/dist/RpgClientEngine.d.ts +3 -0
  5. package/dist/components/gui/index.d.ts +3 -3
  6. package/dist/index.d.ts +2 -0
  7. package/dist/index.js +7 -5
  8. package/dist/index.js.map +1 -1
  9. package/dist/index10.js +150 -4
  10. package/dist/index10.js.map +1 -1
  11. package/dist/index11.js +20 -7
  12. package/dist/index11.js.map +1 -1
  13. package/dist/index12.js +6 -8
  14. package/dist/index12.js.map +1 -1
  15. package/dist/index13.js +11 -14
  16. package/dist/index13.js.map +1 -1
  17. package/dist/index14.js +8 -100
  18. package/dist/index14.js.map +1 -1
  19. package/dist/index15.js +141 -35
  20. package/dist/index15.js.map +1 -1
  21. package/dist/index16.js +42 -0
  22. package/dist/index16.js.map +1 -1
  23. package/dist/index17.js +48 -7
  24. package/dist/index17.js.map +1 -1
  25. package/dist/index18.js +5 -383
  26. package/dist/index18.js.map +1 -1
  27. package/dist/index19.js +384 -28
  28. package/dist/index19.js.map +1 -1
  29. package/dist/index2.js +32 -13
  30. package/dist/index2.js.map +1 -1
  31. package/dist/index20.js +31 -17
  32. package/dist/index20.js.map +1 -1
  33. package/dist/index21.js +17 -2413
  34. package/dist/index21.js.map +1 -1
  35. package/dist/index22.js +2587 -88
  36. package/dist/index22.js.map +1 -1
  37. package/dist/index23.js +108 -103
  38. package/dist/index23.js.map +1 -1
  39. package/dist/index29.js +22 -3
  40. package/dist/index29.js.map +1 -1
  41. package/dist/index3.js +2 -2
  42. package/dist/index30.js +1 -318
  43. package/dist/index30.js.map +1 -1
  44. package/dist/index31.js +332 -24
  45. package/dist/index31.js.map +1 -1
  46. package/dist/index32.js +24 -8
  47. package/dist/index32.js.map +1 -1
  48. package/dist/index33.js +4 -4
  49. package/dist/index33.js.map +1 -1
  50. package/dist/index34.js +8 -9
  51. package/dist/index34.js.map +1 -1
  52. package/dist/index35.js +9 -4400
  53. package/dist/index35.js.map +1 -1
  54. package/dist/index36.js +4394 -307
  55. package/dist/index36.js.map +1 -1
  56. package/dist/index37.js +307 -165
  57. package/dist/index37.js.map +1 -1
  58. package/dist/index38.js +162 -489
  59. package/dist/index38.js.map +1 -1
  60. package/dist/index39.js +496 -56
  61. package/dist/index39.js.map +1 -1
  62. package/dist/index4.js +2 -2
  63. package/dist/index40.js +67 -10
  64. package/dist/index40.js.map +1 -1
  65. package/dist/index41.js +16 -78
  66. package/dist/index41.js.map +1 -1
  67. package/dist/index42.js +96 -0
  68. package/dist/index42.js.map +1 -0
  69. package/dist/index9.js +228 -14
  70. package/dist/index9.js.map +1 -1
  71. package/dist/presets/faceset.d.ts +30 -0
  72. package/dist/presets/index.d.ts +1 -0
  73. package/dist/services/mmorpg.d.ts +1 -1
  74. package/dist/services/standalone.d.ts +1 -1
  75. package/package.json +7 -5
  76. package/src/Game/AnimationManager.ts +1 -0
  77. package/src/Game/Map.ts +9 -1
  78. package/src/Game/Object.ts +28 -6
  79. package/src/Gui/Gui.ts +300 -17
  80. package/src/RpgClient.ts +36 -5
  81. package/src/RpgClientEngine.ts +32 -9
  82. package/src/components/character.ce +11 -9
  83. package/src/components/gui/box.ce +17 -0
  84. package/src/components/gui/dialogbox/index.ce +74 -35
  85. package/src/components/gui/dialogbox/selection.ce +16 -1
  86. package/src/components/gui/index.ts +3 -4
  87. package/src/components/scenes/event-layer.ce +6 -0
  88. package/src/index.ts +3 -1
  89. package/src/presets/faceset.ts +60 -0
  90. package/src/presets/index.ts +3 -1
package/src/Gui/Gui.ts CHANGED
@@ -1,13 +1,36 @@
1
1
  import { Context, inject } from "@signe/di";
2
- import { signal } from "canvasengine";
2
+ import { signal, Signal, WritableSignal } from "canvasengine";
3
3
  import { AbstractWebsocket, WebSocketToken } from "../services/AbstractSocket";
4
- import { PrebuiltGui } from "../components/gui";
4
+ import { DialogboxComponent } from "../components/gui";
5
+ import { combineLatest, Subscription } from "rxjs";
5
6
 
6
7
  interface GuiOptions {
7
- name: string;
8
+ name?: string;
9
+ id?: string;
8
10
  component: any;
9
11
  display?: boolean;
10
12
  data?: any;
13
+ /**
14
+ * Auto display the GUI when added to the system
15
+ * @default false
16
+ */
17
+ autoDisplay?: boolean;
18
+ /**
19
+ * Function that returns an array of Signal dependencies
20
+ * The GUI will only display when all dependencies are resolved (!= undefined)
21
+ * @returns Array of Signal dependencies
22
+ */
23
+ dependencies?: () => Signal[];
24
+ }
25
+
26
+ interface GuiInstance {
27
+ name: string;
28
+ component: any;
29
+ display: WritableSignal<boolean>;
30
+ data: WritableSignal<any>;
31
+ autoDisplay: boolean;
32
+ dependencies?: () => Signal[];
33
+ subscription?: Subscription;
11
34
  }
12
35
 
13
36
  const throwError = (id: string) => {
@@ -16,13 +39,15 @@ const throwError = (id: string) => {
16
39
 
17
40
  export class RpgGui {
18
41
  private webSocket: AbstractWebsocket;
19
- gui = signal<any>({});
42
+ gui = signal<Record<string, GuiInstance>>({});
43
+ extraGuis: GuiInstance[] = [];
44
+ private vueGuiInstance: any = null; // Reference to VueGui instance
20
45
 
21
46
  constructor(private context: Context) {
22
47
  this.webSocket = inject(context, WebSocketToken);
23
48
  this.add({
24
49
  name: "rpg-dialog",
25
- component: PrebuiltGui.Dialogbox,
50
+ component: DialogboxComponent,
26
51
  });
27
52
  }
28
53
 
@@ -36,6 +61,63 @@ export class RpgGui {
36
61
  });
37
62
  }
38
63
 
64
+ /**
65
+ * Set the VueGui instance reference for Vue component management
66
+ * This is called by VueGui when it's initialized
67
+ *
68
+ * @param vueGuiInstance - The VueGui instance
69
+ */
70
+ _setVueGuiInstance(vueGuiInstance: any) {
71
+ this.vueGuiInstance = vueGuiInstance;
72
+ }
73
+
74
+ /**
75
+ * Notify VueGui about GUI state changes
76
+ * This synchronizes the Vue component display state
77
+ *
78
+ * @param guiId - The GUI component ID
79
+ * @param display - Display state
80
+ * @param data - Component data
81
+ */
82
+ private _notifyVueGui(guiId: string, display: boolean, data: any = {}) {
83
+ if (this.vueGuiInstance && this.vueGuiInstance.vm) {
84
+ // Find the GUI in extraGuis
85
+ const extraGui = this.extraGuis.find(gui => gui.name === guiId);
86
+ if (extraGui) {
87
+ // Update the Vue component's display state and data
88
+ this.vueGuiInstance.vm.gui[guiId] = {
89
+ name: guiId,
90
+ display,
91
+ data,
92
+ attachToSprite: false // Default value, could be configurable
93
+ };
94
+ // Trigger Vue reactivity
95
+ this.vueGuiInstance.vm.gui = Object.assign({}, this.vueGuiInstance.vm.gui);
96
+ }
97
+ }
98
+ }
99
+
100
+ /**
101
+ * Initialize Vue components in the VueGui instance
102
+ * This should be called after VueGui is mounted
103
+ */
104
+ _initializeVueComponents() {
105
+ if (this.vueGuiInstance && this.vueGuiInstance.vm) {
106
+ // Initialize all extraGuis in the Vue instance
107
+ this.extraGuis.forEach(gui => {
108
+ this.vueGuiInstance.vm.gui[gui.name] = {
109
+ name: gui.name,
110
+ display: gui.display(),
111
+ data: gui.data(),
112
+ attachToSprite: false
113
+ };
114
+ });
115
+
116
+ // Trigger Vue reactivity
117
+ this.vueGuiInstance.vm.gui = Object.assign({}, this.vueGuiInstance.vm.gui);
118
+ }
119
+ }
120
+
39
121
  guiInteraction(guiId: string, name: string, data: any) {
40
122
  this.webSocket.emit("gui.interaction", {
41
123
  guiId,
@@ -51,42 +133,243 @@ export class RpgGui {
51
133
  });
52
134
  }
53
135
 
136
+ /**
137
+ * Add a GUI component to the system
138
+ *
139
+ * By default, only CanvasEngine components (.ce files) are accepted.
140
+ * Vue components should be handled by the @rpgjs/vue package.
141
+ *
142
+ * @param gui - GUI configuration options
143
+ * @param gui.name - Name or ID of the GUI component
144
+ * @param gui.id - Alternative ID if name is not provided
145
+ * @param gui.component - The component to render (must be a CanvasEngine component)
146
+ * @param gui.display - Initial display state (default: false)
147
+ * @param gui.data - Initial data for the component
148
+ * @param gui.autoDisplay - Auto display when added (default: false)
149
+ * @param gui.dependencies - Function returning Signal dependencies
150
+ *
151
+ * @example
152
+ * ```ts
153
+ * gui.add({
154
+ * name: 'inventory',
155
+ * component: InventoryComponent, // Must be a .ce component
156
+ * autoDisplay: true,
157
+ * dependencies: () => [playerSignal, inventorySignal]
158
+ * });
159
+ * ```
160
+ */
54
161
  add(gui: GuiOptions) {
55
- this.gui()[gui.name] = {
56
- name: gui.name,
162
+ const guiId = gui.name || gui.id;
163
+ if (!guiId) {
164
+ throw new Error("GUI must have a name or id");
165
+ }
166
+
167
+ const guiInstance: GuiInstance = {
168
+ name: guiId,
57
169
  component: gui.component,
58
170
  display: signal(gui.display || false),
59
171
  data: signal(gui.data || {}),
172
+ autoDisplay: gui.autoDisplay || false,
173
+ dependencies: gui.dependencies,
60
174
  };
175
+
176
+ // Accept both CanvasEngine components (.ce) and Vue components
177
+ // Vue components will be handled by VueGui if available
178
+ if (typeof gui.component !== 'function') {
179
+ guiInstance.component = gui;
180
+ this.extraGuis.push(guiInstance);
181
+
182
+ // Auto display Vue components if enabled
183
+ if (guiInstance.autoDisplay) {
184
+ this._notifyVueGui(guiId, true, gui.data || {});
185
+ }
186
+ return;
187
+ }
188
+
189
+ this.gui()[guiId] = guiInstance;
190
+
191
+ // Auto display if enabled and it's a CanvasEngine component
192
+ if (guiInstance.autoDisplay && typeof gui.component === 'function') {
193
+ this.display(guiId);
194
+ }
61
195
  }
62
196
 
63
- get(id: string | GuiOptions) {
64
- if (typeof id != "string") {
65
- id = id.name;
197
+ get(id: string): GuiInstance | undefined {
198
+ // Check CanvasEngine GUIs first
199
+ const canvasGui = this.gui()[id];
200
+ if (canvasGui) {
201
+ return canvasGui;
66
202
  }
67
- return this.gui()[id];
203
+
204
+ // Check Vue GUIs in extraGuis
205
+ return this.extraGuis.find(gui => gui.name === id);
68
206
  }
69
207
 
70
208
  exists(id: string): boolean {
71
209
  return !!this.get(id);
72
210
  }
73
211
 
74
- getAll() {
75
- return this.gui();
212
+ getAll(): Record<string, GuiInstance> {
213
+ const allGuis = { ...this.gui() };
214
+
215
+ // Add extraGuis to the result
216
+ this.extraGuis.forEach(gui => {
217
+ allGuis[gui.name] = gui;
218
+ });
219
+
220
+ return allGuis;
76
221
  }
77
222
 
78
- display(id: string, data = {}) {
223
+ /**
224
+ * Display a GUI component
225
+ *
226
+ * Displays the GUI immediately if no dependencies are configured,
227
+ * or waits for all dependencies to be resolved if dependencies are present.
228
+ * Automatically manages subscriptions to prevent memory leaks.
229
+ * Works with both CanvasEngine components and Vue components.
230
+ *
231
+ * @param id - The GUI component ID
232
+ * @param data - Data to pass to the component
233
+ * @param dependencies - Optional runtime dependencies (overrides config dependencies)
234
+ *
235
+ * @example
236
+ * ```ts
237
+ * // Display immediately
238
+ * gui.display('inventory', { items: [] });
239
+ *
240
+ * // Display with runtime dependencies
241
+ * gui.display('shop', { shopId: 1 }, [playerSignal, shopSignal]);
242
+ * ```
243
+ */
244
+ display(id: string, data = {}, dependencies: Signal[] = []) {
79
245
  if (!this.exists(id)) {
80
246
  throw throwError(id);
81
247
  }
82
- this.get(id).data.set(data);
83
- this.get(id).display.set(true);
248
+
249
+ const guiInstance = this.get(id)!;
250
+
251
+ // Check if it's a Vue component (in extraGuis)
252
+ const isVueComponent = this.extraGuis.some(gui => gui.name === id);
253
+
254
+ if (isVueComponent) {
255
+ // Handle Vue component display
256
+ this._handleVueComponentDisplay(id, data, dependencies, guiInstance);
257
+ } else {
258
+ // Handle CanvasEngine component display
259
+ this._handleCanvasComponentDisplay(id, data, dependencies, guiInstance);
260
+ }
261
+ }
262
+
263
+ /**
264
+ * Handle Vue component display logic
265
+ *
266
+ * @param id - GUI component ID
267
+ * @param data - Component data
268
+ * @param dependencies - Runtime dependencies
269
+ * @param guiInstance - GUI instance
270
+ */
271
+ private _handleVueComponentDisplay(id: string, data: any, dependencies: Signal[], guiInstance: GuiInstance) {
272
+ // Unsubscribe from previous subscription if exists
273
+ if (guiInstance.subscription) {
274
+ guiInstance.subscription.unsubscribe();
275
+ guiInstance.subscription = undefined;
276
+ }
277
+
278
+ // Use runtime dependencies or config dependencies
279
+ const deps = dependencies.length > 0
280
+ ? dependencies
281
+ : (guiInstance.dependencies ? guiInstance.dependencies() : []);
282
+
283
+ if (deps.length > 0) {
284
+ // Subscribe to dependencies
285
+ guiInstance.subscription = combineLatest(
286
+ deps.map(dependency => dependency.observable)
287
+ ).subscribe((values) => {
288
+ if (values.every(value => value !== undefined)) {
289
+ guiInstance.data.set(data);
290
+ guiInstance.display.set(true);
291
+ this._notifyVueGui(id, true, data);
292
+ }
293
+ });
294
+ return;
295
+ }
296
+
297
+ // No dependencies, display immediately
298
+ guiInstance.data.set(data);
299
+ guiInstance.display.set(true);
300
+ this._notifyVueGui(id, true, data);
301
+ }
302
+
303
+ /**
304
+ * Handle CanvasEngine component display logic
305
+ *
306
+ * @param id - GUI component ID
307
+ * @param data - Component data
308
+ * @param dependencies - Runtime dependencies
309
+ * @param guiInstance - GUI instance
310
+ */
311
+ private _handleCanvasComponentDisplay(id: string, data: any, dependencies: Signal[], guiInstance: GuiInstance) {
312
+ // Unsubscribe from previous subscription if exists
313
+ if (guiInstance.subscription) {
314
+ guiInstance.subscription.unsubscribe();
315
+ guiInstance.subscription = undefined;
316
+ }
317
+
318
+ // Use runtime dependencies or config dependencies
319
+ const deps = dependencies.length > 0
320
+ ? dependencies
321
+ : (guiInstance.dependencies ? guiInstance.dependencies() : []);
322
+
323
+ if (deps.length > 0) {
324
+ // Subscribe to dependencies
325
+ guiInstance.subscription = combineLatest(
326
+ deps.map(dependency => dependency.observable)
327
+ ).subscribe((values) => {
328
+ if (values.every(value => value !== undefined)) {
329
+ guiInstance.data.set(data);
330
+ guiInstance.display.set(true);
331
+ }
332
+ });
333
+ return;
334
+ }
335
+
336
+ // No dependencies, display immediately
337
+ guiInstance.data.set(data);
338
+ guiInstance.display.set(true);
84
339
  }
85
340
 
341
+ /**
342
+ * Hide a GUI component
343
+ *
344
+ * Hides the GUI and cleans up any active subscriptions.
345
+ * Works with both CanvasEngine components and Vue components.
346
+ *
347
+ * @param id - The GUI component ID
348
+ *
349
+ * @example
350
+ * ```ts
351
+ * gui.hide('inventory');
352
+ * ```
353
+ */
86
354
  hide(id: string) {
87
355
  if (!this.exists(id)) {
88
356
  throw throwError(id);
89
357
  }
90
- this.get(id).display.set(false);
358
+
359
+ const guiInstance = this.get(id)!;
360
+
361
+ // Unsubscribe if there's an active subscription
362
+ if (guiInstance.subscription) {
363
+ guiInstance.subscription.unsubscribe();
364
+ guiInstance.subscription = undefined;
365
+ }
366
+
367
+ guiInstance.display.set(false);
368
+
369
+ // Check if it's a Vue component and notify VueGui
370
+ const isVueComponent = this.extraGuis.some(gui => gui.name === id);
371
+ if (isVueComponent) {
372
+ this._notifyVueGui(id, false);
373
+ }
91
374
  }
92
375
  }
package/src/RpgClient.ts CHANGED
@@ -1,9 +1,10 @@
1
- import { ComponentFunction } from 'canvasengine'
1
+ import { ComponentFunction, Signal } from 'canvasengine'
2
2
  import { RpgClientEngine } from './RpgClientEngine'
3
3
  import { Loader, Container } from 'pixi.js'
4
+ import { RpgClientObject } from './Game/Object'
4
5
 
5
6
  type RpgClass<T = any> = new (...args: any[]) => T
6
- type RpgComponent = Container
7
+ type RpgComponent = RpgClientObject
7
8
  type SceneMap = Container
8
9
 
9
10
  export interface RpgClientEngineHooks {
@@ -305,15 +306,43 @@ export interface RpgClient {
305
306
  spritesheets?: any[],
306
307
 
307
308
  /**
308
- * Array containing the list of VueJS components
309
+ * Array containing the list of GUI components
309
310
  *
311
+ * ```ts
312
+ * import { defineModule, RpgClient } from '@rpgjs/client'
313
+ * import InventoryComponent from './inventory.ce'
314
+ *
315
+ * defineModule<RpgClient>({
316
+ * gui: [
317
+ * {
318
+ * id: 'inventory',
319
+ * component: InventoryComponent,
320
+ * autoDisplay: true,
321
+ * dependencies: () => [playerSignal, inventorySignal]
322
+ * }
323
+ * ]
324
+ * })
325
+ * ```
310
326
  *
311
327
  * [Guide: Create GUI](/guide/create-gui.html)
312
328
  *
313
- * @prop {Array<Component of CanvasEngine>} [gui]
329
+ * @prop {Array<GuiOptions>} [gui]
314
330
  * @memberof RpgClient
315
331
  * */
316
- gui?: ComponentFunction[],
332
+ gui?: ({
333
+ id: string,
334
+ component: ComponentFunction,
335
+ /**
336
+ * Auto display the GUI when added to the system
337
+ * @default false
338
+ */
339
+ autoDisplay?: boolean,
340
+ /**
341
+ * Function that returns an array of Signal dependencies
342
+ * The GUI will only display when all dependencies are resolved (!= undefined)
343
+ */
344
+ dependencies?: () => Signal[]
345
+ } | any)[],
317
346
 
318
347
  /**
319
348
  * Array containing the list of sounds
@@ -382,6 +411,8 @@ export interface RpgClient {
382
411
  map: RpgSceneMapHooks
383
412
  }
384
413
 
414
+ sceneMap?: RpgSceneMapHooks
415
+
385
416
  /**
386
417
  * Array containing the list of component animations
387
418
  * Each element defines a temporary component to display for animations like hits, effects, etc.
@@ -18,7 +18,7 @@ export class RpgClientEngine<T = any> {
18
18
  private webSocket: AbstractWebsocket;
19
19
  private loadMapService: LoadMapService;
20
20
  private hooks: Hooks;
21
- private sceneMap: RpgClientMap = new RpgClientMap();
21
+ private sceneMap: RpgClientMap
22
22
  private selector: HTMLElement;
23
23
  public globalConfig: T;
24
24
  public sceneComponent: any;
@@ -46,6 +46,19 @@ export class RpgClientEngine<T = any> {
46
46
  this.hooks = inject<Hooks>(context, ModulesToken);
47
47
  this.globalConfig = inject(context, GlobalConfigToken)
48
48
 
49
+ if (!this.globalConfig) {
50
+ this.globalConfig = {}
51
+ }
52
+ if (!this.globalConfig.box) {
53
+ this.globalConfig.box = {
54
+ styles: {
55
+ backgroundColor: "#1a1a2e",
56
+ backgroundOpacity: 0.9
57
+ },
58
+ sounds: {}
59
+ }
60
+ }
61
+
49
62
  this.addComponentAnimation({
50
63
  id: "animation",
51
64
  component: PrebuiltComponentAnimations.Animation
@@ -53,12 +66,21 @@ export class RpgClientEngine<T = any> {
53
66
  }
54
67
 
55
68
  async start() {
69
+ this.sceneMap = new RpgClientMap()
56
70
  this.selector = document.body.querySelector("#rpg") as HTMLElement;
57
71
 
58
72
  const { app, canvasElement } = await bootstrapCanvas(this.selector, Canvas);
59
73
  this.renderer = app.renderer as PIXI.Renderer;
60
74
  this.tick = canvasElement?.propObservables?.context['tick'].observable
61
75
 
76
+
77
+ this.hooks.callHooks("client-spritesheets-load", this).subscribe();
78
+ this.hooks.callHooks("client-sounds-load", this).subscribe();
79
+ this.hooks.callHooks("client-gui-load", this).subscribe();
80
+ this.hooks.callHooks("client-particles-load", this).subscribe();
81
+ this.hooks.callHooks("client-componentAnimations-load", this).subscribe();
82
+ this.hooks.callHooks("client-sprite-load", this).subscribe();
83
+
62
84
  await lastValueFrom(this.hooks.callHooks("client-engine-onStart", this));
63
85
 
64
86
  // wondow is resize
@@ -70,14 +92,6 @@ export class RpgClientEngine<T = any> {
70
92
  this.hooks.callHooks("client-engine-onStep", this, tick).subscribe();
71
93
  })
72
94
 
73
- this.hooks.callHooks("client-spritesheets-load", this).subscribe();
74
- this.hooks.callHooks("client-sounds-load", this).subscribe();
75
- this.hooks.callHooks("client-gui-load", this).subscribe();
76
- this.hooks.callHooks("client-particles-load", this).subscribe();
77
- this.hooks.callHooks("client-componentAnimations-load", this).subscribe();
78
- this.hooks.callHooks("client-sprite-load", this).subscribe();
79
-
80
-
81
95
  await this.webSocket.connection(() => {
82
96
  this.initListeners()
83
97
  this.guiService._initialize()
@@ -124,6 +138,7 @@ export class RpgClientEngine<T = any> {
124
138
  }
125
139
 
126
140
  private async loadScene(mapId: string) {
141
+ this.hooks.callHooks("client-sceneMap-onBeforeLoading", this.sceneMap).subscribe();
127
142
  this.webSocket.updateProperties({ room: mapId })
128
143
  await this.webSocket.reconnect(() => {
129
144
  this.initListeners()
@@ -273,4 +288,12 @@ export class RpgClientEngine<T = any> {
273
288
  get playerId() {
274
289
  return this.playerIdSignal()
275
290
  }
291
+
292
+ get scene() {
293
+ return this.sceneMap
294
+ }
295
+
296
+ getCurrentPlayer() {
297
+ return this.sceneMap.getCurrentPlayer()
298
+ }
276
299
  }
@@ -1,4 +1,4 @@
1
- <Container x y zIndex={y} viewportFollow={isMe} controls>
1
+ <Container x y zIndex={y} viewportFollow={isMe} controls onBeforeDestroy>
2
2
  @for (component of componentsBehind) {
3
3
  <Container>
4
4
  <component object />
@@ -19,6 +19,7 @@
19
19
 
20
20
  <script>
21
21
  import { signal, effect, mount, computed, tick } from "canvasengine";
22
+ import { lastValueFrom } from "rxjs";
22
23
  import { Particle } from "@canvasengine/presets";
23
24
  import { GameEngineToken, ModulesToken } from "@rpgjs/common";
24
25
  import { RpgClientEngine } from "../RpgClientEngine";
@@ -127,19 +128,20 @@
127
128
  }
128
129
 
129
130
  previousAnimationName = currentAnimationName;
131
+
130
132
  });
131
133
 
132
- mount((element) => {
133
- hooks.callHooks("client-sprite-onInit", element.componentInstance)
134
- hooks.callHooks("client-sceneMap-onAddSprite", client.sceneMap, element.componentInstance)
134
+ const onBeforeDestroy = async () => {
135
+ await lastValueFrom(hooks.callHooks("client-sprite-onDestroy", object))
136
+ await lastValueFrom(hooks.callHooks("client-sceneMap-onRemoveSprite", client.sceneMap, object))
137
+ }
135
138
 
136
- return () => {
137
- hooks.callHooks("client-sprite-onDestroy", element.componentInstance)
138
- hooks.callHooks("client-sceneMap-onRemoveSprite", client.sceneMap, element.componentInstance)
139
- }
139
+ mount((element) => {
140
+ hooks.callHooks("client-sprite-onAdd", object).subscribe()
141
+ hooks.callHooks("client-sceneMap-onAddSprite", client.sceneMap, object).subscribe()
140
142
  })
141
143
 
142
144
  tick(() => {
143
- hooks.callHooks("client-sprite-onUpdate")
145
+ hooks.callHooks("client-sprite-onUpdate").subscribe()
144
146
  })
145
147
  </script>
@@ -0,0 +1,17 @@
1
+ <Container positionType="absolute" top={top} left={left}>
2
+ <Container
3
+ anchor={[0.5, 0.5]}
4
+ >
5
+ <Rect width height color={_color} />
6
+ <Container attach={child}></Container>
7
+ </Container>
8
+ </Container>
9
+
10
+ <script>
11
+ import { RpgClientEngine, inject } from "../../index";
12
+
13
+ const { width, height, children, color, top, left } = defineProps();
14
+ const engine = inject(RpgClientEngine)
15
+ const child = children[0]
16
+ const _color = computed(() => engine.globalConfig.gui?.windowColor || color?.() || "#1a1a2e")
17
+ </script>