@rpgjs/client 5.0.0-alpha.11 → 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.
- package/dist/Gui/Gui.d.ts +48 -2
- package/dist/RpgClient.d.ts +2 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/index10.js +56 -11
- package/dist/index10.js.map +1 -1
- package/dist/index11.js +1 -1
- package/dist/index13.js +3 -1
- package/dist/index13.js.map +1 -1
- package/dist/index16.js +228 -45
- package/dist/index16.js.map +1 -1
- package/dist/index17.js +45 -228
- package/dist/index17.js.map +1 -1
- package/dist/index19.js +2 -2
- package/dist/index2.js +20 -8
- package/dist/index2.js.map +1 -1
- package/dist/index20.js +2 -2
- package/dist/index22.js +3 -3
- package/dist/index23.js +1 -1
- package/dist/index29.js +22 -3
- package/dist/index29.js.map +1 -1
- package/dist/index30.js +1 -331
- package/dist/index30.js.map +1 -1
- package/dist/index31.js +332 -24
- package/dist/index31.js.map +1 -1
- package/dist/index32.js +24 -8
- package/dist/index32.js.map +1 -1
- package/dist/index33.js +4 -4
- package/dist/index33.js.map +1 -1
- package/dist/index34.js +8 -9
- package/dist/index34.js.map +1 -1
- package/dist/index35.js +9 -4400
- package/dist/index35.js.map +1 -1
- package/dist/index36.js +4394 -307
- package/dist/index36.js.map +1 -1
- package/dist/index37.js +310 -55
- package/dist/index37.js.map +1 -1
- package/dist/index40.js +67 -10
- package/dist/index40.js.map +1 -1
- package/dist/index41.js +16 -92
- package/dist/index41.js.map +1 -1
- package/dist/index42.js +96 -0
- package/dist/index42.js.map +1 -0
- 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 +129 -6
- package/dist/index9.js.map +1 -1
- package/dist/presets/faceset.d.ts +30 -0
- package/dist/presets/index.d.ts +1 -0
- package/package.json +1 -1
- package/src/Gui/Gui.ts +164 -6
- package/src/RpgClient.ts +2 -2
- package/src/RpgClientEngine.ts +21 -8
- package/src/components/gui/dialogbox/index.ce +73 -35
- package/src/components/gui/dialogbox/selection.ce +16 -1
- package/src/index.ts +2 -1
- package/src/presets/faceset.ts +60 -0
- package/src/presets/index.ts +3 -1
package/src/Gui/Gui.ts
CHANGED
|
@@ -40,6 +40,8 @@ const throwError = (id: string) => {
|
|
|
40
40
|
export class RpgGui {
|
|
41
41
|
private webSocket: AbstractWebsocket;
|
|
42
42
|
gui = signal<Record<string, GuiInstance>>({});
|
|
43
|
+
extraGuis: GuiInstance[] = [];
|
|
44
|
+
private vueGuiInstance: any = null; // Reference to VueGui instance
|
|
43
45
|
|
|
44
46
|
constructor(private context: Context) {
|
|
45
47
|
this.webSocket = inject(context, WebSocketToken);
|
|
@@ -59,6 +61,63 @@ export class RpgGui {
|
|
|
59
61
|
});
|
|
60
62
|
}
|
|
61
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
|
+
|
|
62
121
|
guiInteraction(guiId: string, name: string, data: any) {
|
|
63
122
|
this.webSocket.emit("gui.interaction", {
|
|
64
123
|
guiId,
|
|
@@ -77,10 +136,13 @@ export class RpgGui {
|
|
|
77
136
|
/**
|
|
78
137
|
* Add a GUI component to the system
|
|
79
138
|
*
|
|
139
|
+
* By default, only CanvasEngine components (.ce files) are accepted.
|
|
140
|
+
* Vue components should be handled by the @rpgjs/vue package.
|
|
141
|
+
*
|
|
80
142
|
* @param gui - GUI configuration options
|
|
81
143
|
* @param gui.name - Name or ID of the GUI component
|
|
82
144
|
* @param gui.id - Alternative ID if name is not provided
|
|
83
|
-
* @param gui.component - The component to render
|
|
145
|
+
* @param gui.component - The component to render (must be a CanvasEngine component)
|
|
84
146
|
* @param gui.display - Initial display state (default: false)
|
|
85
147
|
* @param gui.data - Initial data for the component
|
|
86
148
|
* @param gui.autoDisplay - Auto display when added (default: false)
|
|
@@ -90,7 +152,7 @@ export class RpgGui {
|
|
|
90
152
|
* ```ts
|
|
91
153
|
* gui.add({
|
|
92
154
|
* name: 'inventory',
|
|
93
|
-
* component: InventoryComponent,
|
|
155
|
+
* component: InventoryComponent, // Must be a .ce component
|
|
94
156
|
* autoDisplay: true,
|
|
95
157
|
* dependencies: () => [playerSignal, inventorySignal]
|
|
96
158
|
* });
|
|
@@ -111,16 +173,36 @@ export class RpgGui {
|
|
|
111
173
|
dependencies: gui.dependencies,
|
|
112
174
|
};
|
|
113
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
|
+
|
|
114
189
|
this.gui()[guiId] = guiInstance;
|
|
115
190
|
|
|
116
|
-
// Auto display if enabled
|
|
117
|
-
if (guiInstance.autoDisplay) {
|
|
191
|
+
// Auto display if enabled and it's a CanvasEngine component
|
|
192
|
+
if (guiInstance.autoDisplay && typeof gui.component === 'function') {
|
|
118
193
|
this.display(guiId);
|
|
119
194
|
}
|
|
120
195
|
}
|
|
121
196
|
|
|
122
197
|
get(id: string): GuiInstance | undefined {
|
|
123
|
-
|
|
198
|
+
// Check CanvasEngine GUIs first
|
|
199
|
+
const canvasGui = this.gui()[id];
|
|
200
|
+
if (canvasGui) {
|
|
201
|
+
return canvasGui;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Check Vue GUIs in extraGuis
|
|
205
|
+
return this.extraGuis.find(gui => gui.name === id);
|
|
124
206
|
}
|
|
125
207
|
|
|
126
208
|
exists(id: string): boolean {
|
|
@@ -128,7 +210,14 @@ export class RpgGui {
|
|
|
128
210
|
}
|
|
129
211
|
|
|
130
212
|
getAll(): Record<string, GuiInstance> {
|
|
131
|
-
|
|
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;
|
|
132
221
|
}
|
|
133
222
|
|
|
134
223
|
/**
|
|
@@ -137,6 +226,7 @@ export class RpgGui {
|
|
|
137
226
|
* Displays the GUI immediately if no dependencies are configured,
|
|
138
227
|
* or waits for all dependencies to be resolved if dependencies are present.
|
|
139
228
|
* Automatically manages subscriptions to prevent memory leaks.
|
|
229
|
+
* Works with both CanvasEngine components and Vue components.
|
|
140
230
|
*
|
|
141
231
|
* @param id - The GUI component ID
|
|
142
232
|
* @param data - Data to pass to the component
|
|
@@ -158,6 +248,67 @@ export class RpgGui {
|
|
|
158
248
|
|
|
159
249
|
const guiInstance = this.get(id)!;
|
|
160
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) {
|
|
161
312
|
// Unsubscribe from previous subscription if exists
|
|
162
313
|
if (guiInstance.subscription) {
|
|
163
314
|
guiInstance.subscription.unsubscribe();
|
|
@@ -191,6 +342,7 @@ export class RpgGui {
|
|
|
191
342
|
* Hide a GUI component
|
|
192
343
|
*
|
|
193
344
|
* Hides the GUI and cleans up any active subscriptions.
|
|
345
|
+
* Works with both CanvasEngine components and Vue components.
|
|
194
346
|
*
|
|
195
347
|
* @param id - The GUI component ID
|
|
196
348
|
*
|
|
@@ -213,5 +365,11 @@ export class RpgGui {
|
|
|
213
365
|
}
|
|
214
366
|
|
|
215
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
|
+
}
|
|
216
374
|
}
|
|
217
375
|
}
|
package/src/RpgClient.ts
CHANGED
|
@@ -329,7 +329,7 @@ export interface RpgClient {
|
|
|
329
329
|
* @prop {Array<GuiOptions>} [gui]
|
|
330
330
|
* @memberof RpgClient
|
|
331
331
|
* */
|
|
332
|
-
gui?: {
|
|
332
|
+
gui?: ({
|
|
333
333
|
id: string,
|
|
334
334
|
component: ComponentFunction,
|
|
335
335
|
/**
|
|
@@ -342,7 +342,7 @@ export interface RpgClient {
|
|
|
342
342
|
* The GUI will only display when all dependencies are resolved (!= undefined)
|
|
343
343
|
*/
|
|
344
344
|
dependencies?: () => Signal[]
|
|
345
|
-
}[],
|
|
345
|
+
} | any)[],
|
|
346
346
|
|
|
347
347
|
/**
|
|
348
348
|
* Array containing the list of sounds
|
package/src/RpgClientEngine.ts
CHANGED
|
@@ -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
|
|
@@ -60,6 +73,14 @@ export class RpgClientEngine<T = any> {
|
|
|
60
73
|
this.renderer = app.renderer as PIXI.Renderer;
|
|
61
74
|
this.tick = canvasElement?.propObservables?.context['tick'].observable
|
|
62
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
|
+
|
|
63
84
|
await lastValueFrom(this.hooks.callHooks("client-engine-onStart", this));
|
|
64
85
|
|
|
65
86
|
// wondow is resize
|
|
@@ -71,14 +92,6 @@ export class RpgClientEngine<T = any> {
|
|
|
71
92
|
this.hooks.callHooks("client-engine-onStep", this, tick).subscribe();
|
|
72
93
|
})
|
|
73
94
|
|
|
74
|
-
this.hooks.callHooks("client-spritesheets-load", this).subscribe();
|
|
75
|
-
this.hooks.callHooks("client-sounds-load", this).subscribe();
|
|
76
|
-
this.hooks.callHooks("client-gui-load", this).subscribe();
|
|
77
|
-
this.hooks.callHooks("client-particles-load", this).subscribe();
|
|
78
|
-
this.hooks.callHooks("client-componentAnimations-load", this).subscribe();
|
|
79
|
-
this.hooks.callHooks("client-sprite-load", this).subscribe();
|
|
80
|
-
|
|
81
|
-
|
|
82
95
|
await this.webSocket.connection(() => {
|
|
83
96
|
this.initListeners()
|
|
84
97
|
this.guiService._initialize()
|
|
@@ -3,41 +3,38 @@
|
|
|
3
3
|
ref="dialogbox"
|
|
4
4
|
scale={{ x: scaleX }}
|
|
5
5
|
anchor={[0.5, 0.5]}
|
|
6
|
-
width={
|
|
6
|
+
width={widthBox}
|
|
7
7
|
height
|
|
8
8
|
controls
|
|
9
|
-
|
|
10
|
-
bottom={10}
|
|
9
|
+
...positionBox()
|
|
11
10
|
>
|
|
12
|
-
<Rect
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
height
|
|
18
|
-
color="#1a1a2e"
|
|
19
|
-
alpha={0.9}
|
|
20
|
-
borderRadius={10}
|
|
21
|
-
border
|
|
22
|
-
shadow
|
|
23
|
-
/>
|
|
11
|
+
<Rect
|
|
12
|
+
width={widthBox}
|
|
13
|
+
height
|
|
14
|
+
color={@dialogboxStyles.@backgroundColor}
|
|
15
|
+
alpha={@dialogboxStyles.@backgroundOpacity} />
|
|
24
16
|
<Container
|
|
25
17
|
flexDirection="row"
|
|
26
|
-
width={
|
|
18
|
+
width={widthBox}
|
|
27
19
|
height
|
|
28
20
|
alpha={contentOpacity}
|
|
29
21
|
>
|
|
30
|
-
<Container flexDirection="
|
|
31
|
-
<
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
22
|
+
<Container flexDirection="row">
|
|
23
|
+
<Container flexDirection="column">
|
|
24
|
+
<Text
|
|
25
|
+
text
|
|
26
|
+
color="#fff"
|
|
27
|
+
fontSize={18}
|
|
28
|
+
margin
|
|
29
|
+
typewriter
|
|
30
|
+
style={textStyle}
|
|
31
|
+
/>
|
|
32
|
+
@if (visibleSelection) {
|
|
33
|
+
<Selection selectedIndex={0} items={choices} onSelect />
|
|
34
|
+
}
|
|
35
|
+
</Container>
|
|
36
|
+
@if (face) {
|
|
37
|
+
<Sprite sheet={@faceSheet(@face.@id, @face.@expression)} />
|
|
41
38
|
}
|
|
42
39
|
</Container>
|
|
43
40
|
</Container>
|
|
@@ -65,17 +62,31 @@
|
|
|
65
62
|
message,
|
|
66
63
|
choices: _choices,
|
|
67
64
|
onFinish,
|
|
68
|
-
onInteraction
|
|
65
|
+
onInteraction,
|
|
66
|
+
face,
|
|
67
|
+
position,
|
|
68
|
+
typewriterEffect,
|
|
69
|
+
autoClose
|
|
69
70
|
} = defineProps();
|
|
70
|
-
|
|
71
|
+
|
|
71
72
|
const client = inject(RpgClientEngine);
|
|
72
73
|
const keyboardControls = client.globalConfig.keyboardControls;
|
|
73
74
|
|
|
75
|
+
const dialogboxStyles = client.globalConfig.box.styles ?? {
|
|
76
|
+
backgroundColor: "#1a1a2e",
|
|
77
|
+
backgroundOpacity: 0.9,
|
|
78
|
+
}
|
|
79
|
+
const dialogBoxTypewriterSound = client.globalConfig?.box?.sounds?.typewriter
|
|
80
|
+
|
|
81
|
+
const spritesheets = client.spritesheets;
|
|
82
|
+
const sounds = client.sounds;
|
|
83
|
+
|
|
74
84
|
client.stopProcessingInput = true;
|
|
75
85
|
let isDestroyed = false;
|
|
76
86
|
|
|
77
87
|
const texts = [message()]
|
|
78
|
-
const height = signal(
|
|
88
|
+
const height = signal(256);
|
|
89
|
+
const margin = signal(40);
|
|
79
90
|
const isTextCompleted = signal(false);
|
|
80
91
|
|
|
81
92
|
const drawSpeaker = (g) => {
|
|
@@ -101,6 +112,20 @@
|
|
|
101
112
|
duration: 500,
|
|
102
113
|
});
|
|
103
114
|
|
|
115
|
+
const positionBox = computed(() => {
|
|
116
|
+
if (position() === 'bottom') {
|
|
117
|
+
return { positionType: 'absolute', bottom: 10 };
|
|
118
|
+
}
|
|
119
|
+
else if (position() === 'top') {
|
|
120
|
+
return { positionType: 'absolute', top: 10 };
|
|
121
|
+
}
|
|
122
|
+
return {};
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
const widthBox = computed(() => {
|
|
126
|
+
return 700;
|
|
127
|
+
});
|
|
128
|
+
|
|
104
129
|
scaleX.set(1);
|
|
105
130
|
contentOpacity.set(1);
|
|
106
131
|
|
|
@@ -113,6 +138,13 @@
|
|
|
113
138
|
return typeof current === "string" ? current : current.text;
|
|
114
139
|
});
|
|
115
140
|
|
|
141
|
+
const faceSheet = (graphicId, animationName) => {
|
|
142
|
+
return {
|
|
143
|
+
definition: spritesheets.get(graphicId),
|
|
144
|
+
playing: animationName,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
116
148
|
const choices = computed(() => {
|
|
117
149
|
//const current = currentText();
|
|
118
150
|
//return typeof current === "string" ? null : current.choices;
|
|
@@ -123,21 +155,27 @@
|
|
|
123
155
|
|
|
124
156
|
const triggerSkip = trigger();
|
|
125
157
|
|
|
126
|
-
const typewriter = {
|
|
158
|
+
const typewriter = typewriterEffect() ? {
|
|
127
159
|
speed: 0.3,
|
|
128
160
|
skip: triggerSkip,
|
|
161
|
+
sound: {
|
|
162
|
+
src: sounds.get(dialogBoxTypewriterSound)?.src
|
|
163
|
+
},
|
|
129
164
|
onComplete: () => {
|
|
130
165
|
isTextCompleted.set(true);
|
|
166
|
+
if (autoClose()) {
|
|
167
|
+
setTimeout(() => {
|
|
168
|
+
onFinish();
|
|
169
|
+
}, 1000);
|
|
170
|
+
}
|
|
131
171
|
}
|
|
132
|
-
}
|
|
172
|
+
} : null;
|
|
133
173
|
|
|
134
174
|
const textStyle = {
|
|
135
175
|
wordWrap: true,
|
|
136
|
-
wordWrapWidth:
|
|
176
|
+
wordWrapWidth: widthBox() - margin() * 2 - (face ? 256 : 0)
|
|
137
177
|
}
|
|
138
178
|
|
|
139
|
-
const face = signal({ x: 0, y: 0, width: 256, height: 256 });
|
|
140
|
-
|
|
141
179
|
mount((element) => {
|
|
142
180
|
const [dialogbox] = element.props.children
|
|
143
181
|
return () => {
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
</Container>
|
|
6
6
|
|
|
7
7
|
<script>
|
|
8
|
-
import { signal, computed, mount } from "canvasengine";
|
|
8
|
+
import { signal, computed, mount, Howl } from "canvasengine";
|
|
9
9
|
import ItemMenu from "./itemMenu.ce";
|
|
10
10
|
import { RpgClientEngine } from "../../../RpgClientEngine";
|
|
11
11
|
import { inject } from "../../../core/inject";
|
|
@@ -19,6 +19,18 @@
|
|
|
19
19
|
|
|
20
20
|
const client = inject(RpgClientEngine);
|
|
21
21
|
const keyboardControls = client.globalConfig.keyboardControls;
|
|
22
|
+
const sounds = client.sounds;
|
|
23
|
+
const dialogBoxCursorSound = client.globalConfig?.box?.sounds?.cursorMove
|
|
24
|
+
const dialogBoxCursorSelectSound = client.globalConfig?.box?.sounds?.cursorSelect
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
const playDialogBoxSound = (soundId) => {
|
|
28
|
+
if (!soundId) return;
|
|
29
|
+
const sound = new Howl.Howl({
|
|
30
|
+
src: [sounds.get(soundId)?.src]
|
|
31
|
+
})
|
|
32
|
+
sound.play()
|
|
33
|
+
}
|
|
22
34
|
|
|
23
35
|
const selected = (index) => {
|
|
24
36
|
return computed(() => {
|
|
@@ -36,6 +48,7 @@
|
|
|
36
48
|
down: {
|
|
37
49
|
bind: keyboardControls.down,
|
|
38
50
|
keyDown() {
|
|
51
|
+
playDialogBoxSound(dialogBoxCursorSound);
|
|
39
52
|
selectedIndex.update((currentIndex) => {
|
|
40
53
|
if (wrapAround) {
|
|
41
54
|
return (currentIndex + 1) % items().length;
|
|
@@ -48,6 +61,7 @@
|
|
|
48
61
|
up: {
|
|
49
62
|
bind: keyboardControls.up,
|
|
50
63
|
keyDown() {
|
|
64
|
+
playDialogBoxSound(dialogBoxCursorSound);
|
|
51
65
|
selectedIndex.update((currentIndex) => {
|
|
52
66
|
if (wrapAround) {
|
|
53
67
|
return (currentIndex - 1 + items().length) % items().length;
|
|
@@ -61,6 +75,7 @@
|
|
|
61
75
|
bind: keyboardControls.action,
|
|
62
76
|
keyDown() {
|
|
63
77
|
onSelect?.(selectedIndex());
|
|
78
|
+
playDialogBoxSound(dialogBoxCursorSelectSound);
|
|
64
79
|
},
|
|
65
80
|
},
|
|
66
81
|
});
|
package/src/index.ts
CHANGED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creates a faceset preset for character expressions
|
|
3
|
+
*
|
|
4
|
+
* This preset allows you to define multiple facial expressions for a character,
|
|
5
|
+
* where each expression corresponds to a specific frame position (frameX, frameY)
|
|
6
|
+
* within a single faceset texture. Each expression is defined by its position
|
|
7
|
+
* in the faceset grid.
|
|
8
|
+
*
|
|
9
|
+
* @param options - Object containing the faceset configuration
|
|
10
|
+
* @param framesWidth - Number of frames horizontally in the faceset texture
|
|
11
|
+
* @param framesHeight - Number of frames vertically in the faceset texture
|
|
12
|
+
* @param expressions - Object mapping expression names to their frame positions as tuples [frameX, frameY]
|
|
13
|
+
* @returns Faceset configuration with animations for each expression
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```typescript
|
|
17
|
+
* const faceset = FacesetPreset({
|
|
18
|
+
* id: "facesetId",
|
|
19
|
+
* image: "faceset.png",
|
|
20
|
+
* width: 1024,
|
|
21
|
+
* height: 1024,
|
|
22
|
+
* }, 4, 2, {
|
|
23
|
+
* happy: [0, 0],
|
|
24
|
+
* sad: [1, 0],
|
|
25
|
+
* angry: [2, 0],
|
|
26
|
+
* surprised: [3, 0]
|
|
27
|
+
* });
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
export const FacesetPreset = (
|
|
31
|
+
options: any,
|
|
32
|
+
framesWidth: number,
|
|
33
|
+
framesHeight: number,
|
|
34
|
+
expressions: Record<string, [number, number]>,
|
|
35
|
+
) => {
|
|
36
|
+
|
|
37
|
+
const textures: Record<string, any> = {};
|
|
38
|
+
|
|
39
|
+
// Create texture configuration for each expression
|
|
40
|
+
Object.keys(expressions).forEach((expressionName) => {
|
|
41
|
+
const [frameX, frameY] = expressions[expressionName];
|
|
42
|
+
textures[expressionName] = {
|
|
43
|
+
animations: () => [
|
|
44
|
+
[{
|
|
45
|
+
time: 0,
|
|
46
|
+
frameX: frameX,
|
|
47
|
+
frameY: frameY
|
|
48
|
+
}]
|
|
49
|
+
],
|
|
50
|
+
};
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
...options,
|
|
55
|
+
framesWidth,
|
|
56
|
+
framesHeight,
|
|
57
|
+
textures
|
|
58
|
+
};
|
|
59
|
+
};
|
|
60
|
+
|
package/src/presets/index.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { AnimationSpritesheetPreset } from "./animation";
|
|
2
2
|
import { LPCSpritesheetPreset } from "./lpc";
|
|
3
3
|
import { RMSpritesheet } from "./rmspritesheet";
|
|
4
|
+
import { FacesetPreset } from "./faceset";
|
|
4
5
|
|
|
5
6
|
export const Presets = {
|
|
6
7
|
RMSpritesheet,
|
|
7
8
|
LPCSpritesheetPreset,
|
|
8
|
-
AnimationSpritesheetPreset
|
|
9
|
+
AnimationSpritesheetPreset,
|
|
10
|
+
FacesetPreset
|
|
9
11
|
}
|