@rpgjs/client 5.0.0-beta.10 → 5.0.0-beta.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +21 -0
- package/dist/Game/AnimationManager.d.ts +1 -0
- package/dist/Game/AnimationManager.js +3 -0
- package/dist/Game/AnimationManager.js.map +1 -1
- package/dist/Game/ClientVisuals.d.ts +61 -0
- package/dist/Game/ClientVisuals.js +96 -0
- package/dist/Game/ClientVisuals.js.map +1 -0
- package/dist/Game/ClientVisuals.spec.d.ts +1 -0
- package/dist/Game/EventComponentResolver.d.ts +16 -0
- package/dist/Game/EventComponentResolver.js +52 -0
- package/dist/Game/EventComponentResolver.js.map +1 -0
- package/dist/Game/EventComponentResolver.spec.d.ts +1 -0
- package/dist/Game/Map.js +9 -0
- package/dist/Game/Map.js.map +1 -1
- package/dist/Game/Object.js +2 -2
- package/dist/Game/Object.js.map +1 -1
- package/dist/Game/Object.spec.d.ts +1 -0
- package/dist/Game/ProjectileManager.d.ts +98 -0
- package/dist/Game/ProjectileManager.js +196 -0
- package/dist/Game/ProjectileManager.js.map +1 -0
- package/dist/Game/ProjectileManager.spec.d.ts +1 -0
- package/dist/RpgClient.d.ts +117 -13
- package/dist/RpgClientEngine.d.ts +82 -4
- package/dist/RpgClientEngine.js +296 -51
- package/dist/RpgClientEngine.js.map +1 -1
- package/dist/components/animations/fx.ce.js +58 -0
- package/dist/components/animations/fx.ce.js.map +1 -0
- package/dist/components/animations/hit.ce.js.map +1 -1
- package/dist/components/animations/index.d.ts +1 -0
- package/dist/components/animations/index.js +3 -1
- package/dist/components/animations/index.js.map +1 -1
- package/dist/components/character.ce.js +140 -40
- package/dist/components/character.ce.js.map +1 -1
- package/dist/components/dynamics/bar.ce.js +4 -3
- package/dist/components/dynamics/bar.ce.js.map +1 -1
- package/dist/components/dynamics/image.ce.js +2 -1
- package/dist/components/dynamics/image.ce.js.map +1 -1
- package/dist/components/dynamics/shape.ce.js +3 -2
- package/dist/components/dynamics/shape.ce.js.map +1 -1
- package/dist/components/dynamics/text.ce.js +9 -8
- package/dist/components/dynamics/text.ce.js.map +1 -1
- package/dist/components/gui/dialogbox/index.ce.js +3 -2
- package/dist/components/gui/dialogbox/index.ce.js.map +1 -1
- package/dist/components/gui/gameover.ce.js +3 -2
- package/dist/components/gui/gameover.ce.js.map +1 -1
- package/dist/components/gui/hud/hud.ce.js.map +1 -1
- package/dist/components/gui/menu/equip-menu.ce.js +2 -1
- package/dist/components/gui/menu/equip-menu.ce.js.map +1 -1
- package/dist/components/gui/menu/exit-menu.ce.js +2 -1
- package/dist/components/gui/menu/exit-menu.ce.js.map +1 -1
- package/dist/components/gui/menu/items-menu.ce.js +3 -2
- package/dist/components/gui/menu/items-menu.ce.js.map +1 -1
- package/dist/components/gui/menu/main-menu.ce.js +3 -2
- package/dist/components/gui/menu/main-menu.ce.js.map +1 -1
- package/dist/components/gui/menu/options-menu.ce.js.map +1 -1
- package/dist/components/gui/menu/skills-menu.ce.js.map +1 -1
- package/dist/components/gui/mobile/mobile.ce.js.map +1 -1
- package/dist/components/gui/notification/notification.ce.js.map +1 -1
- package/dist/components/gui/save-load.ce.js +2 -1
- package/dist/components/gui/save-load.ce.js.map +1 -1
- package/dist/components/gui/shop/shop.ce.js +3 -2
- package/dist/components/gui/shop/shop.ce.js.map +1 -1
- package/dist/components/gui/title-screen.ce.js +3 -2
- package/dist/components/gui/title-screen.ce.js.map +1 -1
- package/dist/components/index.d.ts +2 -1
- package/dist/components/index.js +1 -0
- package/dist/components/player-components.ce.js +11 -10
- package/dist/components/player-components.ce.js.map +1 -1
- package/dist/components/prebuilt/hp-bar.ce.js +4 -3
- package/dist/components/prebuilt/hp-bar.ce.js.map +1 -1
- package/dist/components/prebuilt/light-halo.ce.js +2 -1
- package/dist/components/prebuilt/light-halo.ce.js.map +1 -1
- package/dist/components/scenes/canvas.ce.js +12 -4
- package/dist/components/scenes/canvas.ce.js.map +1 -1
- package/dist/components/scenes/draw-map.ce.js +6 -3
- package/dist/components/scenes/draw-map.ce.js.map +1 -1
- package/dist/components/scenes/event-layer.ce.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.js +10 -5
- package/dist/module.js +18 -0
- package/dist/module.js.map +1 -1
- package/dist/services/actionInput.d.ts +14 -0
- package/dist/services/actionInput.js +59 -0
- package/dist/services/actionInput.js.map +1 -0
- package/dist/services/actionInput.spec.d.ts +1 -0
- package/dist/services/mmorpg-connection.d.ts +5 -0
- package/dist/services/mmorpg-connection.js +50 -0
- package/dist/services/mmorpg-connection.js.map +1 -0
- package/dist/services/mmorpg-connection.spec.d.ts +1 -0
- package/dist/services/mmorpg.d.ts +10 -4
- package/dist/services/mmorpg.js +48 -30
- package/dist/services/mmorpg.js.map +1 -1
- package/dist/services/pointerContext.d.ts +11 -0
- package/dist/services/pointerContext.js +48 -0
- package/dist/services/pointerContext.js.map +1 -0
- package/dist/services/pointerContext.spec.d.ts +1 -0
- package/dist/services/standalone-message.d.ts +1 -0
- package/dist/services/standalone-message.js +9 -0
- package/dist/services/standalone-message.js.map +1 -0
- package/dist/services/standalone.d.ts +3 -1
- package/dist/services/standalone.js +34 -15
- package/dist/services/standalone.js.map +1 -1
- package/dist/services/standalone.spec.d.ts +1 -0
- package/dist/utils/mapId.d.ts +1 -0
- package/dist/utils/mapId.js +6 -0
- package/dist/utils/mapId.js.map +1 -0
- package/package.json +7 -7
- package/src/Game/AnimationManager.ts +4 -0
- package/src/Game/ClientVisuals.spec.ts +56 -0
- package/src/Game/ClientVisuals.ts +184 -0
- package/src/Game/EventComponentResolver.spec.ts +84 -0
- package/src/Game/EventComponentResolver.ts +74 -0
- package/src/Game/Map.ts +10 -0
- package/src/Game/Object.spec.ts +46 -0
- package/src/Game/Object.ts +2 -2
- package/src/Game/ProjectileManager.spec.ts +449 -0
- package/src/Game/ProjectileManager.ts +346 -0
- package/src/RpgClient.ts +130 -15
- package/src/RpgClientEngine.ts +405 -69
- package/src/components/animations/fx.ce +101 -0
- package/src/components/animations/index.ts +4 -2
- package/src/components/character.ce +185 -40
- package/src/components/dynamics/bar.ce +4 -3
- package/src/components/dynamics/image.ce +2 -1
- package/src/components/dynamics/shape.ce +3 -2
- package/src/components/dynamics/text.ce +9 -8
- package/src/components/gui/dialogbox/index.ce +3 -2
- package/src/components/gui/gameover.ce +2 -1
- package/src/components/gui/menu/equip-menu.ce +2 -1
- package/src/components/gui/menu/exit-menu.ce +2 -1
- package/src/components/gui/menu/items-menu.ce +3 -2
- package/src/components/gui/menu/main-menu.ce +2 -1
- package/src/components/gui/save-load.ce +2 -1
- package/src/components/gui/shop/shop.ce +3 -2
- package/src/components/gui/title-screen.ce +2 -1
- package/src/components/index.ts +2 -1
- package/src/components/player-components.ce +11 -10
- package/src/components/prebuilt/hp-bar.ce +4 -3
- package/src/components/prebuilt/light-halo.ce +2 -2
- package/src/components/scenes/canvas.ce +10 -2
- package/src/components/scenes/draw-map.ce +17 -3
- package/src/index.ts +4 -0
- package/src/module.ts +24 -0
- package/src/services/actionInput.spec.ts +155 -0
- package/src/services/actionInput.ts +120 -0
- package/src/services/mmorpg-connection.spec.ts +99 -0
- package/src/services/mmorpg-connection.ts +69 -0
- package/src/services/mmorpg.ts +60 -34
- package/src/services/pointerContext.spec.ts +36 -0
- package/src/services/pointerContext.ts +84 -0
- package/src/services/standalone-message.ts +7 -0
- package/src/services/standalone.spec.ts +34 -0
- package/src/services/standalone.ts +42 -12
- package/src/utils/mapId.ts +2 -0
|
@@ -90,6 +90,7 @@
|
|
|
90
90
|
import { signal, computed, createTabindexNavigator, effect } from "canvasengine";
|
|
91
91
|
import { inject } from "../../../core/inject";
|
|
92
92
|
import { RpgClientEngine } from "../../../RpgClientEngine";
|
|
93
|
+
import { getKeyboardControlBind } from "../../../services/actionInput";
|
|
93
94
|
|
|
94
95
|
const engine = inject(RpgClientEngine);
|
|
95
96
|
const keyboardControls = engine.globalConfig.keyboardControls;
|
|
@@ -380,7 +381,7 @@
|
|
|
380
381
|
}
|
|
381
382
|
},
|
|
382
383
|
action: {
|
|
383
|
-
bind: keyboardControls.action,
|
|
384
|
+
bind: getKeyboardControlBind(keyboardControls.action),
|
|
384
385
|
keyDown() {
|
|
385
386
|
if (!listEntries().length) return;
|
|
386
387
|
commitSelection(selectedItem());
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
import { signal } from "canvasengine";
|
|
17
17
|
import { inject } from "../../../core/inject";
|
|
18
18
|
import { RpgClientEngine } from "../../../RpgClientEngine";
|
|
19
|
+
import { getKeyboardControlBind } from "../../../services/actionInput";
|
|
19
20
|
|
|
20
21
|
const engine = inject(RpgClientEngine);
|
|
21
22
|
const keyboardControls = engine.globalConfig.keyboardControls;
|
|
@@ -23,7 +24,7 @@
|
|
|
23
24
|
|
|
24
25
|
const controls = signal({
|
|
25
26
|
action: {
|
|
26
|
-
bind: keyboardControls.action,
|
|
27
|
+
bind: getKeyboardControlBind(keyboardControls.action),
|
|
27
28
|
keyDown() {
|
|
28
29
|
if (onConfirm) onConfirm();
|
|
29
30
|
}
|
|
@@ -90,6 +90,7 @@
|
|
|
90
90
|
import { inject } from "../../../core/inject";
|
|
91
91
|
import { RpgClientEngine } from "../../../RpgClientEngine";
|
|
92
92
|
import { delay } from "@rpgjs/common";
|
|
93
|
+
import { getKeyboardControlBind } from "../../../services/actionInput";
|
|
93
94
|
|
|
94
95
|
const engine = inject(RpgClientEngine);
|
|
95
96
|
const keyboardControls = engine.globalConfig.keyboardControls;
|
|
@@ -226,7 +227,7 @@
|
|
|
226
227
|
}
|
|
227
228
|
},
|
|
228
229
|
action: {
|
|
229
|
-
bind: keyboardControls.action,
|
|
230
|
+
bind: getKeyboardControlBind(keyboardControls.action),
|
|
230
231
|
keyDown() {
|
|
231
232
|
if (!confirmOpen()) return;
|
|
232
233
|
confirmSelect(selectedConfirm())();
|
|
@@ -289,7 +290,7 @@
|
|
|
289
290
|
}
|
|
290
291
|
},
|
|
291
292
|
action: {
|
|
292
|
-
bind: keyboardControls.action,
|
|
293
|
+
bind: getKeyboardControlBind(keyboardControls.action),
|
|
293
294
|
keyDown() {
|
|
294
295
|
if (confirmOpen()) {
|
|
295
296
|
confirmSelect(selectedConfirm())();
|
|
@@ -101,6 +101,7 @@
|
|
|
101
101
|
import ExitMenu from "./exit-menu.ce";
|
|
102
102
|
import { getEntityProp } from "../../../utils/getEntityProp";
|
|
103
103
|
import { delay } from "@rpgjs/common";
|
|
104
|
+
import { getKeyboardControlBind } from "../../../services/actionInput";
|
|
104
105
|
|
|
105
106
|
const engine = inject(RpgClientEngine);
|
|
106
107
|
const currentPlayer = engine.scene.currentPlayer;
|
|
@@ -259,7 +260,7 @@
|
|
|
259
260
|
}
|
|
260
261
|
},
|
|
261
262
|
action: {
|
|
262
|
-
bind: keyboardControls.action,
|
|
263
|
+
bind: getKeyboardControlBind(keyboardControls.action),
|
|
263
264
|
keyDown() {
|
|
264
265
|
if (saveOverlay()) return;
|
|
265
266
|
if (view() !== "menu") return;
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
import { SaveClientService } from "../../services/save";
|
|
41
41
|
import { PrebuiltGui } from "@rpgjs/common";
|
|
42
42
|
import { RpgGui } from "../../Gui/Gui";
|
|
43
|
+
import { getKeyboardControlBind } from "../../services/actionInput";
|
|
43
44
|
|
|
44
45
|
const engine = inject(RpgClientEngine);
|
|
45
46
|
const saveClient = inject(SaveClientService);
|
|
@@ -185,7 +186,7 @@
|
|
|
185
186
|
}
|
|
186
187
|
},
|
|
187
188
|
action: {
|
|
188
|
-
bind: keyboardControls.action,
|
|
189
|
+
bind: getKeyboardControlBind(keyboardControls.action),
|
|
189
190
|
keyDown() {
|
|
190
191
|
triggerSelect(selectedSlot());
|
|
191
192
|
}
|
|
@@ -180,6 +180,7 @@
|
|
|
180
180
|
import { mount, signal, computed, createTabindexNavigator, effect } from "canvasengine";
|
|
181
181
|
import { inject } from "../../../core/inject";
|
|
182
182
|
import { RpgClientEngine } from "../../../RpgClientEngine";
|
|
183
|
+
import { getKeyboardControlBind } from "../../../services/actionInput";
|
|
183
184
|
|
|
184
185
|
const engine = inject(RpgClientEngine)
|
|
185
186
|
const currentPlayer = engine.scene.currentPlayer
|
|
@@ -387,7 +388,7 @@
|
|
|
387
388
|
}
|
|
388
389
|
},
|
|
389
390
|
action: {
|
|
390
|
-
bind: keyboardControls.action,
|
|
391
|
+
bind: getKeyboardControlBind(keyboardControls.action),
|
|
391
392
|
keyDown() {
|
|
392
393
|
const mode = selectedModeIndex() === 0 ? 'buy' : 'sell'
|
|
393
394
|
tradeMode.set(mode)
|
|
@@ -433,7 +434,7 @@
|
|
|
433
434
|
}
|
|
434
435
|
},
|
|
435
436
|
action: {
|
|
436
|
-
bind: keyboardControls.action,
|
|
437
|
+
bind: getKeyboardControlBind(keyboardControls.action),
|
|
437
438
|
keyDown() {
|
|
438
439
|
if (quantityDialogOpen()) {
|
|
439
440
|
const item = currentItem()
|
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
import { inject } from "../../core/inject";
|
|
32
32
|
import { RpgClientEngine } from "../../RpgClientEngine";
|
|
33
33
|
import { RpgGui } from "../../Gui/Gui";
|
|
34
|
+
import { getKeyboardControlBind } from "../../services/actionInput";
|
|
34
35
|
|
|
35
36
|
const engine = inject(RpgClientEngine);
|
|
36
37
|
const guiService = inject(RpgGui);
|
|
@@ -150,7 +151,7 @@
|
|
|
150
151
|
}
|
|
151
152
|
},
|
|
152
153
|
action: {
|
|
153
|
-
bind: keyboardControls.action,
|
|
154
|
+
bind: getKeyboardControlBind(keyboardControls.action),
|
|
154
155
|
keyDown() {
|
|
155
156
|
if (guiService.isDisplaying(PrebuiltGui.Save)) return;
|
|
156
157
|
triggerSelect(selectedEntry());
|
package/src/components/index.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import EventLayerComponent from "./scenes/event-layer.ce";
|
|
2
|
+
import SceneMap from "./scenes/draw-map.ce";
|
|
2
3
|
import CharacterComponent from "./character.ce";
|
|
3
4
|
|
|
4
5
|
// Prebuilt sprite components
|
|
5
6
|
export { HpBar } from "./prebuilt";
|
|
6
7
|
|
|
7
|
-
export { EventLayerComponent, CharacterComponent }
|
|
8
|
+
export { EventLayerComponent, SceneMap, CharacterComponent }
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
justifyContent="center"
|
|
30
30
|
alignItems="center"
|
|
31
31
|
>
|
|
32
|
-
<entry.component object ...entry.props />
|
|
32
|
+
<entry.component object={sprite} ...entry.props />
|
|
33
33
|
</Container>
|
|
34
34
|
}
|
|
35
35
|
</Container>
|
|
@@ -54,6 +54,7 @@ const { object, position, graphicBounds } = defineProps({
|
|
|
54
54
|
default: 'top'
|
|
55
55
|
}
|
|
56
56
|
});
|
|
57
|
+
const sprite = object();
|
|
57
58
|
|
|
58
59
|
const client = inject(RpgClientEngine);
|
|
59
60
|
const warnedComponents = new Set();
|
|
@@ -63,16 +64,16 @@ const readPosition = computed(() => position?.() ?? 'top');
|
|
|
63
64
|
const componentSource = computed(() => {
|
|
64
65
|
switch (readPosition()) {
|
|
65
66
|
case 'bottom':
|
|
66
|
-
return
|
|
67
|
+
return sprite.componentsBottom?.();
|
|
67
68
|
case 'center':
|
|
68
|
-
return
|
|
69
|
+
return sprite.componentsCenter?.();
|
|
69
70
|
case 'left':
|
|
70
|
-
return
|
|
71
|
+
return sprite.componentsLeft?.();
|
|
71
72
|
case 'right':
|
|
72
|
-
return
|
|
73
|
+
return sprite.componentsRight?.();
|
|
73
74
|
case 'top':
|
|
74
75
|
default:
|
|
75
|
-
return
|
|
76
|
+
return sprite.componentsTop?.();
|
|
76
77
|
}
|
|
77
78
|
});
|
|
78
79
|
|
|
@@ -98,16 +99,16 @@ const componentData = computed(() => {
|
|
|
98
99
|
|
|
99
100
|
const layout = computed(() => componentData()?.layout ?? {});
|
|
100
101
|
const rows = computed(() => componentData()?.components ?? []);
|
|
101
|
-
const hitbox =
|
|
102
|
+
const hitbox = sprite.hitbox;
|
|
102
103
|
|
|
103
104
|
const toNumber = (value, fallback = 0) => {
|
|
104
|
-
const resolved = resolveDynamicValue(value,
|
|
105
|
+
const resolved = resolveDynamicValue(value, sprite);
|
|
105
106
|
const num = typeof resolved === 'number' ? resolved : parseFloat(resolved);
|
|
106
107
|
return Number.isFinite(num) ? num : fallback;
|
|
107
108
|
};
|
|
108
109
|
|
|
109
110
|
const estimateTextWidth = (value, style = {}) => {
|
|
110
|
-
const text = String(resolveDynamicValue(value ?? '',
|
|
111
|
+
const text = String(resolveDynamicValue(value ?? '', sprite) ?? '');
|
|
111
112
|
const fontSize = toNumber(style.fontSize, 12);
|
|
112
113
|
return Math.max(1, Math.ceil(text.length * fontSize * 0.6));
|
|
113
114
|
};
|
|
@@ -206,7 +207,7 @@ const renderedRows = computed(() => {
|
|
|
206
207
|
|
|
207
208
|
entries.push({
|
|
208
209
|
component,
|
|
209
|
-
props: resolveDynamicProps(getComponentProps(definition),
|
|
210
|
+
props: resolveDynamicProps(getComponentProps(definition), sprite),
|
|
210
211
|
width: cell.width,
|
|
211
212
|
height: cell.height
|
|
212
213
|
});
|
|
@@ -46,6 +46,7 @@
|
|
|
46
46
|
import { computed, animatedSignal, effect } from "canvasengine";
|
|
47
47
|
|
|
48
48
|
const { object } = defineProps();
|
|
49
|
+
const sprite = object();
|
|
49
50
|
|
|
50
51
|
// ====================
|
|
51
52
|
// Configuration
|
|
@@ -93,14 +94,14 @@ const highlightHeight = Math.floor(fillHeight / 2);
|
|
|
93
94
|
// ====================
|
|
94
95
|
|
|
95
96
|
/** Gets hitbox dimensions for positioning */
|
|
96
|
-
const hitbox =
|
|
97
|
+
const hitbox = sprite.hitbox;
|
|
97
98
|
|
|
98
99
|
/**
|
|
99
100
|
* Gets the current HP value from the player object
|
|
100
101
|
* Uses hpSignal which is synchronized from the server
|
|
101
102
|
*/
|
|
102
103
|
const currentHp = computed(() => {
|
|
103
|
-
return
|
|
104
|
+
return sprite.hpSignal?.() ?? 0;
|
|
104
105
|
});
|
|
105
106
|
|
|
106
107
|
/**
|
|
@@ -108,7 +109,7 @@ const currentHp = computed(() => {
|
|
|
108
109
|
* Reads from _param.maxHp which contains calculated stats
|
|
109
110
|
*/
|
|
110
111
|
const maxHp = computed(() => {
|
|
111
|
-
const params =
|
|
112
|
+
const params = sprite._param?.() ?? {};
|
|
112
113
|
return params.maxHp ?? 100;
|
|
113
114
|
});
|
|
114
115
|
|
|
@@ -31,6 +31,7 @@ const {
|
|
|
31
31
|
opacitySpeed,
|
|
32
32
|
lightColor
|
|
33
33
|
} = defineProps();
|
|
34
|
+
const sprite = object();
|
|
34
35
|
|
|
35
36
|
// ====================
|
|
36
37
|
// Props with default values
|
|
@@ -92,7 +93,7 @@ const currentOpacity = computed(() => {
|
|
|
92
93
|
// Position calculations
|
|
93
94
|
// ====================
|
|
94
95
|
|
|
95
|
-
const hitbox =
|
|
96
|
+
const hitbox = sprite.hitbox;
|
|
96
97
|
|
|
97
98
|
const position = computed(() => ({
|
|
98
99
|
x: hitbox().w / 2,
|
|
@@ -145,4 +146,3 @@ tick(() => {
|
|
|
145
146
|
time.update(t => t + 1);
|
|
146
147
|
});
|
|
147
148
|
</script>
|
|
148
|
-
|
|
@@ -23,7 +23,15 @@
|
|
|
23
23
|
<SceneMap />
|
|
24
24
|
</Viewport>
|
|
25
25
|
@for (gui of guiList) {
|
|
26
|
-
<Container
|
|
26
|
+
<Container
|
|
27
|
+
positionType="absolute"
|
|
28
|
+
top={0}
|
|
29
|
+
left={0}
|
|
30
|
+
right={0}
|
|
31
|
+
bottom={0}
|
|
32
|
+
width={engine.width}
|
|
33
|
+
height={engine.height}
|
|
34
|
+
>
|
|
27
35
|
@if (gui.display) {
|
|
28
36
|
<gui.component data={gui.data} dependencies={gui.dependencies} onFinish={(data) => {
|
|
29
37
|
onGuiFinish(gui, data)
|
|
@@ -39,12 +47,12 @@
|
|
|
39
47
|
import { computed, effect } from "canvasengine";
|
|
40
48
|
import { inject } from "../../core/inject";
|
|
41
49
|
import { RpgClientEngine } from "../../RpgClientEngine";
|
|
42
|
-
import SceneMap from './draw-map.ce'
|
|
43
50
|
import { RpgGui } from "../../Gui/Gui";
|
|
44
51
|
import { delay } from "@rpgjs/common";
|
|
45
52
|
import { NightAmbiant, SpriteShadows } from '@canvasengine/presets'
|
|
46
53
|
|
|
47
54
|
const engine = inject(RpgClientEngine);
|
|
55
|
+
const SceneMap = engine.sceneMapComponent;
|
|
48
56
|
const guiService = inject(RpgGui);
|
|
49
57
|
const sceneData = engine.sceneMap.data
|
|
50
58
|
const lighting = engine.sceneMap.lighting
|
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
<Container sound={backgroundMusic} shake={shakeConfig} freeze={engine.gamePause}>
|
|
2
2
|
<Container sound={backgroundAmbientSound} />
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
<Container>
|
|
5
|
+
@if (map() && sceneComponent()) {
|
|
6
|
+
<sceneComponent() data={map().data} params={map().params} />
|
|
7
|
+
}
|
|
8
|
+
</Container>
|
|
9
|
+
|
|
10
|
+
@for (child of children) {
|
|
11
|
+
<child />
|
|
6
12
|
}
|
|
7
13
|
|
|
8
14
|
@for (componentAnimation of componentAnimations) {
|
|
@@ -13,19 +19,27 @@
|
|
|
13
19
|
</Container>
|
|
14
20
|
}
|
|
15
21
|
|
|
22
|
+
<Container sortableChildren={true}>
|
|
23
|
+
@for (projectile of projectiles() ; track projectile.props.id) {
|
|
24
|
+
<projectile.component ...projectile.props />
|
|
25
|
+
}
|
|
26
|
+
</Container>
|
|
27
|
+
|
|
16
28
|
@if (weatherProps()) {
|
|
17
29
|
<Weather ...weatherProps() />
|
|
18
30
|
}
|
|
19
31
|
</Container>
|
|
20
32
|
|
|
21
33
|
<script>
|
|
22
|
-
import { computed } from 'canvasengine'
|
|
34
|
+
import { computed, effect } from 'canvasengine'
|
|
23
35
|
import { inject } from "../../core/inject";
|
|
24
36
|
import { RpgClientEngine } from "../../RpgClientEngine";
|
|
25
37
|
import { Weather } from '@canvasengine/presets'
|
|
26
38
|
|
|
39
|
+
const { children } = defineProps()
|
|
27
40
|
const engine = inject(RpgClientEngine);
|
|
28
41
|
const componentAnimations = engine.componentAnimations
|
|
42
|
+
const projectiles = engine.projectiles.current
|
|
29
43
|
const map = engine.sceneMap?.data
|
|
30
44
|
const sceneComponent = computed(() => map()?.component)
|
|
31
45
|
const mapParams = map()?.params
|
package/src/index.ts
CHANGED
|
@@ -6,6 +6,8 @@ export * from "./services/save";
|
|
|
6
6
|
export * from "./core/setup";
|
|
7
7
|
export * from "./core/inject";
|
|
8
8
|
export * from "./services/loadMap";
|
|
9
|
+
export * from "./services/actionInput";
|
|
10
|
+
export * from "./services/pointerContext";
|
|
9
11
|
export * from "./module";
|
|
10
12
|
export * from "./Gui/Gui";
|
|
11
13
|
export * from "./components/gui";
|
|
@@ -24,5 +26,7 @@ export { Control } from "./services/keyboardControls";
|
|
|
24
26
|
export { RpgClientObject } from "./Game/Object";
|
|
25
27
|
export { RpgClientPlayer } from "./Game/Player";
|
|
26
28
|
export { RpgClientEvent } from "./Game/Event";
|
|
29
|
+
export * from "./Game/ProjectileManager";
|
|
30
|
+
export * from "./Game/ClientVisuals";
|
|
27
31
|
export { withMobile } from "./components/gui/mobile";
|
|
28
32
|
export * from "./services/AbstractSocket";
|
package/src/module.ts
CHANGED
|
@@ -155,6 +155,27 @@ export function provideClientModules(modules: RpgClientModule[]): FactoryProvide
|
|
|
155
155
|
},
|
|
156
156
|
};
|
|
157
157
|
}
|
|
158
|
+
if (module.clientVisuals) {
|
|
159
|
+
const clientVisuals = { ...module.clientVisuals };
|
|
160
|
+
module.clientVisuals = {
|
|
161
|
+
load: (engine: RpgClientEngine) => {
|
|
162
|
+
engine.registerClientVisuals(clientVisuals);
|
|
163
|
+
},
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
if (module.projectiles) {
|
|
167
|
+
const projectiles = { ...module.projectiles };
|
|
168
|
+
module.projectiles = {
|
|
169
|
+
...projectiles,
|
|
170
|
+
load: (engine: RpgClientEngine) => {
|
|
171
|
+
if (projectiles.components) {
|
|
172
|
+
Object.entries(projectiles.components).forEach(([type, component]) => {
|
|
173
|
+
engine.registerProjectileComponent(type, component);
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
},
|
|
177
|
+
};
|
|
178
|
+
}
|
|
158
179
|
if (module.transitions) {
|
|
159
180
|
const transitions = [...module.transitions];
|
|
160
181
|
module.transitions = {
|
|
@@ -200,6 +221,9 @@ export function provideClientModules(modules: RpgClientModule[]): FactoryProvide
|
|
|
200
221
|
engine.registerSpriteComponent(id, component);
|
|
201
222
|
});
|
|
202
223
|
}
|
|
224
|
+
if (sprite.eventComponent) {
|
|
225
|
+
engine.addEventComponentResolver(sprite.eventComponent);
|
|
226
|
+
}
|
|
203
227
|
},
|
|
204
228
|
};
|
|
205
229
|
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { describe, expect, test } from "vitest";
|
|
2
|
+
import {
|
|
3
|
+
getKeyboardControlBind,
|
|
4
|
+
keyboardEventMatchesBind,
|
|
5
|
+
normalizeActionInput,
|
|
6
|
+
resolveKeyboardActionInput,
|
|
7
|
+
resolveKeyboardDirectionInput,
|
|
8
|
+
} from "./actionInput";
|
|
9
|
+
|
|
10
|
+
const keyboardEvent = (values: Partial<KeyboardEvent>) =>
|
|
11
|
+
values as KeyboardEvent;
|
|
12
|
+
|
|
13
|
+
describe("normalizeActionInput", () => {
|
|
14
|
+
test("keeps simple actions compatible", () => {
|
|
15
|
+
expect(normalizeActionInput("action")).toEqual({
|
|
16
|
+
action: "action",
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
test("adds custom data to action payloads", () => {
|
|
21
|
+
expect(normalizeActionInput("projectile:shoot", {
|
|
22
|
+
target: { x: 320, y: 180 },
|
|
23
|
+
source: "map-click",
|
|
24
|
+
})).toEqual({
|
|
25
|
+
action: "projectile:shoot",
|
|
26
|
+
data: {
|
|
27
|
+
target: { x: 320, y: 180 },
|
|
28
|
+
source: "map-click",
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test("keeps object-form action payloads intact", () => {
|
|
34
|
+
const payload = {
|
|
35
|
+
action: "projectile:shoot",
|
|
36
|
+
data: { target: { x: 64, y: 96 } },
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
expect(normalizeActionInput(payload)).toBe(payload);
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
describe("keyboard action controls", () => {
|
|
44
|
+
test("keeps string controls compatible", () => {
|
|
45
|
+
expect(getKeyboardControlBind("space")).toBe("space");
|
|
46
|
+
expect(resolveKeyboardActionInput("space", {}, {})).toEqual({
|
|
47
|
+
action: "action",
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test("resolves object controls with static data", () => {
|
|
52
|
+
const control = {
|
|
53
|
+
bind: "space",
|
|
54
|
+
action: "projectile:shoot",
|
|
55
|
+
data: {
|
|
56
|
+
source: "keyboard",
|
|
57
|
+
target: { x: 10, y: 20 },
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
expect(getKeyboardControlBind(control)).toBe("space");
|
|
62
|
+
expect(resolveKeyboardActionInput(control, {}, {})).toEqual({
|
|
63
|
+
action: "projectile:shoot",
|
|
64
|
+
data: {
|
|
65
|
+
source: "keyboard",
|
|
66
|
+
target: { x: 10, y: 20 },
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test("resolves object controls with functional data", () => {
|
|
72
|
+
const client = {
|
|
73
|
+
pointer: {
|
|
74
|
+
world: () => ({ x: 64, y: 96 }),
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
const sprite = { id: "player-1" };
|
|
78
|
+
const control = {
|
|
79
|
+
bind: "space",
|
|
80
|
+
action: "projectile:shoot",
|
|
81
|
+
data: (resolvedClient: typeof client, resolvedSprite: typeof sprite) => ({
|
|
82
|
+
source: "keyboard",
|
|
83
|
+
target: resolvedClient.pointer.world(),
|
|
84
|
+
playerId: resolvedSprite.id,
|
|
85
|
+
}),
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
expect(resolveKeyboardActionInput(control, client, sprite)).toEqual({
|
|
89
|
+
action: "projectile:shoot",
|
|
90
|
+
data: {
|
|
91
|
+
source: "keyboard",
|
|
92
|
+
target: { x: 64, y: 96 },
|
|
93
|
+
playerId: "player-1",
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test("omits data when object controls do not provide it", () => {
|
|
99
|
+
expect(resolveKeyboardActionInput({
|
|
100
|
+
bind: "space",
|
|
101
|
+
action: "projectile:shoot",
|
|
102
|
+
}, {}, {})).toEqual({
|
|
103
|
+
action: "projectile:shoot",
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
test("matches keyboard events against string, numeric, and array binds", () => {
|
|
108
|
+
expect(
|
|
109
|
+
keyboardEventMatchesBind(
|
|
110
|
+
keyboardEvent({ key: " ", code: "Space", keyCode: 32 }),
|
|
111
|
+
"space"
|
|
112
|
+
)
|
|
113
|
+
).toBe(true);
|
|
114
|
+
expect(
|
|
115
|
+
keyboardEventMatchesBind(
|
|
116
|
+
keyboardEvent({ key: "ArrowUp", code: "ArrowUp", keyCode: 38 }),
|
|
117
|
+
"up"
|
|
118
|
+
)
|
|
119
|
+
).toBe(true);
|
|
120
|
+
expect(
|
|
121
|
+
keyboardEventMatchesBind(
|
|
122
|
+
keyboardEvent({ key: "x", code: "KeyX", keyCode: 88 }),
|
|
123
|
+
["space", "x"]
|
|
124
|
+
)
|
|
125
|
+
).toBe(true);
|
|
126
|
+
expect(
|
|
127
|
+
keyboardEventMatchesBind(
|
|
128
|
+
keyboardEvent({ key: "Escape", code: "Escape", keyCode: 27 }),
|
|
129
|
+
27
|
|
130
|
+
)
|
|
131
|
+
).toBe(true);
|
|
132
|
+
expect(
|
|
133
|
+
keyboardEventMatchesBind(
|
|
134
|
+
keyboardEvent({ key: "a", code: "KeyA", keyCode: 65 }),
|
|
135
|
+
"space"
|
|
136
|
+
)
|
|
137
|
+
).toBe(false);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
test("resolves directional keyboard controls from a native keyboard event", () => {
|
|
141
|
+
const controls = {
|
|
142
|
+
up: "up",
|
|
143
|
+
down: "down",
|
|
144
|
+
left: "left",
|
|
145
|
+
right: "right",
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
expect(
|
|
149
|
+
resolveKeyboardDirectionInput(
|
|
150
|
+
keyboardEvent({ key: "ArrowRight", code: "ArrowRight", keyCode: 39 }),
|
|
151
|
+
controls
|
|
152
|
+
)
|
|
153
|
+
).toBe("right");
|
|
154
|
+
});
|
|
155
|
+
});
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { Direction, type RpgActionInput, type RpgActionName } from "@rpgjs/common";
|
|
2
|
+
|
|
3
|
+
export type KeyboardActionDataResolver<TClient = any, TSprite = any> = (
|
|
4
|
+
client: TClient,
|
|
5
|
+
sprite: TSprite,
|
|
6
|
+
) => any;
|
|
7
|
+
|
|
8
|
+
export interface KeyboardActionConfig<TClient = any, TSprite = any> {
|
|
9
|
+
bind: any;
|
|
10
|
+
action?: RpgActionName;
|
|
11
|
+
data?: any | KeyboardActionDataResolver<TClient, TSprite>;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function normalizeActionInput(action: RpgActionName, data?: any): RpgActionInput;
|
|
15
|
+
export function normalizeActionInput(action: RpgActionInput): RpgActionInput;
|
|
16
|
+
export function normalizeActionInput(action: RpgActionName | RpgActionInput, data?: any): RpgActionInput {
|
|
17
|
+
if (typeof action === "object") {
|
|
18
|
+
return action;
|
|
19
|
+
}
|
|
20
|
+
return data === undefined
|
|
21
|
+
? { action }
|
|
22
|
+
: { action, data };
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function isKeyboardActionConfig(value: any): value is KeyboardActionConfig {
|
|
26
|
+
return value !== null
|
|
27
|
+
&& typeof value === "object"
|
|
28
|
+
&& Object.prototype.hasOwnProperty.call(value, "bind");
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function getKeyboardControlBind(control: any): any {
|
|
32
|
+
return isKeyboardActionConfig(control) ? control.bind : control;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const KEY_CODE_NAMES: Record<number, string> = {
|
|
36
|
+
32: "space",
|
|
37
|
+
27: "escape",
|
|
38
|
+
37: "left",
|
|
39
|
+
38: "up",
|
|
40
|
+
39: "right",
|
|
41
|
+
40: "down",
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const normalizeKeyboardName = (value: unknown): string | undefined => {
|
|
45
|
+
if (typeof value !== "string") return undefined;
|
|
46
|
+
const normalized = value.toLowerCase();
|
|
47
|
+
if (
|
|
48
|
+
normalized === " " ||
|
|
49
|
+
normalized === "spacebar" ||
|
|
50
|
+
normalized === "space"
|
|
51
|
+
) {
|
|
52
|
+
return "space";
|
|
53
|
+
}
|
|
54
|
+
if (normalized.startsWith("arrow")) {
|
|
55
|
+
return normalized.slice("arrow".length);
|
|
56
|
+
}
|
|
57
|
+
return normalized;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export function keyboardEventMatchesBind(
|
|
61
|
+
event: KeyboardEvent,
|
|
62
|
+
bind: any
|
|
63
|
+
): boolean {
|
|
64
|
+
if (Array.isArray(bind)) {
|
|
65
|
+
return bind.some(item => keyboardEventMatchesBind(event, item));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (typeof bind === "number") {
|
|
69
|
+
return event.keyCode === bind;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const expected = normalizeKeyboardName(bind);
|
|
73
|
+
if (!expected) return false;
|
|
74
|
+
|
|
75
|
+
return (
|
|
76
|
+
normalizeKeyboardName(event.key) === expected ||
|
|
77
|
+
normalizeKeyboardName(event.code) === expected ||
|
|
78
|
+
KEY_CODE_NAMES[event.keyCode] === expected
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function resolveKeyboardActionInput(
|
|
83
|
+
control: any,
|
|
84
|
+
client: any,
|
|
85
|
+
sprite: any,
|
|
86
|
+
defaultAction: RpgActionName = "action",
|
|
87
|
+
): RpgActionInput {
|
|
88
|
+
if (!isKeyboardActionConfig(control)) {
|
|
89
|
+
return { action: defaultAction };
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const action = control.action ?? defaultAction;
|
|
93
|
+
const data = typeof control.data === "function"
|
|
94
|
+
? control.data(client, sprite)
|
|
95
|
+
: control.data;
|
|
96
|
+
|
|
97
|
+
return data === undefined
|
|
98
|
+
? { action }
|
|
99
|
+
: { action, data };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function resolveKeyboardDirectionInput(
|
|
103
|
+
event: KeyboardEvent,
|
|
104
|
+
keyboardControls: any
|
|
105
|
+
): Direction | undefined {
|
|
106
|
+
const directions: Array<[any, Direction]> = [
|
|
107
|
+
[keyboardControls?.up, Direction.Up],
|
|
108
|
+
[keyboardControls?.down, Direction.Down],
|
|
109
|
+
[keyboardControls?.left, Direction.Left],
|
|
110
|
+
[keyboardControls?.right, Direction.Right],
|
|
111
|
+
];
|
|
112
|
+
|
|
113
|
+
for (const [control, direction] of directions) {
|
|
114
|
+
if (keyboardEventMatchesBind(event, getKeyboardControlBind(control))) {
|
|
115
|
+
return direction;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return undefined;
|
|
120
|
+
}
|