@rpgjs/client 5.0.0-beta.3 → 5.0.0-beta.5
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/Object.d.ts +10 -2
- package/dist/Game/Object.js +56 -26
- package/dist/Game/Object.js.map +1 -1
- package/dist/RpgClientEngine.d.ts +16 -0
- package/dist/RpgClientEngine.js +51 -5
- package/dist/RpgClientEngine.js.map +1 -1
- package/dist/components/character.ce.js +10 -0
- package/dist/components/character.ce.js.map +1 -1
- package/dist/components/gui/dialogbox/index.ce.js +14 -5
- package/dist/components/gui/dialogbox/index.ce.js.map +1 -1
- package/package.json +4 -4
- package/src/Game/Object.ts +86 -32
- package/src/RpgClientEngine.ts +83 -12
- package/src/components/character.ce +14 -1
- package/src/components/gui/dialogbox/index.ce +15 -6
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.ce.js","names":[],"sources":["../../../../src/components/gui/dialogbox/index.ce"],"sourcesContent":["<DOMContainer width=\"100%\" height=\"100%\" controls={dialogControls}>\n <div\n class=\"rpg-ui-dialog-container\"\n data-position={dialogPosition()}\n data-full-width={isFullWidth() ? \"true\" : \"false\"}\n data-has-face={hasFace() ? \"true\" : \"false\"}\n >\n <div class=\"rpg-ui-dialog rpg-anim-fade-in\">\n <div class=\"rpg-ui-dialog-body\">\n <div>\n @if (speakerName()) {\n <div class=\"rpg-ui-dialog-speaker\">{speakerName()}</div>\n }\n <div class=\"rpg-ui-dialog-content\">\n {displayMessage()}\n </div>\n @if (hasChoices()) {\n <Navigation tabindex={selectedItem} controls={controls}>\n <div class=\"rpg-ui-dialog-choices\">\n @for ((choice,index) of choices) {\n <div\n class=\"rpg-ui-dialog-choice\"\n class={{active: selectedItem() === index}}\n tabindex={index}\n data-choice-index={index}\n click={selectChoice(index)}\n >{{ choice.text }}</div>\n }\n </div>\n </Navigation>\n }\n </div>\n @if (hasFace()) {\n <div class=\"rpg-ui-dialog-face\">\n <DOMSprite sheet={faceSheet(face.id, face.expression)} />\n </div>\n }\n </div>\n @if (showIndicator) {\n <div class=\"rpg-ui-dialog-indicator\"></div>\n }\n </div>\n </div>\n</DOMContainer>\n \n<script>\n import { effect, signal, computed, createTabindexNavigator, mount } from \"canvasengine\";\n import { inject } from \"../../../core/inject\";\n import { RpgClientEngine } from \"../../../RpgClientEngine\";\n import { delay } from \"@rpgjs/common\";\n\n const engine = inject(RpgClientEngine);\n const currentPlayer = engine.scene.currentPlayer\n const keyboardControls = engine.globalConfig.keyboardControls;\n\n engine.stopProcessingInput = true;\n\n const selectedItem = signal(0)\n let isDestroyed = false;\n\n const {\n data,\n onFinish,\n onInteraction\n } = defineProps();\n\n const { message, choices, face, speaker, position, typewriterEffect, autoClose } = data();\n const fullWidth = computed(() => data().fullWidth || false);\n\n const resolveProp = (value) => typeof value === \"function\" ? value() : value;\n\n const speakerName = computed(() => {\n const value = resolveProp(speaker);\n return value ? String(value) : \"\";\n });\n\n const dialogPosition = computed(() => resolveProp(position) || \"bottom\");\n const isFullWidth = computed(() => resolveProp(fullWidth) !== false);\n const hasFace = computed(() => !!resolveProp(face));\n\n const displayMessage = signal(\"\");\n const fullMessage = signal(\"\");\n const isTyping = signal(false);\n let typewriterTimer = null;\n let rootElement = null;\n\n mount((element) => {\n rootElement = element;\n });\n\n const startTypewriter = (text) => {\n if (typewriterTimer) clearInterval(typewriterTimer);\n displayMessage.set(\"\");\n if (!text) return;\n let index = 0;\n isTyping.set(true);\n typewriterTimer = setInterval(() => {\n index += 1;\n displayMessage.set(text.slice(0, index));\n if (index >= text.length) {\n clearInterval(typewriterTimer);\n typewriterTimer = null;\n isTyping.set(false);\n }\n }, 20);\n };\n\n const finishTyping = () => {\n if (typewriterTimer) clearInterval(typewriterTimer);\n typewriterTimer = null;\n displayMessage.set(fullMessage());\n isTyping.set(false);\n };\n\n effect(() => {\n const text = resolveProp(message) || \"\";\n fullMessage.set(text);\n const useTypewriter = resolveProp(typewriterEffect) !== false;\n if (!useTypewriter) {\n finishTyping();\n return;\n }\n startTypewriter(text);\n });\n\n\n const hasChoices = computed(() => choices.length > 0);\n const showIndicator = computed(() => !hasChoices() && !isTyping());\n const nav = createTabindexNavigator(selectedItem, { count: () => choices.length }, 'wrap');\n\n function selectChoice(index) {\n return function() {\n selectedItem.set(index);\n onSelect(index);\n }\n }\n\n function _onFinish(value) {\n if (onFinish) onFinish(value);\n }\n\n const onSelect = (index) => {\n _onFinish(index);\n };\n\n const controls = signal({\n up: {\n repeat: true,\n bind: keyboardControls.up,\n throttle: 150,\n keyDown() {\n if (!hasChoices()) return;\n nav.next(-1);\n }\n },\n down: {\n repeat: true,\n bind: keyboardControls.down,\n throttle: 150,\n keyDown() {\n if (!hasChoices()) return;\n nav.next(1);\n }\n },\n action: {\n bind: keyboardControls.action,\n keyDown() {\n if (isTyping()) {\n finishTyping();\n return;\n }\n if (!hasChoices()) return;\n onSelect(selectedItem());\n }\n },\n gamepad: {\n enabled: true\n }\n });\n \n const dialogControls = signal({\n action: {\n bind: keyboardControls.action,\n keyDown() {\n if (isTyping()) {\n finishTyping();\n return;\n }\n if (hasChoices()) return;\n _onFinish();\n }\n },\n })\n\n const faceSheet = (graphicId, animationName) => {\n return {\n definition: engine.getSpriteSheet(graphicId),\n playing: animationName,\n };\n }\n\n mount((element) => {\n return () => {\n isDestroyed = true;\n // Wait destroy is finished before start processing input\n delay(() => {\n engine.stopProcessingInput = false;\n })\n }\n })\n</script>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.ce.js","names":[],"sources":["../../../../src/components/gui/dialogbox/index.ce"],"sourcesContent":["<DOMContainer width=\"100%\" height=\"100%\" controls={dialogControls}>\n <div\n class=\"rpg-ui-dialog-container\"\n data-position={dialogPosition()}\n data-full-width={isFullWidth() ? \"true\" : \"false\"}\n data-has-face={hasFace() ? \"true\" : \"false\"}\n >\n <div class=\"rpg-ui-dialog rpg-anim-fade-in\">\n <div class=\"rpg-ui-dialog-body\">\n <div>\n @if (speakerName()) {\n <div class=\"rpg-ui-dialog-speaker\">{speakerName()}</div>\n }\n <div class=\"rpg-ui-dialog-content\">\n {displayMessage()}\n </div>\n @if (hasChoices()) {\n <Navigation tabindex={selectedItem} controls={controls}>\n <div class=\"rpg-ui-dialog-choices\">\n @for ((choice,index) of choices) {\n <div\n class=\"rpg-ui-dialog-choice\"\n class={{active: selectedItem() === index}}\n tabindex={index}\n data-choice-index={index}\n click={selectChoice(index)}\n >{{ choice.text }}</div>\n }\n </div>\n </Navigation>\n }\n </div>\n @if (hasFace()) {\n <div class=\"rpg-ui-dialog-face\">\n <DOMSprite\n sheet={faceSheet(dialogFace())}\n width=\"100%\"\n height=\"100%\"\n objectFit=\"contain\"\n />\n </div>\n }\n </div>\n @if (showIndicator) {\n <div class=\"rpg-ui-dialog-indicator\"></div>\n }\n </div>\n </div>\n</DOMContainer>\n \n<script>\n import { effect, signal, computed, createTabindexNavigator, mount } from \"canvasengine\";\n import { inject } from \"../../../core/inject\";\n import { RpgClientEngine } from \"../../../RpgClientEngine\";\n import { delay } from \"@rpgjs/common\";\n\n const engine = inject(RpgClientEngine);\n const currentPlayer = engine.scene.currentPlayer\n const keyboardControls = engine.globalConfig.keyboardControls;\n\n engine.stopProcessingInput = true;\n\n const selectedItem = signal(0)\n let isDestroyed = false;\n\n const {\n data,\n onFinish,\n onInteraction\n } = defineProps();\n\n const { message, choices, face, speaker, position, typewriterEffect, autoClose } = data();\n const fullWidth = computed(() => data().fullWidth || false);\n\n const resolveProp = (value) => typeof value === \"function\" ? value() : value;\n\n const speakerName = computed(() => {\n const value = resolveProp(speaker);\n return value ? String(value) : \"\";\n });\n\n const dialogPosition = computed(() => resolveProp(position) || \"bottom\");\n const isFullWidth = computed(() => resolveProp(fullWidth) !== false);\n const dialogFace = computed(() => resolveProp(face));\n const hasFace = computed(() => {\n const value = dialogFace();\n return !!(value && value.id);\n });\n\n const displayMessage = signal(\"\");\n const fullMessage = signal(\"\");\n const isTyping = signal(false);\n let typewriterTimer = null;\n let rootElement = null;\n\n mount((element) => {\n rootElement = element;\n });\n\n const startTypewriter = (text) => {\n if (typewriterTimer) clearInterval(typewriterTimer);\n displayMessage.set(\"\");\n if (!text) return;\n let index = 0;\n isTyping.set(true);\n typewriterTimer = setInterval(() => {\n index += 1;\n displayMessage.set(text.slice(0, index));\n if (index >= text.length) {\n clearInterval(typewriterTimer);\n typewriterTimer = null;\n isTyping.set(false);\n }\n }, 20);\n };\n\n const finishTyping = () => {\n if (typewriterTimer) clearInterval(typewriterTimer);\n typewriterTimer = null;\n displayMessage.set(fullMessage());\n isTyping.set(false);\n };\n\n effect(() => {\n const text = resolveProp(message) || \"\";\n fullMessage.set(text);\n const useTypewriter = resolveProp(typewriterEffect) !== false;\n if (!useTypewriter) {\n finishTyping();\n return;\n }\n startTypewriter(text);\n });\n\n\n const hasChoices = computed(() => choices.length > 0);\n const showIndicator = computed(() => !hasChoices() && !isTyping());\n const nav = createTabindexNavigator(selectedItem, { count: () => choices.length }, 'wrap');\n\n function selectChoice(index) {\n return function() {\n selectedItem.set(index);\n onSelect(index);\n }\n }\n\n function _onFinish(value) {\n if (onFinish) onFinish(value);\n }\n\n const onSelect = (index) => {\n _onFinish(index);\n };\n\n const controls = signal({\n up: {\n repeat: true,\n bind: keyboardControls.up,\n throttle: 150,\n keyDown() {\n if (!hasChoices()) return;\n nav.next(-1);\n }\n },\n down: {\n repeat: true,\n bind: keyboardControls.down,\n throttle: 150,\n keyDown() {\n if (!hasChoices()) return;\n nav.next(1);\n }\n },\n action: {\n bind: keyboardControls.action,\n keyDown() {\n if (isTyping()) {\n finishTyping();\n return;\n }\n if (!hasChoices()) return;\n onSelect(selectedItem());\n }\n },\n gamepad: {\n enabled: true\n }\n });\n \n const dialogControls = signal({\n action: {\n bind: keyboardControls.action,\n keyDown() {\n if (isTyping()) {\n finishTyping();\n return;\n }\n if (hasChoices()) return;\n _onFinish();\n }\n },\n })\n\n const faceSheet = (faceValue) => {\n return {\n definition: engine.getSpriteSheet(faceValue.id),\n playing: faceValue.expression || \"default\",\n };\n }\n\n mount((element) => {\n return () => {\n isDestroyed = true;\n // Wait destroy is finished before start processing input\n delay(() => {\n engine.stopProcessingInput = false;\n })\n }\n })\n</script>\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rpgjs/client",
|
|
3
|
-
"version": "5.0.0-beta.
|
|
3
|
+
"version": "5.0.0-beta.5",
|
|
4
4
|
"description": "RPGJS is a framework for creating RPG/MMORPG games",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -22,9 +22,9 @@
|
|
|
22
22
|
"pixi.js": "^8.9.2"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@rpgjs/common": "5.0.0-beta.
|
|
26
|
-
"@rpgjs/server": "5.0.0-beta.
|
|
27
|
-
"@rpgjs/ui-css": "5.0.0-beta.
|
|
25
|
+
"@rpgjs/common": "5.0.0-beta.5",
|
|
26
|
+
"@rpgjs/server": "5.0.0-beta.5",
|
|
27
|
+
"@rpgjs/ui-css": "5.0.0-beta.5",
|
|
28
28
|
"@signe/di": "^2.9.0",
|
|
29
29
|
"@signe/room": "^2.9.0",
|
|
30
30
|
"@signe/sync": "^2.9.0",
|
package/src/Game/Object.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Hooks, ModulesToken, RpgCommonPlayer } from "@rpgjs/common";
|
|
2
2
|
import { trigger, signal, effect } from "canvasengine";
|
|
3
|
-
import { filter, from, map, Subscription, switchMap } from "rxjs";
|
|
3
|
+
import { filter, from, map, of, Subscription, switchMap } from "rxjs";
|
|
4
4
|
import { inject } from "../core/inject";
|
|
5
5
|
import { RpgClientEngine } from "../RpgClientEngine";
|
|
6
6
|
import TextComponent from "../components/dynamics/text.ce";
|
|
@@ -11,6 +11,11 @@ const DYNAMIC_COMPONENTS = {
|
|
|
11
11
|
|
|
12
12
|
type Frame = { x: number; y: number; ts: number };
|
|
13
13
|
|
|
14
|
+
type AnimationRestoreOptions = {
|
|
15
|
+
restoreAnimationName?: string;
|
|
16
|
+
restoreGraphics?: any[];
|
|
17
|
+
};
|
|
18
|
+
|
|
14
19
|
export abstract class RpgClientObject extends RpgCommonPlayer {
|
|
15
20
|
abstract _type: string;
|
|
16
21
|
emitParticleTrigger = trigger();
|
|
@@ -22,6 +27,10 @@ export abstract class RpgClientObject extends RpgCommonPlayer {
|
|
|
22
27
|
graphicsSignals = signal<any[]>([]);
|
|
23
28
|
_component = {} // temporary component memory
|
|
24
29
|
flashTrigger = trigger();
|
|
30
|
+
private animationRestoreState?: {
|
|
31
|
+
animationName: string;
|
|
32
|
+
graphics: any[];
|
|
33
|
+
};
|
|
25
34
|
|
|
26
35
|
constructor() {
|
|
27
36
|
super();
|
|
@@ -39,8 +48,10 @@ export abstract class RpgClientObject extends RpgCommonPlayer {
|
|
|
39
48
|
this.graphics.observable
|
|
40
49
|
.pipe(
|
|
41
50
|
map(({ items }) => items),
|
|
42
|
-
|
|
43
|
-
|
|
51
|
+
switchMap(graphics => {
|
|
52
|
+
if (graphics.length === 0) return of([]);
|
|
53
|
+
return from(Promise.all(graphics.map(graphic => this.engine.getSpriteSheet(graphic))));
|
|
54
|
+
})
|
|
44
55
|
)
|
|
45
56
|
.subscribe((sheets) => {
|
|
46
57
|
this.graphicsSignals.set(sheets);
|
|
@@ -101,6 +112,30 @@ export abstract class RpgClientObject extends RpgCommonPlayer {
|
|
|
101
112
|
}
|
|
102
113
|
|
|
103
114
|
private animationSubscription?: Subscription;
|
|
115
|
+
private animationResetTimeout?: ReturnType<typeof setTimeout>;
|
|
116
|
+
|
|
117
|
+
private clearAnimationControls() {
|
|
118
|
+
if (this.animationSubscription) {
|
|
119
|
+
this.animationSubscription.unsubscribe();
|
|
120
|
+
this.animationSubscription = undefined;
|
|
121
|
+
}
|
|
122
|
+
if (this.animationResetTimeout) {
|
|
123
|
+
clearTimeout(this.animationResetTimeout);
|
|
124
|
+
this.animationResetTimeout = undefined;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
private finishTemporaryAnimation() {
|
|
129
|
+
const restoreState = this.animationRestoreState;
|
|
130
|
+
this.clearAnimationControls();
|
|
131
|
+
this.animationCurrentIndex.set(0);
|
|
132
|
+
if (restoreState) {
|
|
133
|
+
this.animationName.set(restoreState.animationName);
|
|
134
|
+
this.graphics.set([...restoreState.graphics]);
|
|
135
|
+
}
|
|
136
|
+
this.animationRestoreState = undefined;
|
|
137
|
+
this.animationIsPlaying.set(false);
|
|
138
|
+
}
|
|
104
139
|
|
|
105
140
|
/**
|
|
106
141
|
* Trigger a flash animation on this sprite
|
|
@@ -199,12 +234,13 @@ export abstract class RpgClientObject extends RpgCommonPlayer {
|
|
|
199
234
|
* ```
|
|
200
235
|
*/
|
|
201
236
|
resetAnimationState() {
|
|
237
|
+
if (this.animationRestoreState) {
|
|
238
|
+
this.finishTemporaryAnimation();
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
202
241
|
this.animationIsPlaying.set(false);
|
|
203
242
|
this.animationCurrentIndex.set(0);
|
|
204
|
-
|
|
205
|
-
this.animationSubscription.unsubscribe();
|
|
206
|
-
this.animationSubscription = undefined;
|
|
207
|
-
}
|
|
243
|
+
this.clearAnimationControls();
|
|
208
244
|
}
|
|
209
245
|
|
|
210
246
|
/**
|
|
@@ -226,7 +262,7 @@ export abstract class RpgClientObject extends RpgCommonPlayer {
|
|
|
226
262
|
* player.setAnimation('spell');
|
|
227
263
|
* ```
|
|
228
264
|
*/
|
|
229
|
-
setAnimation(animationName: string, nbTimes?: number): void;
|
|
265
|
+
setAnimation(animationName: string, nbTimes?: number, options?: AnimationRestoreOptions): void;
|
|
230
266
|
/**
|
|
231
267
|
* Set a custom animation with temporary graphic change
|
|
232
268
|
*
|
|
@@ -244,30 +280,52 @@ export abstract class RpgClientObject extends RpgCommonPlayer {
|
|
|
244
280
|
* player.setAnimation('attack', 'hero_attack', 3);
|
|
245
281
|
* ```
|
|
246
282
|
*/
|
|
247
|
-
setAnimation(animationName: string, graphic?: string | string[], nbTimes?: number): void;
|
|
248
|
-
setAnimation(
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
283
|
+
setAnimation(animationName: string, graphic?: string | string[], nbTimes?: number, options?: AnimationRestoreOptions): void;
|
|
284
|
+
setAnimation(
|
|
285
|
+
animationName: string,
|
|
286
|
+
graphicOrNbTimes?: string | string[] | number,
|
|
287
|
+
nbTimesOrOptions?: number | AnimationRestoreOptions,
|
|
288
|
+
options?: AnimationRestoreOptions
|
|
289
|
+
): void {
|
|
255
290
|
let graphic: string | string[] | undefined;
|
|
256
291
|
let finalNbTimes: number = Infinity;
|
|
292
|
+
let restoreOptions: AnimationRestoreOptions | undefined = options;
|
|
257
293
|
|
|
258
294
|
// Handle overloads
|
|
259
295
|
if (typeof graphicOrNbTimes === 'number') {
|
|
260
296
|
// setAnimation(animationName, nbTimes)
|
|
261
297
|
finalNbTimes = graphicOrNbTimes;
|
|
298
|
+
restoreOptions = typeof nbTimesOrOptions === 'object' ? nbTimesOrOptions : options;
|
|
262
299
|
} else if (graphicOrNbTimes !== undefined) {
|
|
263
300
|
// setAnimation(animationName, graphic, nbTimes)
|
|
264
301
|
graphic = graphicOrNbTimes;
|
|
265
|
-
|
|
302
|
+
if (typeof nbTimesOrOptions === 'number') {
|
|
303
|
+
finalNbTimes = nbTimesOrOptions;
|
|
304
|
+
} else {
|
|
305
|
+
finalNbTimes = Infinity;
|
|
306
|
+
restoreOptions = nbTimesOrOptions ?? options;
|
|
307
|
+
}
|
|
266
308
|
} else {
|
|
267
309
|
// setAnimation(animationName) - nbTimes remains Infinity
|
|
268
310
|
finalNbTimes = Infinity;
|
|
269
311
|
}
|
|
270
312
|
|
|
313
|
+
if (this.animationIsPlaying()) {
|
|
314
|
+
this.finishTemporaryAnimation();
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
this.animationIsPlaying.set(true);
|
|
318
|
+
const previousAnimationName =
|
|
319
|
+
restoreOptions?.restoreAnimationName ?? this.animationName();
|
|
320
|
+
const previousGraphics = restoreOptions?.restoreGraphics
|
|
321
|
+
? [...restoreOptions.restoreGraphics]
|
|
322
|
+
: [...this.graphics()];
|
|
323
|
+
this.animationRestoreState = {
|
|
324
|
+
animationName: previousAnimationName,
|
|
325
|
+
graphics: previousGraphics,
|
|
326
|
+
};
|
|
327
|
+
this.animationCurrentIndex.set(0);
|
|
328
|
+
|
|
271
329
|
// Temporarily change graphic if provided
|
|
272
330
|
if (graphic !== undefined) {
|
|
273
331
|
if (Array.isArray(graphic)) {
|
|
@@ -277,27 +335,23 @@ export abstract class RpgClientObject extends RpgCommonPlayer {
|
|
|
277
335
|
}
|
|
278
336
|
}
|
|
279
337
|
|
|
280
|
-
|
|
281
|
-
if (this.animationSubscription) {
|
|
282
|
-
this.animationSubscription.unsubscribe();
|
|
283
|
-
}
|
|
338
|
+
this.clearAnimationControls();
|
|
284
339
|
|
|
285
340
|
this.animationSubscription =
|
|
286
341
|
this.animationCurrentIndex.observable.subscribe((index) => {
|
|
287
342
|
if (index >= finalNbTimes) {
|
|
288
|
-
this.
|
|
289
|
-
this.animationName.set(previousAnimationName);
|
|
290
|
-
// Reset graphic to previous value if it was changed
|
|
291
|
-
if (graphic !== undefined) {
|
|
292
|
-
this.graphics.set(previousGraphics);
|
|
293
|
-
}
|
|
294
|
-
this.animationIsPlaying.set(false);
|
|
295
|
-
if (this.animationSubscription) {
|
|
296
|
-
this.animationSubscription.unsubscribe();
|
|
297
|
-
this.animationSubscription = undefined;
|
|
298
|
-
}
|
|
343
|
+
this.finishTemporaryAnimation();
|
|
299
344
|
}
|
|
300
345
|
});
|
|
346
|
+
|
|
347
|
+
if (finalNbTimes !== Infinity) {
|
|
348
|
+
this.animationResetTimeout = setTimeout(() => {
|
|
349
|
+
if (this.animationIsPlaying()) {
|
|
350
|
+
this.finishTemporaryAnimation();
|
|
351
|
+
}
|
|
352
|
+
}, Math.max(1000, finalNbTimes * 1000));
|
|
353
|
+
}
|
|
354
|
+
|
|
301
355
|
this.animationName.set(animationName);
|
|
302
356
|
}
|
|
303
357
|
|
package/src/RpgClientEngine.ts
CHANGED
|
@@ -374,15 +374,27 @@ export class RpgClientEngine<T = any> {
|
|
|
374
374
|
this.notificationManager.add(data);
|
|
375
375
|
});
|
|
376
376
|
|
|
377
|
-
|
|
378
|
-
const {
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
377
|
+
this.webSocket.on("setAnimation", (data) => {
|
|
378
|
+
const {
|
|
379
|
+
animationName,
|
|
380
|
+
nbTimes,
|
|
381
|
+
object,
|
|
382
|
+
graphic,
|
|
383
|
+
restoreAnimationName,
|
|
384
|
+
restoreGraphics,
|
|
385
|
+
} = data;
|
|
386
|
+
const player = object ? this.sceneMap.getObjectById(object) : undefined;
|
|
387
|
+
if (!player) return;
|
|
388
|
+
const restoreOptions = {
|
|
389
|
+
restoreAnimationName,
|
|
390
|
+
restoreGraphics,
|
|
391
|
+
};
|
|
392
|
+
if (graphic !== undefined) {
|
|
393
|
+
player.setAnimation(animationName, graphic, nbTimes, restoreOptions);
|
|
394
|
+
} else {
|
|
395
|
+
player.setAnimation(animationName, nbTimes, restoreOptions);
|
|
396
|
+
}
|
|
397
|
+
})
|
|
386
398
|
|
|
387
399
|
this.webSocket.on("playSound", (data) => {
|
|
388
400
|
const { soundId, volume, loop } = data;
|
|
@@ -1174,6 +1186,18 @@ export class RpgClientEngine<T = any> {
|
|
|
1174
1186
|
}
|
|
1175
1187
|
|
|
1176
1188
|
async processInput({ input }: { input: Direction }) {
|
|
1189
|
+
if (this.stopProcessingInput) return;
|
|
1190
|
+
|
|
1191
|
+
const currentPlayer = this.sceneMap.getCurrentPlayer() as any;
|
|
1192
|
+
const canMove =
|
|
1193
|
+
!currentPlayer ||
|
|
1194
|
+
typeof currentPlayer.canMove !== "function" ||
|
|
1195
|
+
currentPlayer.canMove();
|
|
1196
|
+
if (!canMove) {
|
|
1197
|
+
this.interruptCurrentPlayerMovement(currentPlayer);
|
|
1198
|
+
return;
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1177
1201
|
const timestamp = Date.now();
|
|
1178
1202
|
let frame: number;
|
|
1179
1203
|
let tick: number;
|
|
@@ -1188,7 +1212,6 @@ export class RpgClientEngine<T = any> {
|
|
|
1188
1212
|
this.inputFrameCounter = frame;
|
|
1189
1213
|
this.hooks.callHooks("client-engine-onInput", this, { input, playerId: this.playerId }).subscribe();
|
|
1190
1214
|
|
|
1191
|
-
const currentPlayer = this.sceneMap.getCurrentPlayer();
|
|
1192
1215
|
const bodyReady = this.ensureCurrentPlayerBody();
|
|
1193
1216
|
if (currentPlayer && bodyReady) {
|
|
1194
1217
|
currentPlayer.changeDirection(input);
|
|
@@ -1207,6 +1230,13 @@ export class RpgClientEngine<T = any> {
|
|
|
1207
1230
|
|
|
1208
1231
|
processAction({ action }: { action: number }) {
|
|
1209
1232
|
if (this.stopProcessingInput) return;
|
|
1233
|
+
const currentPlayer = this.sceneMap.getCurrentPlayer() as any;
|
|
1234
|
+
const canMove =
|
|
1235
|
+
!currentPlayer ||
|
|
1236
|
+
typeof currentPlayer.canMove !== "function" ||
|
|
1237
|
+
currentPlayer.canMove();
|
|
1238
|
+
if (!canMove) return;
|
|
1239
|
+
|
|
1210
1240
|
this.hooks.callHooks("client-engine-onInput", this, { input: 'action', playerId: this.playerId }).subscribe();
|
|
1211
1241
|
this.webSocket.emit('action', { action })
|
|
1212
1242
|
}
|
|
@@ -1337,6 +1367,15 @@ export class RpgClientEngine<T = any> {
|
|
|
1337
1367
|
if (!this.predictionEnabled || !this.prediction) {
|
|
1338
1368
|
return;
|
|
1339
1369
|
}
|
|
1370
|
+
const player = this.sceneMap?.getCurrentPlayer?.() as any;
|
|
1371
|
+
if (
|
|
1372
|
+
player &&
|
|
1373
|
+
typeof player.canMove === "function" &&
|
|
1374
|
+
!player.canMove()
|
|
1375
|
+
) {
|
|
1376
|
+
this.interruptCurrentPlayerMovement(player);
|
|
1377
|
+
return;
|
|
1378
|
+
}
|
|
1340
1379
|
const pendingInputs = this.prediction.getPendingInputs();
|
|
1341
1380
|
if (pendingInputs.length === 0) {
|
|
1342
1381
|
return;
|
|
@@ -1475,6 +1514,34 @@ export class RpgClientEngine<T = any> {
|
|
|
1475
1514
|
this.lastMovePathSentFrame = 0;
|
|
1476
1515
|
}
|
|
1477
1516
|
|
|
1517
|
+
/**
|
|
1518
|
+
* Stop local movement immediately and discard pending predicted movement.
|
|
1519
|
+
*
|
|
1520
|
+
* Use this before a blocking action such as an A-RPG attack, dialog, dash
|
|
1521
|
+
* startup, or any client-side state where already buffered movement inputs
|
|
1522
|
+
* must not be replayed after server reconciliation.
|
|
1523
|
+
*
|
|
1524
|
+
* @param player - Player object to stop. Defaults to the current player.
|
|
1525
|
+
* @returns `true` when a player was found and interrupted.
|
|
1526
|
+
*
|
|
1527
|
+
* @example
|
|
1528
|
+
* ```ts
|
|
1529
|
+
* engine.interruptCurrentPlayerMovement();
|
|
1530
|
+
* ```
|
|
1531
|
+
*/
|
|
1532
|
+
interruptCurrentPlayerMovement(player: any = this.sceneMap?.getCurrentPlayer?.()): boolean {
|
|
1533
|
+
if (!player) {
|
|
1534
|
+
return false;
|
|
1535
|
+
}
|
|
1536
|
+
(this.sceneMap as any)?.stopMovement?.(player);
|
|
1537
|
+
this.prediction?.clearPendingInputs();
|
|
1538
|
+
this.pendingPredictionFrames = [];
|
|
1539
|
+
this.lastInputTime = 0;
|
|
1540
|
+
this.lastMovePathSentAt = Date.now();
|
|
1541
|
+
this.lastMovePathSentFrame = this.inputFrameCounter;
|
|
1542
|
+
return true;
|
|
1543
|
+
}
|
|
1544
|
+
|
|
1478
1545
|
/**
|
|
1479
1546
|
* Trigger a flash animation on a sprite
|
|
1480
1547
|
*
|
|
@@ -1560,7 +1627,7 @@ export class RpgClientEngine<T = any> {
|
|
|
1560
1627
|
if (typeof ack.x !== "number" || typeof ack.y !== "number") {
|
|
1561
1628
|
return;
|
|
1562
1629
|
}
|
|
1563
|
-
const player = this.getCurrentPlayer();
|
|
1630
|
+
const player = this.getCurrentPlayer() as any;
|
|
1564
1631
|
const myId = this.playerIdSignal();
|
|
1565
1632
|
if (!player || !myId) {
|
|
1566
1633
|
return;
|
|
@@ -1583,10 +1650,14 @@ export class RpgClientEngine<T = any> {
|
|
|
1583
1650
|
authoritativeState: PredictionState<Direction>,
|
|
1584
1651
|
pendingInputs: PredictionHistoryEntry<Direction>[],
|
|
1585
1652
|
): void {
|
|
1586
|
-
const player = this.getCurrentPlayer();
|
|
1653
|
+
const player = this.getCurrentPlayer() as any;
|
|
1587
1654
|
if (!player) {
|
|
1588
1655
|
return;
|
|
1589
1656
|
}
|
|
1657
|
+
if (typeof player.canMove === "function" && !player.canMove()) {
|
|
1658
|
+
this.interruptCurrentPlayerMovement(player);
|
|
1659
|
+
return;
|
|
1660
|
+
}
|
|
1590
1661
|
|
|
1591
1662
|
(this.sceneMap as any).stopMovement(player);
|
|
1592
1663
|
this.applyAuthoritativeState(authoritativeState);
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
@for (graphicObj of graphicsSignals) {
|
|
10
10
|
<Sprite
|
|
11
11
|
sheet={sheet(graphicObj)}
|
|
12
|
+
scale={graphicScale(graphicObj)}
|
|
12
13
|
direction
|
|
13
14
|
tint
|
|
14
15
|
hitbox
|
|
@@ -377,6 +378,18 @@
|
|
|
377
378
|
};
|
|
378
379
|
}
|
|
379
380
|
|
|
381
|
+
const graphicScale = (graphicObject) => {
|
|
382
|
+
const scale = graphicObject?.scale;
|
|
383
|
+
if (Array.isArray(scale)) return scale;
|
|
384
|
+
if (typeof scale === 'number') return [scale, scale];
|
|
385
|
+
if (scale && typeof scale === 'object') {
|
|
386
|
+
const x = typeof scale.x === 'number' ? scale.x : 1;
|
|
387
|
+
const y = typeof scale.y === 'number' ? scale.y : x;
|
|
388
|
+
return [x, y];
|
|
389
|
+
}
|
|
390
|
+
return undefined;
|
|
391
|
+
}
|
|
392
|
+
|
|
380
393
|
// Combine animation change detection with movement state from smoothX/smoothY
|
|
381
394
|
const movementAnimations = ['walk', 'stand'];
|
|
382
395
|
const epsilon = 0; // movement threshold to consider the easing still running
|
|
@@ -467,4 +480,4 @@
|
|
|
467
480
|
tick(() => {
|
|
468
481
|
hooks.callHooks("client-sprite-onUpdate").subscribe()
|
|
469
482
|
})
|
|
470
|
-
</script>
|
|
483
|
+
</script>
|
|
@@ -29,10 +29,15 @@
|
|
|
29
29
|
</div>
|
|
30
30
|
</Navigation>
|
|
31
31
|
}
|
|
32
|
-
|
|
32
|
+
</div>
|
|
33
33
|
@if (hasFace()) {
|
|
34
34
|
<div class="rpg-ui-dialog-face">
|
|
35
|
-
<DOMSprite
|
|
35
|
+
<DOMSprite
|
|
36
|
+
sheet={faceSheet(dialogFace())}
|
|
37
|
+
width="100%"
|
|
38
|
+
height="100%"
|
|
39
|
+
objectFit="contain"
|
|
40
|
+
/>
|
|
36
41
|
</div>
|
|
37
42
|
}
|
|
38
43
|
</div>
|
|
@@ -76,7 +81,11 @@
|
|
|
76
81
|
|
|
77
82
|
const dialogPosition = computed(() => resolveProp(position) || "bottom");
|
|
78
83
|
const isFullWidth = computed(() => resolveProp(fullWidth) !== false);
|
|
79
|
-
const
|
|
84
|
+
const dialogFace = computed(() => resolveProp(face));
|
|
85
|
+
const hasFace = computed(() => {
|
|
86
|
+
const value = dialogFace();
|
|
87
|
+
return !!(value && value.id);
|
|
88
|
+
});
|
|
80
89
|
|
|
81
90
|
const displayMessage = signal("");
|
|
82
91
|
const fullMessage = signal("");
|
|
@@ -192,10 +201,10 @@
|
|
|
192
201
|
},
|
|
193
202
|
})
|
|
194
203
|
|
|
195
|
-
const faceSheet = (
|
|
204
|
+
const faceSheet = (faceValue) => {
|
|
196
205
|
return {
|
|
197
|
-
definition: engine.getSpriteSheet(
|
|
198
|
-
playing:
|
|
206
|
+
definition: engine.getSpriteSheet(faceValue.id),
|
|
207
|
+
playing: faceValue.expression || "default",
|
|
199
208
|
};
|
|
200
209
|
}
|
|
201
210
|
|