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

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/Map.d.ts +4 -0
  2. package/dist/Gui/Gui.d.ts +82 -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 +1 -0
  7. package/dist/index.js +6 -5
  8. package/dist/index.js.map +1 -1
  9. package/dist/index10.js +105 -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 +9 -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 +45 -186
  22. package/dist/index16.js.map +1 -1
  23. package/dist/index17.js +229 -5
  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 +15 -8
  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.map +1 -1
  40. package/dist/index3.js +2 -2
  41. package/dist/index30.js +14 -1
  42. package/dist/index30.js.map +1 -1
  43. package/dist/index37.js +56 -169
  44. package/dist/index37.js.map +1 -1
  45. package/dist/index38.js +162 -489
  46. package/dist/index38.js.map +1 -1
  47. package/dist/index39.js +496 -56
  48. package/dist/index39.js.map +1 -1
  49. package/dist/index4.js +2 -2
  50. package/dist/index41.js +21 -7
  51. package/dist/index41.js.map +1 -1
  52. package/dist/index5.js +1 -1
  53. package/dist/index6.js +1 -1
  54. package/dist/index7.js +1 -1
  55. package/dist/index8.js +1 -1
  56. package/dist/index9.js +105 -14
  57. package/dist/index9.js.map +1 -1
  58. package/dist/services/mmorpg.d.ts +1 -1
  59. package/dist/services/standalone.d.ts +1 -1
  60. package/package.json +7 -5
  61. package/src/Game/AnimationManager.ts +1 -0
  62. package/src/Game/Map.ts +9 -1
  63. package/src/Game/Object.ts +28 -6
  64. package/src/Gui/Gui.ts +141 -16
  65. package/src/RpgClient.ts +36 -5
  66. package/src/RpgClientEngine.ts +11 -1
  67. package/src/components/character.ce +11 -9
  68. package/src/components/gui/box.ce +17 -0
  69. package/src/components/gui/dialogbox/index.ce +1 -0
  70. package/src/components/gui/index.ts +3 -4
  71. package/src/components/scenes/event-layer.ce +6 -0
  72. package/src/index.ts +2 -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,13 @@ 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>>({});
20
43
 
21
44
  constructor(private context: Context) {
22
45
  this.webSocket = inject(context, WebSocketToken);
23
46
  this.add({
24
47
  name: "rpg-dialog",
25
- component: PrebuiltGui.Dialogbox,
48
+ component: DialogboxComponent,
26
49
  });
27
50
  }
28
51
 
@@ -51,19 +74,52 @@ export class RpgGui {
51
74
  });
52
75
  }
53
76
 
77
+ /**
78
+ * Add a GUI component to the system
79
+ *
80
+ * @param gui - GUI configuration options
81
+ * @param gui.name - Name or ID of the GUI component
82
+ * @param gui.id - Alternative ID if name is not provided
83
+ * @param gui.component - The component to render
84
+ * @param gui.display - Initial display state (default: false)
85
+ * @param gui.data - Initial data for the component
86
+ * @param gui.autoDisplay - Auto display when added (default: false)
87
+ * @param gui.dependencies - Function returning Signal dependencies
88
+ *
89
+ * @example
90
+ * ```ts
91
+ * gui.add({
92
+ * name: 'inventory',
93
+ * component: InventoryComponent,
94
+ * autoDisplay: true,
95
+ * dependencies: () => [playerSignal, inventorySignal]
96
+ * });
97
+ * ```
98
+ */
54
99
  add(gui: GuiOptions) {
55
- this.gui()[gui.name] = {
56
- name: gui.name,
100
+ const guiId = gui.name || gui.id;
101
+ if (!guiId) {
102
+ throw new Error("GUI must have a name or id");
103
+ }
104
+
105
+ const guiInstance: GuiInstance = {
106
+ name: guiId,
57
107
  component: gui.component,
58
108
  display: signal(gui.display || false),
59
109
  data: signal(gui.data || {}),
110
+ autoDisplay: gui.autoDisplay || false,
111
+ dependencies: gui.dependencies,
60
112
  };
61
- }
62
113
 
63
- get(id: string | GuiOptions) {
64
- if (typeof id != "string") {
65
- id = id.name;
114
+ this.gui()[guiId] = guiInstance;
115
+
116
+ // Auto display if enabled
117
+ if (guiInstance.autoDisplay) {
118
+ this.display(guiId);
66
119
  }
120
+ }
121
+
122
+ get(id: string): GuiInstance | undefined {
67
123
  return this.gui()[id];
68
124
  }
69
125
 
@@ -71,22 +127,91 @@ export class RpgGui {
71
127
  return !!this.get(id);
72
128
  }
73
129
 
74
- getAll() {
130
+ getAll(): Record<string, GuiInstance> {
75
131
  return this.gui();
76
132
  }
77
133
 
78
- display(id: string, data = {}) {
134
+ /**
135
+ * Display a GUI component
136
+ *
137
+ * Displays the GUI immediately if no dependencies are configured,
138
+ * or waits for all dependencies to be resolved if dependencies are present.
139
+ * Automatically manages subscriptions to prevent memory leaks.
140
+ *
141
+ * @param id - The GUI component ID
142
+ * @param data - Data to pass to the component
143
+ * @param dependencies - Optional runtime dependencies (overrides config dependencies)
144
+ *
145
+ * @example
146
+ * ```ts
147
+ * // Display immediately
148
+ * gui.display('inventory', { items: [] });
149
+ *
150
+ * // Display with runtime dependencies
151
+ * gui.display('shop', { shopId: 1 }, [playerSignal, shopSignal]);
152
+ * ```
153
+ */
154
+ display(id: string, data = {}, dependencies: Signal[] = []) {
79
155
  if (!this.exists(id)) {
80
156
  throw throwError(id);
81
157
  }
82
- this.get(id).data.set(data);
83
- this.get(id).display.set(true);
158
+
159
+ const guiInstance = this.get(id)!;
160
+
161
+ // Unsubscribe from previous subscription if exists
162
+ if (guiInstance.subscription) {
163
+ guiInstance.subscription.unsubscribe();
164
+ guiInstance.subscription = undefined;
165
+ }
166
+
167
+ // Use runtime dependencies or config dependencies
168
+ const deps = dependencies.length > 0
169
+ ? dependencies
170
+ : (guiInstance.dependencies ? guiInstance.dependencies() : []);
171
+
172
+ if (deps.length > 0) {
173
+ // Subscribe to dependencies
174
+ guiInstance.subscription = combineLatest(
175
+ deps.map(dependency => dependency.observable)
176
+ ).subscribe((values) => {
177
+ if (values.every(value => value !== undefined)) {
178
+ guiInstance.data.set(data);
179
+ guiInstance.display.set(true);
180
+ }
181
+ });
182
+ return;
183
+ }
184
+
185
+ // No dependencies, display immediately
186
+ guiInstance.data.set(data);
187
+ guiInstance.display.set(true);
84
188
  }
85
189
 
190
+ /**
191
+ * Hide a GUI component
192
+ *
193
+ * Hides the GUI and cleans up any active subscriptions.
194
+ *
195
+ * @param id - The GUI component ID
196
+ *
197
+ * @example
198
+ * ```ts
199
+ * gui.hide('inventory');
200
+ * ```
201
+ */
86
202
  hide(id: string) {
87
203
  if (!this.exists(id)) {
88
204
  throw throwError(id);
89
205
  }
90
- this.get(id).display.set(false);
206
+
207
+ const guiInstance = this.get(id)!;
208
+
209
+ // Unsubscribe if there's an active subscription
210
+ if (guiInstance.subscription) {
211
+ guiInstance.subscription.unsubscribe();
212
+ guiInstance.subscription = undefined;
213
+ }
214
+
215
+ guiInstance.display.set(false);
91
216
  }
92
217
  }
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
+ }[],
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;
@@ -53,6 +53,7 @@ export class RpgClientEngine<T = any> {
53
53
  }
54
54
 
55
55
  async start() {
56
+ this.sceneMap = new RpgClientMap()
56
57
  this.selector = document.body.querySelector("#rpg") as HTMLElement;
57
58
 
58
59
  const { app, canvasElement } = await bootstrapCanvas(this.selector, Canvas);
@@ -124,6 +125,7 @@ export class RpgClientEngine<T = any> {
124
125
  }
125
126
 
126
127
  private async loadScene(mapId: string) {
128
+ this.hooks.callHooks("client-sceneMap-onBeforeLoading", this.sceneMap).subscribe();
127
129
  this.webSocket.updateProperties({ room: mapId })
128
130
  await this.webSocket.reconnect(() => {
129
131
  this.initListeners()
@@ -273,4 +275,12 @@ export class RpgClientEngine<T = any> {
273
275
  get playerId() {
274
276
  return this.playerIdSignal()
275
277
  }
278
+
279
+ get scene() {
280
+ return this.sceneMap
281
+ }
282
+
283
+ getCurrentPlayer() {
284
+ return this.sceneMap.getCurrentPlayer()
285
+ }
276
286
  }
@@ -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>
@@ -59,6 +59,7 @@
59
59
 
60
60
  import { inject } from "../../../core/inject";
61
61
  import { RpgClientEngine } from "../../../RpgClientEngine";
62
+ import BoxComponent from "../box.ce";
62
63
 
63
64
  const {
64
65
  message,
@@ -1,5 +1,4 @@
1
- import Dialogbox from "./dialogbox/index.ce";
1
+ import DialogboxComponent from "./dialogbox/index.ce";
2
+ import BoxComponent from "./box.ce";
2
3
 
3
- export const PrebuiltGui = {
4
- Dialogbox
5
- }
4
+ export { DialogboxComponent, BoxComponent }
@@ -6,6 +6,10 @@
6
6
  @for ((player,id) of players) {
7
7
  <Character id={id} object={player} />
8
8
  }
9
+
10
+ @for (child of children) {
11
+ <child />
12
+ }
9
13
  </Container>
10
14
 
11
15
  <script>
@@ -15,6 +19,8 @@
15
19
  import Character from "../character.ce";
16
20
 
17
21
  const engine = inject(RpgClientEngine);
22
+ const { children } = defineProps()
23
+
18
24
  const players = engine.sceneMap.players
19
25
  const events = engine.sceneMap.events
20
26
  </script>
package/src/index.ts CHANGED
@@ -10,4 +10,5 @@ export * from "./Gui/Gui";
10
10
  export * from "./components/gui";
11
11
  export * from "./components/animations";
12
12
  export * from "./presets";
13
- export * from "./components";
13
+ export * from "./components";
14
+ export * from "./components/gui";