@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.
Files changed (61) hide show
  1. package/dist/Gui/Gui.d.ts +48 -2
  2. package/dist/RpgClient.d.ts +2 -2
  3. package/dist/index.d.ts +1 -0
  4. package/dist/index.js +1 -0
  5. package/dist/index.js.map +1 -1
  6. package/dist/index10.js +56 -11
  7. package/dist/index10.js.map +1 -1
  8. package/dist/index11.js +1 -1
  9. package/dist/index13.js +3 -1
  10. package/dist/index13.js.map +1 -1
  11. package/dist/index16.js +228 -45
  12. package/dist/index16.js.map +1 -1
  13. package/dist/index17.js +45 -228
  14. package/dist/index17.js.map +1 -1
  15. package/dist/index19.js +2 -2
  16. package/dist/index2.js +20 -8
  17. package/dist/index2.js.map +1 -1
  18. package/dist/index20.js +2 -2
  19. package/dist/index22.js +3 -3
  20. package/dist/index23.js +1 -1
  21. package/dist/index29.js +22 -3
  22. package/dist/index29.js.map +1 -1
  23. package/dist/index30.js +1 -331
  24. package/dist/index30.js.map +1 -1
  25. package/dist/index31.js +332 -24
  26. package/dist/index31.js.map +1 -1
  27. package/dist/index32.js +24 -8
  28. package/dist/index32.js.map +1 -1
  29. package/dist/index33.js +4 -4
  30. package/dist/index33.js.map +1 -1
  31. package/dist/index34.js +8 -9
  32. package/dist/index34.js.map +1 -1
  33. package/dist/index35.js +9 -4400
  34. package/dist/index35.js.map +1 -1
  35. package/dist/index36.js +4394 -307
  36. package/dist/index36.js.map +1 -1
  37. package/dist/index37.js +310 -55
  38. package/dist/index37.js.map +1 -1
  39. package/dist/index40.js +67 -10
  40. package/dist/index40.js.map +1 -1
  41. package/dist/index41.js +16 -92
  42. package/dist/index41.js.map +1 -1
  43. package/dist/index42.js +96 -0
  44. package/dist/index42.js.map +1 -0
  45. package/dist/index5.js +1 -1
  46. package/dist/index6.js +1 -1
  47. package/dist/index7.js +1 -1
  48. package/dist/index8.js +1 -1
  49. package/dist/index9.js +129 -6
  50. package/dist/index9.js.map +1 -1
  51. package/dist/presets/faceset.d.ts +30 -0
  52. package/dist/presets/index.d.ts +1 -0
  53. package/package.json +1 -1
  54. package/src/Gui/Gui.ts +164 -6
  55. package/src/RpgClient.ts +2 -2
  56. package/src/RpgClientEngine.ts +21 -8
  57. package/src/components/gui/dialogbox/index.ce +73 -35
  58. package/src/components/gui/dialogbox/selection.ce +16 -1
  59. package/src/index.ts +2 -1
  60. package/src/presets/faceset.ts +60 -0
  61. 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
- return this.gui()[id];
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
- return this.gui();
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
@@ -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={700}
6
+ width={widthBox}
7
7
  height
8
8
  controls
9
- positionType="absolute"
10
- bottom={10}
9
+ ...positionBox()
11
10
  >
12
- <Rect width={700} height={250} color="#1a1a2e" />
13
- <Rect
14
- x={0}
15
- y={0}
16
- width={700}
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={700}
18
+ width={widthBox}
27
19
  height
28
20
  alpha={contentOpacity}
29
21
  >
30
- <Container flexDirection="column">
31
- <Text
32
- text
33
- color="#fff"
34
- fontSize={18}
35
- margin={40}
36
- typewriter
37
- style={textStyle}
38
- />
39
- @if (visibleSelection) {
40
- <Selection selectedIndex={0} items={choices} onSelect />
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(250);
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: 700 - 256 - 80
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
@@ -11,4 +11,5 @@ export * from "./components/gui";
11
11
  export * from "./components/animations";
12
12
  export * from "./presets";
13
13
  export * from "./components";
14
- export * from "./components/gui";
14
+ export * from "./components/gui";
15
+ export { Context } from "@signe/di";
@@ -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
+
@@ -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
  }