@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.
- package/dist/Game/Map.d.ts +4 -0
- package/dist/Gui/Gui.d.ts +82 -5
- package/dist/RpgClient.d.ts +35 -5
- package/dist/RpgClientEngine.d.ts +3 -0
- package/dist/components/gui/index.d.ts +3 -3
- package/dist/index.d.ts +1 -0
- package/dist/index.js +6 -5
- package/dist/index.js.map +1 -1
- package/dist/index10.js +105 -4
- package/dist/index10.js.map +1 -1
- package/dist/index11.js +20 -7
- package/dist/index11.js.map +1 -1
- package/dist/index12.js +6 -8
- package/dist/index12.js.map +1 -1
- package/dist/index13.js +9 -14
- package/dist/index13.js.map +1 -1
- package/dist/index14.js +8 -100
- package/dist/index14.js.map +1 -1
- package/dist/index15.js +141 -35
- package/dist/index15.js.map +1 -1
- package/dist/index16.js +45 -186
- package/dist/index16.js.map +1 -1
- package/dist/index17.js +229 -5
- package/dist/index17.js.map +1 -1
- package/dist/index18.js +5 -383
- package/dist/index18.js.map +1 -1
- package/dist/index19.js +384 -28
- package/dist/index19.js.map +1 -1
- package/dist/index2.js +15 -8
- package/dist/index2.js.map +1 -1
- package/dist/index20.js +31 -17
- package/dist/index20.js.map +1 -1
- package/dist/index21.js +17 -2413
- package/dist/index21.js.map +1 -1
- package/dist/index22.js +2587 -88
- package/dist/index22.js.map +1 -1
- package/dist/index23.js +108 -103
- package/dist/index23.js.map +1 -1
- package/dist/index29.js.map +1 -1
- package/dist/index3.js +2 -2
- package/dist/index30.js +14 -1
- package/dist/index30.js.map +1 -1
- package/dist/index37.js +56 -169
- package/dist/index37.js.map +1 -1
- package/dist/index38.js +162 -489
- package/dist/index38.js.map +1 -1
- package/dist/index39.js +496 -56
- package/dist/index39.js.map +1 -1
- package/dist/index4.js +2 -2
- package/dist/index41.js +21 -7
- package/dist/index41.js.map +1 -1
- package/dist/index5.js +1 -1
- package/dist/index6.js +1 -1
- package/dist/index7.js +1 -1
- package/dist/index8.js +1 -1
- package/dist/index9.js +105 -14
- package/dist/index9.js.map +1 -1
- package/dist/services/mmorpg.d.ts +1 -1
- package/dist/services/standalone.d.ts +1 -1
- package/package.json +7 -5
- package/src/Game/AnimationManager.ts +1 -0
- package/src/Game/Map.ts +9 -1
- package/src/Game/Object.ts +28 -6
- package/src/Gui/Gui.ts +141 -16
- package/src/RpgClient.ts +36 -5
- package/src/RpgClientEngine.ts +11 -1
- package/src/components/character.ce +11 -9
- package/src/components/gui/box.ce +17 -0
- package/src/components/gui/dialogbox/index.ce +1 -0
- package/src/components/gui/index.ts +3 -4
- package/src/components/scenes/event-layer.ce +6 -0
- 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 {
|
|
4
|
+
import { DialogboxComponent } from "../components/gui";
|
|
5
|
+
import { combineLatest, Subscription } from "rxjs";
|
|
5
6
|
|
|
6
7
|
interface GuiOptions {
|
|
7
|
-
name
|
|
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<
|
|
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:
|
|
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
|
-
|
|
56
|
-
|
|
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
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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
|
-
|
|
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
|
-
|
|
83
|
-
this.get(id)
|
|
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
|
-
|
|
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 =
|
|
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
|
|
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<
|
|
329
|
+
* @prop {Array<GuiOptions>} [gui]
|
|
314
330
|
* @memberof RpgClient
|
|
315
331
|
* */
|
|
316
|
-
gui?:
|
|
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.
|
package/src/RpgClientEngine.ts
CHANGED
|
@@ -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
|
|
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
|
-
|
|
133
|
-
hooks.callHooks("client-sprite-
|
|
134
|
-
hooks.callHooks("client-sceneMap-
|
|
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
|
-
|
|
137
|
-
|
|
138
|
-
|
|
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>
|
|
@@ -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