@rpgjs/client 5.0.0-alpha.2 → 5.0.0-alpha.20
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/Game/Map.d.ts +7 -1
- package/dist/Gui/Gui.d.ts +128 -5
- package/dist/RpgClient.d.ts +217 -59
- package/dist/RpgClientEngine.d.ts +345 -6
- package/dist/Sound.d.ts +199 -0
- package/dist/components/animations/index.d.ts +4 -0
- package/dist/components/dynamics/parse-value.d.ts +1 -0
- package/dist/components/gui/index.d.ts +3 -3
- package/dist/components/index.d.ts +3 -1
- package/dist/components/prebuilt/index.d.ts +18 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.js +9 -4
- package/dist/index.js.map +1 -1
- package/dist/index10.js +149 -4
- package/dist/index10.js.map +1 -1
- package/dist/index11.js +21 -7
- package/dist/index11.js.map +1 -1
- package/dist/index12.js +6 -4
- package/dist/index12.js.map +1 -1
- package/dist/index13.js +11 -14
- package/dist/index13.js.map +1 -1
- package/dist/index14.js +8 -40
- package/dist/index14.js.map +1 -1
- package/dist/index15.js +187 -180
- package/dist/index15.js.map +1 -1
- package/dist/index16.js +104 -7
- package/dist/index16.js.map +1 -1
- package/dist/index17.js +82 -372
- package/dist/index17.js.map +1 -1
- package/dist/index18.js +361 -26
- package/dist/index18.js.map +1 -1
- package/dist/index19.js +46 -20
- package/dist/index19.js.map +1 -1
- package/dist/index2.js +683 -32
- package/dist/index2.js.map +1 -1
- package/dist/index20.js +5 -2417
- package/dist/index20.js.map +1 -1
- package/dist/index21.js +383 -97
- package/dist/index21.js.map +1 -1
- package/dist/index22.js +41 -104
- package/dist/index22.js.map +1 -1
- package/dist/index23.js +21 -67
- package/dist/index23.js.map +1 -1
- package/dist/index24.js +2632 -20
- package/dist/index24.js.map +1 -1
- package/dist/index25.js +107 -34
- package/dist/index25.js.map +1 -1
- package/dist/index26.js +69 -3
- package/dist/index26.js.map +1 -1
- package/dist/index27.js +17 -318
- package/dist/index27.js.map +1 -1
- package/dist/index28.js +24 -22
- package/dist/index28.js.map +1 -1
- package/dist/index29.js +92 -8
- package/dist/index29.js.map +1 -1
- package/dist/index3.js +68 -8
- package/dist/index3.js.map +1 -1
- package/dist/index30.js +37 -7
- package/dist/index30.js.map +1 -1
- package/dist/index31.js +18 -168
- package/dist/index31.js.map +1 -1
- package/dist/index32.js +3 -499
- package/dist/index32.js.map +1 -1
- package/dist/index33.js +332 -9
- package/dist/index33.js.map +1 -1
- package/dist/index34.js +24 -4400
- package/dist/index34.js.map +1 -1
- package/dist/index35.js +6 -311
- package/dist/index35.js.map +1 -1
- package/dist/index36.js +8 -88
- package/dist/index36.js.map +1 -1
- package/dist/index37.js +182 -56
- package/dist/index37.js.map +1 -1
- package/dist/index38.js +500 -16
- package/dist/index38.js.map +1 -1
- package/dist/index39.js +10 -18
- package/dist/index39.js.map +1 -1
- package/dist/index4.js +23 -5
- package/dist/index4.js.map +1 -1
- package/dist/index40.js +7 -0
- package/dist/index40.js.map +1 -0
- package/dist/index41.js +3690 -0
- package/dist/index41.js.map +1 -0
- package/dist/index42.js +77 -0
- package/dist/index42.js.map +1 -0
- package/dist/index43.js +6 -0
- package/dist/index43.js.map +1 -0
- package/dist/index44.js +20 -0
- package/dist/index44.js.map +1 -0
- package/dist/index45.js +146 -0
- package/dist/index45.js.map +1 -0
- package/dist/index46.js +12 -0
- package/dist/index46.js.map +1 -0
- package/dist/index47.js +113 -0
- package/dist/index47.js.map +1 -0
- package/dist/index48.js +136 -0
- package/dist/index48.js.map +1 -0
- package/dist/index49.js +137 -0
- package/dist/index49.js.map +1 -0
- package/dist/index5.js +2 -1
- package/dist/index5.js.map +1 -1
- package/dist/index50.js +112 -0
- package/dist/index50.js.map +1 -0
- package/dist/index51.js +141 -0
- package/dist/index51.js.map +1 -0
- package/dist/index52.js +9 -0
- package/dist/index52.js.map +1 -0
- package/dist/index53.js +54 -0
- package/dist/index53.js.map +1 -0
- package/dist/index6.js +1 -1
- package/dist/index6.js.map +1 -1
- package/dist/index7.js +11 -3
- package/dist/index7.js.map +1 -1
- package/dist/index8.js +68 -7
- package/dist/index8.js.map +1 -1
- package/dist/index9.js +230 -15
- package/dist/index9.js.map +1 -1
- package/dist/presets/animation.d.ts +31 -0
- package/dist/presets/faceset.d.ts +30 -0
- package/dist/presets/index.d.ts +103 -0
- package/dist/presets/lpc.d.ts +89 -0
- package/dist/services/loadMap.d.ts +123 -2
- package/dist/services/mmorpg.d.ts +9 -4
- package/dist/services/standalone.d.ts +51 -2
- package/package.json +22 -18
- package/src/Game/{EffectManager.ts → AnimationManager.ts} +3 -2
- package/src/Game/Map.ts +20 -2
- package/src/Game/Object.ts +163 -9
- package/src/Gui/Gui.ts +300 -17
- package/src/RpgClient.ts +222 -58
- package/src/RpgClientEngine.ts +804 -36
- package/src/Sound.ts +253 -0
- package/src/components/{effects → animations}/animation.ce +3 -6
- package/src/components/{effects → animations}/index.ts +1 -1
- package/src/components/character.ce +165 -37
- package/src/components/dynamics/parse-value.ts +80 -0
- package/src/components/dynamics/text.ce +183 -0
- package/src/components/gui/box.ce +17 -0
- package/src/components/gui/dialogbox/index.ce +73 -35
- package/src/components/gui/dialogbox/selection.ce +16 -1
- package/src/components/gui/index.ts +3 -4
- package/src/components/index.ts +5 -1
- package/src/components/prebuilt/hp-bar.ce +255 -0
- package/src/components/prebuilt/index.ts +21 -0
- package/src/components/scenes/draw-map.ce +6 -23
- package/src/components/scenes/event-layer.ce +9 -3
- package/src/core/setup.ts +2 -0
- package/src/index.ts +5 -2
- package/src/module.ts +72 -6
- package/src/presets/animation.ts +46 -0
- package/src/presets/faceset.ts +60 -0
- package/src/presets/index.ts +7 -1
- package/src/presets/lpc.ts +108 -0
- package/src/services/loadMap.ts +132 -3
- package/src/services/mmorpg.ts +27 -5
- package/src/services/standalone.ts +68 -6
- 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
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prebuilt sprite components for common UI elements
|
|
3
|
+
*
|
|
4
|
+
* This module exports ready-to-use components that can be attached
|
|
5
|
+
* to sprites using componentsInFront or componentsBehind configuration.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```ts
|
|
9
|
+
* import { HpBar } from '@rpgjs/client/components/prebuilt';
|
|
10
|
+
*
|
|
11
|
+
* export default defineModule<RpgClient>({
|
|
12
|
+
* sprite: {
|
|
13
|
+
* componentsInFront: [HpBar]
|
|
14
|
+
* }
|
|
15
|
+
* })
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
export { default as HpBar } from './hp-bar.ce';
|
|
20
|
+
|
|
21
|
+
|
|
@@ -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,20 +1,26 @@
|
|
|
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
|
+
}
|
|
9
|
+
|
|
10
|
+
@for (child of children) {
|
|
11
|
+
<child />
|
|
8
12
|
}
|
|
9
13
|
</Container>
|
|
10
14
|
|
|
11
15
|
<script>
|
|
12
|
-
import { effect, signal
|
|
16
|
+
import { effect, signal } from 'canvasengine'
|
|
13
17
|
import { inject } from "../../core/inject";
|
|
14
18
|
import { RpgClientEngine } from "../../RpgClientEngine";
|
|
15
19
|
import Character from "../character.ce";
|
|
16
20
|
|
|
17
21
|
const engine = inject(RpgClientEngine);
|
|
22
|
+
const { children } = defineProps()
|
|
23
|
+
|
|
18
24
|
const players = engine.sceneMap.players
|
|
19
25
|
const events = engine.sceneMap.events
|
|
20
26
|
</script>
|
package/src/core/setup.ts
CHANGED
package/src/index.ts
CHANGED
|
@@ -8,6 +8,9 @@ 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
|
-
export * from "./components";
|
|
13
|
+
export * from "./components";
|
|
14
|
+
export * from "./components/gui";
|
|
15
|
+
export * from "./Sound";
|
|
16
|
+
export { Context } from "@signe/di";
|
package/src/module.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { RpgClientEngine } from "./RpgClientEngine";
|
|
|
3
3
|
import { RpgClient } from "./RpgClient";
|
|
4
4
|
import { inject } from "@signe/di";
|
|
5
5
|
import { RpgGui } from "./Gui/Gui";
|
|
6
|
+
import { getSoundMetadata } from "./Sound";
|
|
6
7
|
|
|
7
8
|
export function provideClientModules(modules: RpgClient[]) {
|
|
8
9
|
return provideModules(modules, "client", (modules, context) => {
|
|
@@ -22,16 +23,63 @@ export function provideClientModules(modules: RpgClient[]) {
|
|
|
22
23
|
},
|
|
23
24
|
};
|
|
24
25
|
}
|
|
26
|
+
if (module.spritesheetResolver) {
|
|
27
|
+
const resolver = module.spritesheetResolver;
|
|
28
|
+
module.spritesheetResolver = {
|
|
29
|
+
load: (engine: RpgClientEngine) => {
|
|
30
|
+
engine.setSpritesheetResolver(resolver);
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
}
|
|
25
34
|
if (module.sounds) {
|
|
26
35
|
const sounds = [...module.sounds];
|
|
27
36
|
module.sounds = {
|
|
28
37
|
load: (engine: RpgClientEngine) => {
|
|
29
38
|
sounds.forEach((sound) => {
|
|
30
|
-
|
|
39
|
+
// Check if it's a class decorated with @Sound
|
|
40
|
+
if (typeof sound === 'function' || (sound && sound.constructor && sound.constructor !== Object)) {
|
|
41
|
+
const metadata = getSoundMetadata(sound);
|
|
42
|
+
if (metadata) {
|
|
43
|
+
// Handle single sound
|
|
44
|
+
if (metadata.id && metadata.sound) {
|
|
45
|
+
engine.addSound({
|
|
46
|
+
id: metadata.id,
|
|
47
|
+
src: metadata.sound,
|
|
48
|
+
loop: metadata.loop,
|
|
49
|
+
volume: metadata.volume,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
// Handle multiple sounds
|
|
53
|
+
if (metadata.sounds) {
|
|
54
|
+
Object.entries(metadata.sounds).forEach(([soundId, soundSrc]) => {
|
|
55
|
+
engine.addSound({
|
|
56
|
+
id: soundId,
|
|
57
|
+
src: soundSrc,
|
|
58
|
+
loop: metadata.loop,
|
|
59
|
+
volume: metadata.volume,
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
} else {
|
|
64
|
+
// Not a decorated class, treat as regular sound object
|
|
65
|
+
engine.addSound(sound);
|
|
66
|
+
}
|
|
67
|
+
} else {
|
|
68
|
+
// Regular sound object
|
|
69
|
+
engine.addSound(sound);
|
|
70
|
+
}
|
|
31
71
|
});
|
|
32
72
|
},
|
|
33
73
|
};
|
|
34
74
|
}
|
|
75
|
+
if (module.soundResolver) {
|
|
76
|
+
const resolver = module.soundResolver;
|
|
77
|
+
module.soundResolver = {
|
|
78
|
+
load: (engine: RpgClientEngine) => {
|
|
79
|
+
engine.setSoundResolver(resolver);
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
}
|
|
35
83
|
if (module.gui) {
|
|
36
84
|
const gui = [...module.gui];
|
|
37
85
|
module.gui = {
|
|
@@ -43,12 +91,12 @@ export function provideClientModules(modules: RpgClient[]) {
|
|
|
43
91
|
},
|
|
44
92
|
};
|
|
45
93
|
}
|
|
46
|
-
if (module.
|
|
47
|
-
const
|
|
48
|
-
module.
|
|
94
|
+
if (module.componentAnimations) {
|
|
95
|
+
const componentAnimations = [...module.componentAnimations];
|
|
96
|
+
module.componentAnimations = {
|
|
49
97
|
load: (engine: RpgClientEngine) => {
|
|
50
|
-
|
|
51
|
-
engine.
|
|
98
|
+
componentAnimations.forEach((componentAnimation) => {
|
|
99
|
+
engine.addComponentAnimation(componentAnimation);
|
|
52
100
|
});
|
|
53
101
|
},
|
|
54
102
|
};
|
|
@@ -63,6 +111,24 @@ export function provideClientModules(modules: RpgClient[]) {
|
|
|
63
111
|
},
|
|
64
112
|
};
|
|
65
113
|
}
|
|
114
|
+
if (module.sprite) {
|
|
115
|
+
const sprite = {...module.sprite};
|
|
116
|
+
module.sprite = {
|
|
117
|
+
...sprite,
|
|
118
|
+
load: (engine: RpgClientEngine) => {
|
|
119
|
+
if (sprite.componentsBehind) {
|
|
120
|
+
sprite.componentsBehind.forEach((component) => {
|
|
121
|
+
engine.addSpriteComponentBehind(component);
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
if (sprite.componentsInFront) {
|
|
125
|
+
sprite.componentsInFront.forEach((component) => {
|
|
126
|
+
engine.addSpriteComponentInFront(component);
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
};
|
|
131
|
+
}
|
|
66
132
|
return module;
|
|
67
133
|
});
|
|
68
134
|
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
|
+
|
|
@@ -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,5 +1,11 @@
|
|
|
1
|
+
import { AnimationSpritesheetPreset } from "./animation";
|
|
2
|
+
import { LPCSpritesheetPreset } from "./lpc";
|
|
1
3
|
import { RMSpritesheet } from "./rmspritesheet";
|
|
4
|
+
import { FacesetPreset } from "./faceset";
|
|
2
5
|
|
|
3
6
|
export const Presets = {
|
|
4
|
-
RMSpritesheet
|
|
7
|
+
RMSpritesheet,
|
|
8
|
+
LPCSpritesheetPreset,
|
|
9
|
+
AnimationSpritesheetPreset,
|
|
10
|
+
FacesetPreset
|
|
5
11
|
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { Animation, Direction } from "@rpgjs/common";
|
|
2
|
+
|
|
3
|
+
export const LPCSpritesheetPreset = (options: {
|
|
4
|
+
id: string;
|
|
5
|
+
imageSource: string;
|
|
6
|
+
width: number;
|
|
7
|
+
height: number;
|
|
8
|
+
ratio?: number;
|
|
9
|
+
}) => {
|
|
10
|
+
const ratio = options.ratio ?? 1;
|
|
11
|
+
|
|
12
|
+
const frameY = (direction: Direction) => {
|
|
13
|
+
return {
|
|
14
|
+
[Direction.Down]: 2,
|
|
15
|
+
[Direction.Left]: 1,
|
|
16
|
+
[Direction.Right]: 3,
|
|
17
|
+
[Direction.Up]: 0,
|
|
18
|
+
}[direction];
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const stand = (direction: Direction) => [
|
|
22
|
+
{ time: 0, frameX: 0, frameY: frameY(direction) },
|
|
23
|
+
];
|
|
24
|
+
const anim = (
|
|
25
|
+
direction: Direction,
|
|
26
|
+
framesWidth: number,
|
|
27
|
+
speed: number = 5
|
|
28
|
+
) => {
|
|
29
|
+
const array: any = [];
|
|
30
|
+
for (let i = 0; i < framesWidth; i++) {
|
|
31
|
+
array.push({ time: i * speed, frameX: i, frameY: frameY(direction) });
|
|
32
|
+
}
|
|
33
|
+
return array;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
id: options.id,
|
|
38
|
+
image: options.imageSource,
|
|
39
|
+
width: options.width,
|
|
40
|
+
height: options.height,
|
|
41
|
+
opacity: 1,
|
|
42
|
+
rectWidth: 64 * ratio,
|
|
43
|
+
rectHeight: 64 * ratio,
|
|
44
|
+
framesWidth: 6,
|
|
45
|
+
framesHeight: 4,
|
|
46
|
+
spriteRealSize: {
|
|
47
|
+
width: 48 * ratio,
|
|
48
|
+
height: 52 * ratio,
|
|
49
|
+
},
|
|
50
|
+
textures: {
|
|
51
|
+
[Animation.Stand]: {
|
|
52
|
+
offset: {
|
|
53
|
+
x: 0,
|
|
54
|
+
y: 512 * ratio,
|
|
55
|
+
},
|
|
56
|
+
animations: ({ direction }) => [stand(direction)],
|
|
57
|
+
},
|
|
58
|
+
[Animation.Walk]: {
|
|
59
|
+
offset: {
|
|
60
|
+
x: 0,
|
|
61
|
+
y: 512 * ratio,
|
|
62
|
+
},
|
|
63
|
+
framesWidth: 9,
|
|
64
|
+
framesHeight: 4,
|
|
65
|
+
animations: ({ direction }) => [anim(direction, 9)],
|
|
66
|
+
},
|
|
67
|
+
[Animation.Attack]: {
|
|
68
|
+
offset: {
|
|
69
|
+
x: 0,
|
|
70
|
+
y: 768 * ratio,
|
|
71
|
+
},
|
|
72
|
+
framesWidth: 6,
|
|
73
|
+
framesHeight: 4,
|
|
74
|
+
animations: ({ direction }) => [anim(direction, 6, 3)],
|
|
75
|
+
},
|
|
76
|
+
[Animation.Skill]: {
|
|
77
|
+
framesWidth: 7,
|
|
78
|
+
framesHeight: 4,
|
|
79
|
+
animations: ({ direction }) => [anim(direction, 7, 3)],
|
|
80
|
+
},
|
|
81
|
+
attack2: {
|
|
82
|
+
offset: {
|
|
83
|
+
x: 0,
|
|
84
|
+
y: 256 * ratio,
|
|
85
|
+
},
|
|
86
|
+
framesWidth: 7,
|
|
87
|
+
framesHeight: 8,
|
|
88
|
+
animations: ({ direction }) => [anim(direction, 7, 3)],
|
|
89
|
+
},
|
|
90
|
+
...(options.height > 3000
|
|
91
|
+
? {
|
|
92
|
+
attack3: {
|
|
93
|
+
offset: {
|
|
94
|
+
x: 0,
|
|
95
|
+
y: 5568 - 288 * 4,
|
|
96
|
+
},
|
|
97
|
+
rectWidth: 288,
|
|
98
|
+
rectHeight: 288,
|
|
99
|
+
framesWidth: 6,
|
|
100
|
+
framesHeight: 4,
|
|
101
|
+
animations: ({ direction }) => [anim(direction, 6, 3)],
|
|
102
|
+
},
|
|
103
|
+
}
|
|
104
|
+
: {}),
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
};
|
|
108
|
+
|
package/src/services/loadMap.ts
CHANGED
|
@@ -3,27 +3,156 @@ import { UpdateMapToken, UpdateMapService } from "@rpgjs/common";
|
|
|
3
3
|
|
|
4
4
|
export const LoadMapToken = 'LoadMapToken'
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
/**
|
|
7
|
+
* Represents the structure of map data that should be returned by the load map callback.
|
|
8
|
+
* This interface defines all the properties that can be provided when loading a custom map.
|
|
9
|
+
*
|
|
10
|
+
* @interface MapData
|
|
11
|
+
*/
|
|
12
|
+
type MapData = {
|
|
13
|
+
/** Raw map data that will be passed to the map component */
|
|
14
|
+
data: any;
|
|
15
|
+
/** CanvasEngine component that will render the map */
|
|
16
|
+
component: any;
|
|
17
|
+
/** Optional map width in pixels, used for viewport calculations */
|
|
18
|
+
width?: number;
|
|
19
|
+
/** Optional map height in pixels, used for viewport calculations */
|
|
20
|
+
height?: number;
|
|
21
|
+
/** Optional map events data (NPCs, interactive objects, etc.) */
|
|
22
|
+
events?: any;
|
|
23
|
+
/** Optional map identifier, defaults to the mapId parameter if not provided */
|
|
24
|
+
id?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Callback function type for loading map data.
|
|
29
|
+
* This function receives a map ID and should return either a MapData object directly
|
|
30
|
+
* or a Promise that resolves to a MapData object.
|
|
31
|
+
*
|
|
32
|
+
* @callback LoadMapOptions
|
|
33
|
+
* @param {string} mapId - The identifier of the map to load
|
|
34
|
+
* @returns {Promise<MapData> | MapData} The map data object or a promise resolving to it
|
|
35
|
+
*/
|
|
36
|
+
export type LoadMapOptions = (mapId: string) => Promise<MapData> | MapData
|
|
7
37
|
|
|
8
38
|
export class LoadMapService {
|
|
9
39
|
private updateMapService: UpdateMapService;
|
|
10
40
|
|
|
11
41
|
constructor(private context: Context, private options: LoadMapOptions) {
|
|
42
|
+
if (context['side'] === 'server') {
|
|
43
|
+
return
|
|
44
|
+
}
|
|
12
45
|
this.updateMapService = inject(context, UpdateMapToken);
|
|
13
46
|
}
|
|
14
47
|
|
|
15
48
|
async load(mapId: string) {
|
|
16
49
|
const map = await this.options(mapId.replace('map-', ''))
|
|
17
|
-
await this.updateMapService.update(
|
|
50
|
+
await this.updateMapService.update(map);
|
|
18
51
|
return map;
|
|
19
52
|
}
|
|
20
53
|
}
|
|
21
54
|
|
|
55
|
+
/**
|
|
56
|
+
* Creates a dependency injection configuration for custom map loading on the client side.
|
|
57
|
+
*
|
|
58
|
+
* This function allows you to customize how maps are loaded and displayed by providing
|
|
59
|
+
* a callback that defines custom map data and rendering components. It's designed to work
|
|
60
|
+
* with the RPG-JS dependency injection system and enables integration of custom map formats
|
|
61
|
+
* like Tiled TMX files or any other map data structure.
|
|
62
|
+
*
|
|
63
|
+
* The function sets up the necessary service providers for map loading, including:
|
|
64
|
+
* - UpdateMapToken: Handles map updates in the client context
|
|
65
|
+
* - LoadMapToken: Provides the LoadMapService with your custom loading logic
|
|
66
|
+
*
|
|
67
|
+
* **Design Concept:**
|
|
68
|
+
* The function follows the provider pattern, creating a modular way to inject custom
|
|
69
|
+
* map loading behavior into the RPG-JS client engine. It separates the concern of
|
|
70
|
+
* map data fetching from map rendering, allowing developers to focus on their specific
|
|
71
|
+
* map format while leveraging the engine's rendering capabilities.
|
|
72
|
+
*
|
|
73
|
+
* @param {LoadMapOptions} options - Callback function that handles map loading logic
|
|
74
|
+
* @returns {Array<Object>} Array of dependency injection provider configurations
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* ```typescript
|
|
78
|
+
* import { provideLoadMap } from '@rpgjs/client'
|
|
79
|
+
* import { createModule } from '@rpgjs/common'
|
|
80
|
+
* import MyTiledMapComponent from './MyTiledMapComponent.ce'
|
|
81
|
+
*
|
|
82
|
+
* // Basic usage with JSON map data
|
|
83
|
+
* export function provideCustomMap() {
|
|
84
|
+
* return createModule("CustomMap", [
|
|
85
|
+
* provideLoadMap(async (mapId) => {
|
|
86
|
+
* const response = await fetch(`/maps/${mapId}.json`)
|
|
87
|
+
* const mapData = await response.json()
|
|
88
|
+
*
|
|
89
|
+
* return {
|
|
90
|
+
* data: mapData,
|
|
91
|
+
* component: MyTiledMapComponent,
|
|
92
|
+
* width: mapData.width,
|
|
93
|
+
* height: mapData.height,
|
|
94
|
+
* events: mapData.events
|
|
95
|
+
* }
|
|
96
|
+
* })
|
|
97
|
+
* ])
|
|
98
|
+
* }
|
|
99
|
+
*
|
|
100
|
+
* // Advanced usage with Tiled TMX files
|
|
101
|
+
* export function provideTiledMap() {
|
|
102
|
+
* return createModule("TiledMap", [
|
|
103
|
+
* provideLoadMap(async (mapId) => {
|
|
104
|
+
* // Load TMX file
|
|
105
|
+
* const tmxResponse = await fetch(`/tiled/${mapId}.tmx`)
|
|
106
|
+
* const tmxData = await tmxResponse.text()
|
|
107
|
+
*
|
|
108
|
+
* // Parse TMX data (using a TMX parser)
|
|
109
|
+
* const parsedMap = parseTMX(tmxData)
|
|
110
|
+
*
|
|
111
|
+
* return {
|
|
112
|
+
* data: parsedMap,
|
|
113
|
+
* component: TiledMapRenderer,
|
|
114
|
+
* width: parsedMap.width * parsedMap.tilewidth,
|
|
115
|
+
* height: parsedMap.height * parsedMap.tileheight,
|
|
116
|
+
* events: parsedMap.objectGroups?.find(g => g.name === 'events')?.objects
|
|
117
|
+
* }
|
|
118
|
+
* })
|
|
119
|
+
* ])
|
|
120
|
+
* }
|
|
121
|
+
*
|
|
122
|
+
* // Synchronous usage for static maps
|
|
123
|
+
* export function provideStaticMap() {
|
|
124
|
+
* return createModule("StaticMap", [
|
|
125
|
+
* provideLoadMap((mapId) => {
|
|
126
|
+
* const staticMaps = {
|
|
127
|
+
* 'town': { tiles: [...], npcs: [...] },
|
|
128
|
+
* 'dungeon': { tiles: [...], monsters: [...] }
|
|
129
|
+
* }
|
|
130
|
+
*
|
|
131
|
+
* return {
|
|
132
|
+
* data: staticMaps[mapId],
|
|
133
|
+
* component: StaticMapComponent,
|
|
134
|
+
* width: 800,
|
|
135
|
+
* height: 600
|
|
136
|
+
* }
|
|
137
|
+
* })
|
|
138
|
+
* ])
|
|
139
|
+
* }
|
|
140
|
+
* ```
|
|
141
|
+
*
|
|
142
|
+
* @since 4.0.0
|
|
143
|
+
* @see {@link LoadMapOptions} for callback function signature
|
|
144
|
+
* @see {@link MapData} for return data structure
|
|
145
|
+
*/
|
|
22
146
|
export function provideLoadMap(options: LoadMapOptions) {
|
|
23
147
|
return [
|
|
24
148
|
{
|
|
25
149
|
provide: UpdateMapToken,
|
|
26
|
-
useFactory: (context: Context) =>
|
|
150
|
+
useFactory: (context: Context) => {
|
|
151
|
+
if (context['side'] === 'client') {
|
|
152
|
+
console.warn('UpdateMapToken is not overridden')
|
|
153
|
+
}
|
|
154
|
+
return
|
|
155
|
+
},
|
|
27
156
|
},
|
|
28
157
|
{
|
|
29
158
|
provide: LoadMapToken,
|
package/src/services/mmorpg.ts
CHANGED
|
@@ -6,23 +6,33 @@ import { AbstractWebsocket, WebSocketToken } from "./AbstractSocket";
|
|
|
6
6
|
import { UpdateMapService, UpdateMapToken } from "@rpgjs/common";
|
|
7
7
|
|
|
8
8
|
interface MmorpgOptions {
|
|
9
|
-
host
|
|
9
|
+
host?: string;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
class BridgeWebsocket extends AbstractWebsocket {
|
|
13
|
-
|
|
13
|
+
private socket: any;
|
|
14
|
+
private privateId: string;
|
|
14
15
|
|
|
15
|
-
constructor(protected context: Context, private options: MmorpgOptions) {
|
|
16
|
+
constructor(protected context: Context, private options: MmorpgOptions = {}) {
|
|
16
17
|
super(context);
|
|
18
|
+
const id = localStorage.getItem("rpgjs-user-id") || crypto.randomUUID()
|
|
19
|
+
localStorage.setItem("rpgjs-user-id", id)
|
|
20
|
+
this.privateId = id
|
|
17
21
|
}
|
|
18
22
|
|
|
19
|
-
async connection() {
|
|
23
|
+
async connection(listeners?: (data: any) => void) {
|
|
20
24
|
// tmp
|
|
21
25
|
class Room {
|
|
22
26
|
|
|
23
27
|
}
|
|
24
28
|
const instance = new Room()
|
|
25
|
-
this.socket = await connectionRoom(
|
|
29
|
+
this.socket = await connectionRoom({
|
|
30
|
+
host: this.options.host || window.location.host,
|
|
31
|
+
room: "lobby-1",
|
|
32
|
+
id: this.privateId
|
|
33
|
+
}, instance)
|
|
34
|
+
|
|
35
|
+
listeners?.(this.socket)
|
|
26
36
|
}
|
|
27
37
|
|
|
28
38
|
on(key: string, callback: (data: any) => void) {
|
|
@@ -36,6 +46,18 @@ class BridgeWebsocket extends AbstractWebsocket {
|
|
|
36
46
|
emit(event: string, data: any) {
|
|
37
47
|
this.socket.emit(event, data);
|
|
38
48
|
}
|
|
49
|
+
|
|
50
|
+
updateProperties({ room }: { room: any }) {
|
|
51
|
+
this.socket.conn.updateProperties({
|
|
52
|
+
room: room,
|
|
53
|
+
id: this.privateId,
|
|
54
|
+
host: this.options.host
|
|
55
|
+
})
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async reconnect(listeners?: (data: any) => void) {
|
|
59
|
+
this.socket.conn.reconnect()
|
|
60
|
+
}
|
|
39
61
|
}
|
|
40
62
|
|
|
41
63
|
class UpdateMapStandaloneService extends UpdateMapService {
|