@rpgjs/client 5.0.0-alpha.0 → 5.0.0-alpha.10
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/AnimationManager.d.ts +8 -0
- package/dist/RpgClient.d.ts +99 -68
- package/dist/RpgClientEngine.d.ts +86 -4
- package/dist/components/animations/index.d.ts +4 -0
- package/dist/components/index.d.ts +2 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/index10.js +1 -1
- package/dist/index11.js +4 -4
- package/dist/index11.js.map +1 -1
- package/dist/index12.js +6 -2
- package/dist/index12.js.map +1 -1
- package/dist/index13.js +2 -2
- package/dist/index13.js.map +1 -1
- package/dist/index14.js +95 -35
- package/dist/index14.js.map +1 -1
- package/dist/index15.js +45 -186
- package/dist/index15.js.map +1 -1
- package/dist/index16.js +187 -5
- package/dist/index16.js.map +1 -1
- package/dist/index17.js +5 -383
- package/dist/index17.js.map +1 -1
- package/dist/index18.js +384 -28
- package/dist/index18.js.map +1 -1
- package/dist/index19.js +24 -17
- package/dist/index19.js.map +1 -1
- package/dist/index2.js +147 -25
- package/dist/index2.js.map +1 -1
- package/dist/index20.js +16 -2413
- package/dist/index20.js.map +1 -1
- package/dist/index21.js +2395 -88
- package/dist/index21.js.map +1 -1
- package/dist/index22.js +108 -103
- package/dist/index22.js.map +1 -1
- package/dist/index23.js +95 -57
- package/dist/index23.js.map +1 -1
- package/dist/index24.js +62 -12
- package/dist/index24.js.map +1 -1
- package/dist/index25.js +18 -37
- package/dist/index25.js.map +1 -1
- package/dist/index26.js +25 -3
- package/dist/index26.js.map +1 -1
- package/dist/index27.js +87 -314
- package/dist/index27.js.map +1 -1
- package/dist/index28.js +37 -21
- package/dist/index28.js.map +1 -1
- package/dist/index29.js +3 -9
- package/dist/index29.js.map +1 -1
- package/dist/index3.js +2 -2
- package/dist/index30.js +317 -6
- package/dist/index30.js.map +1 -1
- package/dist/index31.js +24 -171
- package/dist/index31.js.map +1 -1
- package/dist/index32.js +7 -497
- package/dist/index32.js.map +1 -1
- package/dist/index33.js +8 -9
- package/dist/index33.js.map +1 -1
- package/dist/index34.js +9 -4400
- package/dist/index34.js.map +1 -1
- package/dist/index35.js +4397 -85
- package/dist/index35.js.map +1 -1
- package/dist/index36.js +310 -55
- package/dist/index36.js.map +1 -1
- package/dist/index37.js +169 -15
- package/dist/index37.js.map +1 -1
- package/dist/index38.js +496 -15
- package/dist/index38.js.map +1 -1
- package/dist/index39.js +61 -0
- package/dist/index39.js.map +1 -0
- package/dist/index4.js +18 -5
- package/dist/index4.js.map +1 -1
- package/dist/index40.js +20 -0
- package/dist/index40.js.map +1 -0
- package/dist/index41.js +82 -0
- package/dist/index41.js.map +1 -0
- package/dist/index5.js +2 -1
- package/dist/index5.js.map +1 -1
- package/dist/index6.js +1 -1
- package/dist/index7.js +10 -2
- package/dist/index7.js.map +1 -1
- package/dist/index8.js +24 -6
- package/dist/index8.js.map +1 -1
- package/dist/index9.js +2 -2
- package/dist/presets/animation.d.ts +31 -0
- package/dist/presets/index.d.ts +102 -0
- package/dist/presets/lpc.d.ts +89 -0
- package/dist/services/loadMap.d.ts +123 -2
- package/dist/services/mmorpg.d.ts +7 -3
- package/package.json +14 -12
- package/src/Game/{EffectManager.ts → AnimationManager.ts} +2 -2
- package/src/Game/Object.ts +69 -0
- package/src/RpgClient.ts +101 -67
- package/src/RpgClientEngine.ts +159 -24
- package/src/components/{effects → animations}/animation.ce +3 -5
- package/src/components/{effects → animations}/index.ts +1 -1
- package/src/components/character.ce +74 -33
- package/src/components/index.ts +2 -1
- package/src/components/scenes/draw-map.ce +6 -23
- package/src/components/scenes/event-layer.ce +3 -3
- package/src/core/setup.ts +2 -0
- package/src/index.ts +1 -1
- package/src/module.ts +23 -5
- package/src/presets/animation.ts +46 -0
- package/src/presets/index.ts +5 -1
- package/src/presets/lpc.ts +108 -0
- package/src/services/loadMap.ts +131 -2
- package/src/services/mmorpg.ts +20 -4
- package/tsconfig.json +1 -1
- package/vite.config.ts +1 -1
- package/dist/Game/EffectManager.d.ts +0 -5
- package/dist/components/effects/index.d.ts +0 -4
- package/src/components/scenes/element-map.ce +0 -23
- /package/src/components/{effects → animations}/hit.ce +0 -0
package/src/RpgClientEngine.ts
CHANGED
|
@@ -7,10 +7,11 @@ import { Hooks, ModulesToken } from "@rpgjs/common";
|
|
|
7
7
|
import { load } from "@signe/sync";
|
|
8
8
|
import { RpgClientMap } from "./Game/Map"
|
|
9
9
|
import { RpgGui } from "./Gui/Gui";
|
|
10
|
-
import {
|
|
11
|
-
import { lastValueFrom } from "rxjs";
|
|
10
|
+
import { AnimationManager } from "./Game/AnimationManager";
|
|
11
|
+
import { lastValueFrom, Observable } from "rxjs";
|
|
12
12
|
import { GlobalConfigToken } from "./module";
|
|
13
|
-
import
|
|
13
|
+
import * as PIXI from "pixi.js";
|
|
14
|
+
import { PrebuiltComponentAnimations } from "./components/animations";
|
|
14
15
|
|
|
15
16
|
export class RpgClientEngine<T = any> {
|
|
16
17
|
private guiService: RpgGui;
|
|
@@ -22,17 +23,21 @@ export class RpgClientEngine<T = any> {
|
|
|
22
23
|
public globalConfig: T;
|
|
23
24
|
public sceneComponent: any;
|
|
24
25
|
stopProcessingInput = false;
|
|
25
|
-
|
|
26
26
|
width = signal("100%");
|
|
27
27
|
height = signal("100%");
|
|
28
28
|
spritesheets: Map<string, any> = new Map();
|
|
29
29
|
sounds: Map<string, any> = new Map();
|
|
30
|
-
|
|
30
|
+
componentAnimations: any[] = [];
|
|
31
31
|
particleSettings: {
|
|
32
32
|
emitters: any[]
|
|
33
33
|
} = {
|
|
34
34
|
emitters: []
|
|
35
35
|
}
|
|
36
|
+
renderer: PIXI.Renderer;
|
|
37
|
+
tick: Observable<number>;
|
|
38
|
+
playerIdSignal = signal<string | null>(null);
|
|
39
|
+
spriteComponentsBehind = signal<any[]>([]);
|
|
40
|
+
spriteComponentsInFront = signal<any[]>([]);
|
|
36
41
|
|
|
37
42
|
constructor(public context: Context) {
|
|
38
43
|
this.webSocket = inject(context, WebSocketToken);
|
|
@@ -40,20 +45,37 @@ export class RpgClientEngine<T = any> {
|
|
|
40
45
|
this.loadMapService = inject(context, LoadMapToken);
|
|
41
46
|
this.hooks = inject<Hooks>(context, ModulesToken);
|
|
42
47
|
this.globalConfig = inject(context, GlobalConfigToken)
|
|
48
|
+
|
|
49
|
+
this.addComponentAnimation({
|
|
50
|
+
id: "animation",
|
|
51
|
+
component: PrebuiltComponentAnimations.Animation
|
|
52
|
+
})
|
|
43
53
|
}
|
|
44
54
|
|
|
45
55
|
async start() {
|
|
46
56
|
this.selector = document.body.querySelector("#rpg") as HTMLElement;
|
|
47
57
|
|
|
48
|
-
await bootstrapCanvas(this.selector, Canvas);
|
|
58
|
+
const { app, canvasElement } = await bootstrapCanvas(this.selector, Canvas);
|
|
59
|
+
this.renderer = app.renderer as PIXI.Renderer;
|
|
60
|
+
this.tick = canvasElement?.propObservables?.context['tick'].observable
|
|
49
61
|
|
|
50
62
|
await lastValueFrom(this.hooks.callHooks("client-engine-onStart", this));
|
|
51
63
|
|
|
64
|
+
// wondow is resize
|
|
65
|
+
window.addEventListener('resize', () => {
|
|
66
|
+
this.hooks.callHooks("client-engine-onWindowResize", this).subscribe();
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
this.tick.subscribe((tick) => {
|
|
70
|
+
this.hooks.callHooks("client-engine-onStep", this, tick).subscribe();
|
|
71
|
+
})
|
|
72
|
+
|
|
52
73
|
this.hooks.callHooks("client-spritesheets-load", this).subscribe();
|
|
53
74
|
this.hooks.callHooks("client-sounds-load", this).subscribe();
|
|
54
75
|
this.hooks.callHooks("client-gui-load", this).subscribe();
|
|
55
76
|
this.hooks.callHooks("client-particles-load", this).subscribe();
|
|
56
|
-
this.hooks.callHooks("client-
|
|
77
|
+
this.hooks.callHooks("client-componentAnimations-load", this).subscribe();
|
|
78
|
+
this.hooks.callHooks("client-sprite-load", this).subscribe();
|
|
57
79
|
|
|
58
80
|
|
|
59
81
|
await this.webSocket.connection(() => {
|
|
@@ -64,6 +86,8 @@ export class RpgClientEngine<T = any> {
|
|
|
64
86
|
|
|
65
87
|
private initListeners() {
|
|
66
88
|
this.webSocket.on("sync", (data) => {
|
|
89
|
+
if (data.pId) this.playerIdSignal.set(data.pId)
|
|
90
|
+
this.hooks.callHooks("client-sceneMap-onChanges", this.sceneMap, { partial: data }).subscribe();
|
|
67
91
|
load(this.sceneMap, data, true);
|
|
68
92
|
});
|
|
69
93
|
|
|
@@ -71,14 +95,32 @@ export class RpgClientEngine<T = any> {
|
|
|
71
95
|
this.loadScene(data.mapId);
|
|
72
96
|
});
|
|
73
97
|
|
|
74
|
-
this.webSocket.on("
|
|
75
|
-
const { params, object, id } = data;
|
|
76
|
-
if (!object) {
|
|
77
|
-
throw new Error("
|
|
98
|
+
this.webSocket.on("showComponentAnimation", (data) => {
|
|
99
|
+
const { params, object, position, id } = data;
|
|
100
|
+
if (!object && position === undefined) {
|
|
101
|
+
throw new Error("Please provide an object or x and y coordinates");
|
|
78
102
|
}
|
|
79
|
-
const player = this.sceneMap.getObjectById(object);
|
|
80
|
-
this.
|
|
103
|
+
const player = object ? this.sceneMap.getObjectById(object) : undefined;
|
|
104
|
+
this.getComponentAnimation(id).displayEffect(params, player || position)
|
|
81
105
|
});
|
|
106
|
+
|
|
107
|
+
this.webSocket.on("setAnimation", (data) => {
|
|
108
|
+
const { animationName, nbTimes, object } = data;
|
|
109
|
+
const player = this.sceneMap.getObjectById(object);
|
|
110
|
+
player.setAnimation(animationName, nbTimes)
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
this.webSocket.on('open', () => {
|
|
114
|
+
this.hooks.callHooks("client-engine-onConnected", this, this.socket).subscribe();
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
this.webSocket.on('close', () => {
|
|
118
|
+
this.hooks.callHooks("client-engine-onDisconnected", this, this.socket).subscribe();
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
this.webSocket.on('error', (error) => {
|
|
122
|
+
this.hooks.callHooks("client-engine-onConnectError", this, error, this.socket).subscribe();
|
|
123
|
+
})
|
|
82
124
|
}
|
|
83
125
|
|
|
84
126
|
private async loadScene(mapId: string) {
|
|
@@ -108,34 +150,127 @@ export class RpgClientEngine<T = any> {
|
|
|
108
150
|
return particle;
|
|
109
151
|
}
|
|
110
152
|
|
|
111
|
-
|
|
153
|
+
/**
|
|
154
|
+
* Add a component to render behind sprites
|
|
155
|
+
* Components added with this method will be displayed with a lower z-index than the sprite
|
|
156
|
+
*
|
|
157
|
+
* @param component - The component to add behind sprites
|
|
158
|
+
* @returns The added component
|
|
159
|
+
*
|
|
160
|
+
* @example
|
|
161
|
+
* ```ts
|
|
162
|
+
* // Add a shadow component behind all sprites
|
|
163
|
+
* engine.addSpriteComponentBehind(ShadowComponent);
|
|
164
|
+
* ```
|
|
165
|
+
*/
|
|
166
|
+
addSpriteComponentBehind(component: any) {
|
|
167
|
+
this.spriteComponentsBehind.update((components: any[]) => [...components, component])
|
|
168
|
+
return component
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Add a component to render in front of sprites
|
|
173
|
+
* Components added with this method will be displayed with a higher z-index than the sprite
|
|
174
|
+
*
|
|
175
|
+
* @param component - The component to add in front of sprites
|
|
176
|
+
* @returns The added component
|
|
177
|
+
*
|
|
178
|
+
* @example
|
|
179
|
+
* ```ts
|
|
180
|
+
* // Add a health bar component in front of all sprites
|
|
181
|
+
* engine.addSpriteComponentInFront(HealthBarComponent);
|
|
182
|
+
* ```
|
|
183
|
+
*/
|
|
184
|
+
addSpriteComponentInFront(component: any) {
|
|
185
|
+
this.spriteComponentsInFront.update((components: any[]) => [...components, component])
|
|
186
|
+
return component
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Add a component animation to the engine
|
|
191
|
+
*
|
|
192
|
+
* Component animations are temporary visual effects that can be displayed
|
|
193
|
+
* on sprites or objects, such as hit indicators, spell effects, or status animations.
|
|
194
|
+
*
|
|
195
|
+
* @param componentAnimation - The component animation configuration
|
|
196
|
+
* @param componentAnimation.id - Unique identifier for the animation
|
|
197
|
+
* @param componentAnimation.component - The component function to render
|
|
198
|
+
* @returns The added component animation configuration
|
|
199
|
+
*
|
|
200
|
+
* @example
|
|
201
|
+
* ```ts
|
|
202
|
+
* // Add a hit animation component
|
|
203
|
+
* engine.addComponentAnimation({
|
|
204
|
+
* id: 'hit',
|
|
205
|
+
* component: HitComponent
|
|
206
|
+
* });
|
|
207
|
+
*
|
|
208
|
+
* // Add an explosion effect component
|
|
209
|
+
* engine.addComponentAnimation({
|
|
210
|
+
* id: 'explosion',
|
|
211
|
+
* component: ExplosionComponent
|
|
212
|
+
* });
|
|
213
|
+
* ```
|
|
214
|
+
*/
|
|
215
|
+
addComponentAnimation(componentAnimation: {
|
|
112
216
|
component: any,
|
|
113
217
|
id: string
|
|
114
218
|
}) {
|
|
115
|
-
const instance = new
|
|
116
|
-
this.
|
|
117
|
-
id:
|
|
118
|
-
component:
|
|
219
|
+
const instance = new AnimationManager()
|
|
220
|
+
this.componentAnimations.push({
|
|
221
|
+
id: componentAnimation.id,
|
|
222
|
+
component: componentAnimation.component,
|
|
119
223
|
instance: instance,
|
|
120
224
|
current: instance.current
|
|
121
225
|
})
|
|
122
|
-
return
|
|
226
|
+
return componentAnimation;
|
|
123
227
|
}
|
|
124
228
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
229
|
+
/**
|
|
230
|
+
* Get a component animation by its ID
|
|
231
|
+
*
|
|
232
|
+
* Retrieves the EffectManager instance for a specific component animation,
|
|
233
|
+
* which can be used to display the animation on sprites or objects.
|
|
234
|
+
*
|
|
235
|
+
* @param id - The unique identifier of the component animation
|
|
236
|
+
* @returns The EffectManager instance for the animation
|
|
237
|
+
* @throws Error if the component animation is not found
|
|
238
|
+
*
|
|
239
|
+
* @example
|
|
240
|
+
* ```ts
|
|
241
|
+
* // Get the hit animation and display it
|
|
242
|
+
* const hitAnimation = engine.getComponentAnimation('hit');
|
|
243
|
+
* hitAnimation.displayEffect({ text: "Critical!" }, player);
|
|
244
|
+
* ```
|
|
245
|
+
*/
|
|
246
|
+
getComponentAnimation(id: string): AnimationManager {
|
|
247
|
+
const componentAnimation = this.componentAnimations.find((componentAnimation) => componentAnimation.id === id)
|
|
248
|
+
if (!componentAnimation) {
|
|
249
|
+
throw new Error(`Component animation with id ${id} not found`)
|
|
129
250
|
}
|
|
130
|
-
return
|
|
251
|
+
return componentAnimation.instance
|
|
131
252
|
}
|
|
132
253
|
|
|
133
254
|
processInput({ input }: { input: number }) {
|
|
255
|
+
this.hooks.callHooks("client-engine-onInput", this, { input, playerId: this.playerId }).subscribe();
|
|
134
256
|
this.webSocket.emit('move', { input })
|
|
135
257
|
}
|
|
136
258
|
|
|
137
259
|
processAction({ action }: { action: number }) {
|
|
138
260
|
if (this.stopProcessingInput) return;
|
|
261
|
+
this.hooks.callHooks("client-engine-onInput", this, { input: 'action', playerId: this.playerId }).subscribe();
|
|
139
262
|
this.webSocket.emit('action', { action })
|
|
140
263
|
}
|
|
264
|
+
|
|
265
|
+
get PIXI() {
|
|
266
|
+
return PIXI
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
get socket() {
|
|
270
|
+
return this.webSocket
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
get playerId() {
|
|
274
|
+
return this.playerIdSignal()
|
|
275
|
+
}
|
|
141
276
|
}
|
|
@@ -1,19 +1,17 @@
|
|
|
1
1
|
<Sprite sheet x y anchor={0.5} />
|
|
2
2
|
|
|
3
3
|
<script>
|
|
4
|
-
import { signal } from "canvasengine";
|
|
5
4
|
import { RpgClientEngine } from "../../RpgClientEngine";
|
|
6
5
|
import { inject } from "../../core/inject";
|
|
7
6
|
|
|
8
|
-
const { x, y,
|
|
7
|
+
const { x, y, animationName, graphic, onFinish } = defineProps();
|
|
9
8
|
|
|
10
9
|
const client = inject(RpgClientEngine);
|
|
11
10
|
const spritesheets = client.spritesheets;
|
|
12
11
|
|
|
13
12
|
const sheet = {
|
|
14
|
-
definition: spritesheets.get(
|
|
15
|
-
playing: 'default',
|
|
13
|
+
definition: spritesheets.get(graphic()),
|
|
14
|
+
playing: animationName() ?? 'default',
|
|
16
15
|
onFinish
|
|
17
16
|
};
|
|
18
|
-
|
|
19
17
|
</script>
|
|
@@ -1,52 +1,57 @@
|
|
|
1
1
|
<Container x y zIndex={y} viewportFollow={isMe} controls>
|
|
2
|
+
@for (component of componentsBehind) {
|
|
3
|
+
<Container>
|
|
4
|
+
<component object />
|
|
5
|
+
</Container>
|
|
6
|
+
}
|
|
2
7
|
<Particle emit={@emitParticleTrigger} settings={@particleSettings} zIndex={1000} name={particleName} />
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
alpha={0.5}
|
|
14
|
-
/> -->
|
|
8
|
+
<Container>
|
|
9
|
+
@for (graphicId of graphics) {
|
|
10
|
+
<Sprite sheet={@sheet(@graphicId)} direction tint hitbox />
|
|
11
|
+
}
|
|
12
|
+
</Container>
|
|
13
|
+
@for (component of componentsInFront) {
|
|
14
|
+
<Container>
|
|
15
|
+
<component object />
|
|
16
|
+
</Container>
|
|
17
|
+
}
|
|
15
18
|
</Container>
|
|
16
19
|
|
|
17
20
|
<script>
|
|
18
|
-
import { signal, effect, mount, computed } from "canvasengine";
|
|
21
|
+
import { signal, effect, mount, computed, tick } from "canvasengine";
|
|
19
22
|
import { Particle } from "@canvasengine/presets";
|
|
20
|
-
import { GameEngineToken } from "@rpgjs/common";
|
|
23
|
+
import { GameEngineToken, ModulesToken } from "@rpgjs/common";
|
|
21
24
|
import { RpgClientEngine } from "../RpgClientEngine";
|
|
22
25
|
import { inject } from "../core/inject";
|
|
23
26
|
import { Direction } from "@rpgjs/common";
|
|
24
27
|
import Hit from "./effects/hit.ce";
|
|
25
28
|
|
|
26
|
-
const { object, id
|
|
27
|
-
|
|
29
|
+
const { object, id } = defineProps();
|
|
30
|
+
|
|
28
31
|
const client = inject(RpgClientEngine);
|
|
32
|
+
const hooks = inject(ModulesToken);
|
|
29
33
|
|
|
30
34
|
const spritesheets = client.spritesheets;
|
|
35
|
+
const playerId = client.playerId;
|
|
36
|
+
const componentsBehind = client.spriteComponentsBehind;
|
|
37
|
+
const componentsInFront = client.spriteComponentsInFront;
|
|
38
|
+
const isMe = computed(() => id() === playerId);
|
|
39
|
+
|
|
40
|
+
const {
|
|
41
|
+
x,
|
|
42
|
+
y,
|
|
43
|
+
tint,
|
|
44
|
+
direction,
|
|
45
|
+
animationName,
|
|
46
|
+
animationCurrentIndex,
|
|
47
|
+
emitParticleTrigger,
|
|
48
|
+
particleName,
|
|
49
|
+
graphics,
|
|
50
|
+
hitbox
|
|
51
|
+
} = object;
|
|
31
52
|
|
|
32
|
-
const x = object.x;
|
|
33
|
-
const y = object.y;
|
|
34
|
-
const tint = object.tint;
|
|
35
|
-
const direction = object.direction;
|
|
36
|
-
const animationName = object.animationName;
|
|
37
|
-
const emitParticleTrigger = object.emitParticleTrigger;
|
|
38
53
|
const particleSettings = client.particleSettings;
|
|
39
|
-
const particleName = object.particleName;
|
|
40
|
-
const graphics = object.graphics;
|
|
41
54
|
|
|
42
|
-
const hitbox = object.hitbox;
|
|
43
|
-
const widthShadow = 10;
|
|
44
|
-
const shadow = computed(() => ({
|
|
45
|
-
x: hitbox().w / 2,
|
|
46
|
-
y: hitbox().h - (hitbox().h / 2),
|
|
47
|
-
width: hitbox().w + widthShadow,
|
|
48
|
-
height: hitbox().h,
|
|
49
|
-
}))
|
|
50
55
|
const canControls = () => isMe() && object.canMove()
|
|
51
56
|
const keyboardControls = client.globalConfig.keyboardControls;
|
|
52
57
|
|
|
@@ -84,7 +89,7 @@
|
|
|
84
89
|
keyDown() {
|
|
85
90
|
if (canControls()) {
|
|
86
91
|
client.processAction({ action: 'action' })
|
|
87
|
-
|
|
92
|
+
// particleName.set('hit')
|
|
88
93
|
// emitParticleTrigger.start()
|
|
89
94
|
// object.flash('red')
|
|
90
95
|
}
|
|
@@ -99,6 +104,42 @@
|
|
|
99
104
|
params: {
|
|
100
105
|
direction
|
|
101
106
|
},
|
|
107
|
+
onFinish() {
|
|
108
|
+
animationCurrentIndex.update(index => index + 1)
|
|
109
|
+
}
|
|
102
110
|
};
|
|
103
111
|
}
|
|
112
|
+
|
|
113
|
+
// Track animation changes to reset animation state when needed
|
|
114
|
+
let previousAnimationName = animationName();
|
|
115
|
+
effect(() => {
|
|
116
|
+
const currentAnimationName = animationName();
|
|
117
|
+
|
|
118
|
+
// If animation changed externally (not through setAnimation), reset the state
|
|
119
|
+
if (currentAnimationName !== previousAnimationName && object.animationIsPlaying && object.animationIsPlaying()) {
|
|
120
|
+
// Check if this is a movement animation (walk, stand) that should interrupt custom animations
|
|
121
|
+
const movementAnimations = ['walk', 'stand'];
|
|
122
|
+
if (movementAnimations.includes(currentAnimationName)) {
|
|
123
|
+
if (typeof object.resetAnimationState === 'function') {
|
|
124
|
+
object.resetAnimationState();
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
previousAnimationName = currentAnimationName;
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
mount((element) => {
|
|
133
|
+
hooks.callHooks("client-sprite-onInit", element.componentInstance)
|
|
134
|
+
hooks.callHooks("client-sceneMap-onAddSprite", client.sceneMap, element.componentInstance)
|
|
135
|
+
|
|
136
|
+
return () => {
|
|
137
|
+
hooks.callHooks("client-sprite-onDestroy", element.componentInstance)
|
|
138
|
+
hooks.callHooks("client-sceneMap-onRemoveSprite", client.sceneMap, element.componentInstance)
|
|
139
|
+
}
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
tick(() => {
|
|
143
|
+
hooks.callHooks("client-sprite-onUpdate")
|
|
144
|
+
})
|
|
104
145
|
</script>
|
package/src/components/index.ts
CHANGED
|
@@ -1,28 +1,12 @@
|
|
|
1
1
|
<Container sound={backgroundMusic} >
|
|
2
2
|
<Container sound={backgroundAmbientSound} />
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
<sceneComponent() data={map.@data} />
|
|
4
|
+
<sceneComponent() data={map.@data} params={map.@params} />
|
|
7
5
|
|
|
8
|
-
|
|
9
|
-
@for (obj of middleLayer) {
|
|
10
|
-
<ElementMap ...obj image={map.@gridImage} scale />
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
@for ((event,id) of events) {
|
|
14
|
-
<Character id={id} object={event} isMe={false} />
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
@for ((player,id) of players) {
|
|
18
|
-
<Character id={id} object={player} isMe={true} />
|
|
19
|
-
}
|
|
20
|
-
</Container> -->
|
|
21
|
-
|
|
22
|
-
@for (effect of effects) {
|
|
6
|
+
@for (componentAnimation of componentAnimations) {
|
|
23
7
|
<Container>
|
|
24
|
-
@for (animation of
|
|
25
|
-
<
|
|
8
|
+
@for (animation of componentAnimation.current) {
|
|
9
|
+
<componentAnimation.component ...animation />
|
|
26
10
|
}
|
|
27
11
|
</Container>
|
|
28
12
|
}
|
|
@@ -34,17 +18,16 @@
|
|
|
34
18
|
import { RpgClientEngine } from "../../RpgClientEngine";
|
|
35
19
|
|
|
36
20
|
const engine = inject(RpgClientEngine);
|
|
37
|
-
const
|
|
21
|
+
const componentAnimations = engine.componentAnimations
|
|
38
22
|
const map = engine.sceneMap.data
|
|
39
23
|
const sceneComponent = computed(() => map()?.component)
|
|
24
|
+
const sceneParams = map()?.params
|
|
40
25
|
const mapParams = map().params
|
|
41
26
|
const animations = engine.sceneMap.animations
|
|
42
27
|
const backgroundMusic = { src: mapParams?.backgroundMusic, autoplay: true, loop: true }
|
|
43
28
|
const backgroundAmbientSound = { src: mapParams?.backgroundAmbientSound, autoplay: true, loop: true }
|
|
44
29
|
|
|
45
30
|
const data = signal(map().data)
|
|
46
|
-
// const middleLayer = computed(() => data().filter(obj => obj.layer === 1))
|
|
47
|
-
// const topLayer = computed(() => data().filter(obj => obj.layer === 2))
|
|
48
31
|
|
|
49
32
|
const scale = mapParams?.scale || 1
|
|
50
33
|
const worldWidth = (mapParams?.width || 2048) * scale
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
<Container sortableChildren={true}>
|
|
2
2
|
@for ((event,id) of events) {
|
|
3
|
-
<Character id={id} object={event}
|
|
3
|
+
<Character id={id} object={event} />
|
|
4
4
|
}
|
|
5
5
|
|
|
6
6
|
@for ((player,id) of players) {
|
|
7
|
-
<Character id={id} object={player}
|
|
7
|
+
<Character id={id} object={player} />
|
|
8
8
|
}
|
|
9
9
|
</Container>
|
|
10
10
|
|
|
11
11
|
<script>
|
|
12
|
-
import { effect, signal
|
|
12
|
+
import { effect, signal } from 'canvasengine'
|
|
13
13
|
import { inject } from "../../core/inject";
|
|
14
14
|
import { RpgClientEngine } from "../../RpgClientEngine";
|
|
15
15
|
import Character from "../character.ce";
|
package/src/core/setup.ts
CHANGED
package/src/index.ts
CHANGED
|
@@ -8,6 +8,6 @@ export * from "./services/loadMap";
|
|
|
8
8
|
export * from "./module";
|
|
9
9
|
export * from "./Gui/Gui";
|
|
10
10
|
export * from "./components/gui";
|
|
11
|
-
export * from "./components/
|
|
11
|
+
export * from "./components/animations";
|
|
12
12
|
export * from "./presets";
|
|
13
13
|
export * from "./components";
|
package/src/module.ts
CHANGED
|
@@ -43,12 +43,12 @@ export function provideClientModules(modules: RpgClient[]) {
|
|
|
43
43
|
},
|
|
44
44
|
};
|
|
45
45
|
}
|
|
46
|
-
if (module.
|
|
47
|
-
const
|
|
48
|
-
module.
|
|
46
|
+
if (module.componentAnimations) {
|
|
47
|
+
const componentAnimations = [...module.componentAnimations];
|
|
48
|
+
module.componentAnimations = {
|
|
49
49
|
load: (engine: RpgClientEngine) => {
|
|
50
|
-
|
|
51
|
-
engine.
|
|
50
|
+
componentAnimations.forEach((componentAnimation) => {
|
|
51
|
+
engine.addComponentAnimation(componentAnimation);
|
|
52
52
|
});
|
|
53
53
|
},
|
|
54
54
|
};
|
|
@@ -63,6 +63,24 @@ export function provideClientModules(modules: RpgClient[]) {
|
|
|
63
63
|
},
|
|
64
64
|
};
|
|
65
65
|
}
|
|
66
|
+
if (module.sprite) {
|
|
67
|
+
const sprite = {...module.sprite};
|
|
68
|
+
module.sprite = {
|
|
69
|
+
...sprite,
|
|
70
|
+
load: (engine: RpgClientEngine) => {
|
|
71
|
+
if (sprite.componentsBehind) {
|
|
72
|
+
sprite.componentsBehind.forEach((component) => {
|
|
73
|
+
engine.addSpriteComponentBehind(component);
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
if (sprite.componentsInFront) {
|
|
77
|
+
sprite.componentsInFront.forEach((component) => {
|
|
78
|
+
engine.addSpriteComponentInFront(component);
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
}
|
|
66
84
|
return module;
|
|
67
85
|
});
|
|
68
86
|
return modules
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creates an animation spritesheet preset with automatic frame generation
|
|
3
|
+
*
|
|
4
|
+
* This function generates animation frames based on the provided width and height dimensions.
|
|
5
|
+
* It creates a sequence of frames that progresses through the spritesheet from left to right,
|
|
6
|
+
* top to bottom, with each frame having a 10ms time increment.
|
|
7
|
+
*
|
|
8
|
+
* @param {number} framesWidth - The number of frames horizontally in the spritesheet
|
|
9
|
+
* @param {number} framesHeight - The number of frames vertically in the spritesheet
|
|
10
|
+
* @returns {Object} Animation preset configuration object
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```javascript
|
|
14
|
+
* // For a 4x4 spritesheet
|
|
15
|
+
* const preset = AnimationSpritesheetPreset(4, 4);
|
|
16
|
+
* // This will generate 16 frames with coordinates from (0,0) to (3,3)
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
export const AnimationSpritesheetPreset = (framesWidth: number, framesHeight: number) => {
|
|
20
|
+
|
|
21
|
+
const animations: Array<{ time: number; frameX: number; frameY: number }> = [];
|
|
22
|
+
|
|
23
|
+
for (let y = 0; y < framesHeight; y++) {
|
|
24
|
+
for (let x = 0; x < framesWidth; x++) {
|
|
25
|
+
const frameIndex = y * framesWidth + x;
|
|
26
|
+
animations.push({
|
|
27
|
+
time: frameIndex * 10,
|
|
28
|
+
frameX: x,
|
|
29
|
+
frameY: y
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return {
|
|
35
|
+
framesWidth,
|
|
36
|
+
framesHeight,
|
|
37
|
+
textures: {
|
|
38
|
+
default: {
|
|
39
|
+
animations: () => [
|
|
40
|
+
animations
|
|
41
|
+
],
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
|
package/src/presets/index.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
|
+
import { AnimationSpritesheetPreset } from "./animation";
|
|
2
|
+
import { LPCSpritesheetPreset } from "./lpc";
|
|
1
3
|
import { RMSpritesheet } from "./rmspritesheet";
|
|
2
4
|
|
|
3
5
|
export const Presets = {
|
|
4
|
-
RMSpritesheet
|
|
6
|
+
RMSpritesheet,
|
|
7
|
+
LPCSpritesheetPreset,
|
|
8
|
+
AnimationSpritesheetPreset
|
|
5
9
|
}
|