@rpgjs/client 5.0.0-beta.1 → 5.0.0-beta.11
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 +49 -0
- package/LICENSE +19 -0
- package/dist/Game/AnimationManager.d.ts +1 -1
- package/dist/Game/AnimationManager.js +18 -9
- package/dist/Game/AnimationManager.js.map +1 -1
- package/dist/Game/AnimationManager.spec.d.ts +1 -0
- package/dist/Game/Event.js.map +1 -1
- package/dist/Game/Map.d.ts +9 -1
- package/dist/Game/Map.js +63 -5
- package/dist/Game/Map.js.map +1 -1
- package/dist/Game/Object.d.ts +47 -15
- package/dist/Game/Object.js +82 -38
- package/dist/Game/Object.js.map +1 -1
- package/dist/Game/Player.js.map +1 -1
- package/dist/Game/ProjectileManager.d.ts +89 -0
- package/dist/Game/ProjectileManager.js +179 -0
- package/dist/Game/ProjectileManager.js.map +1 -0
- package/dist/Game/ProjectileManager.spec.d.ts +1 -0
- package/dist/Gui/Gui.d.ts +17 -4
- package/dist/Gui/Gui.js +78 -48
- package/dist/Gui/Gui.js.map +1 -1
- package/dist/Gui/Gui.spec.d.ts +1 -0
- package/dist/Gui/NotificationManager.js.map +1 -1
- package/dist/Resource.js +1 -1
- package/dist/Resource.js.map +1 -1
- package/dist/RpgClient.d.ts +110 -15
- package/dist/RpgClientEngine.d.ts +86 -10
- package/dist/RpgClientEngine.js +306 -49
- package/dist/RpgClientEngine.js.map +1 -1
- package/dist/Sound.js.map +1 -1
- package/dist/_virtual/{_@oxc-project_runtime@0.122.0 → _@oxc-project_runtime@0.130.0}/helpers/decorate.js +1 -1
- package/dist/_virtual/{_@oxc-project_runtime@0.122.0 → _@oxc-project_runtime@0.130.0}/helpers/decorateMetadata.js +1 -1
- package/dist/components/animations/animation.ce.js +4 -5
- package/dist/components/animations/animation.ce.js.map +1 -1
- package/dist/components/animations/hit.ce.js +19 -25
- package/dist/components/animations/hit.ce.js.map +1 -1
- package/dist/components/animations/index.js +4 -4
- package/dist/components/animations/index.js.map +1 -1
- package/dist/components/character.ce.js +422 -240
- package/dist/components/character.ce.js.map +1 -1
- package/dist/components/dynamics/bar.ce.js +97 -0
- package/dist/components/dynamics/bar.ce.js.map +1 -0
- package/dist/components/dynamics/image.ce.js +24 -0
- package/dist/components/dynamics/image.ce.js.map +1 -0
- package/dist/components/dynamics/parse-value.d.ts +3 -0
- package/dist/components/dynamics/parse-value.js +54 -35
- package/dist/components/dynamics/parse-value.js.map +1 -1
- package/dist/components/dynamics/parse-value.spec.d.ts +1 -0
- package/dist/components/dynamics/shape-utils.d.ts +16 -0
- package/dist/components/dynamics/shape-utils.js +73 -0
- package/dist/components/dynamics/shape-utils.js.map +1 -0
- package/dist/components/dynamics/shape-utils.spec.d.ts +1 -0
- package/dist/components/dynamics/shape.ce.js +84 -0
- package/dist/components/dynamics/shape.ce.js.map +1 -0
- package/dist/components/dynamics/text.ce.js +34 -56
- package/dist/components/dynamics/text.ce.js.map +1 -1
- package/dist/components/gui/box.ce.js +6 -8
- package/dist/components/gui/box.ce.js.map +1 -1
- package/dist/components/gui/dialogbox/index.ce.js +56 -62
- package/dist/components/gui/dialogbox/index.ce.js.map +1 -1
- package/dist/components/gui/gameover.ce.js +42 -65
- package/dist/components/gui/gameover.ce.js.map +1 -1
- package/dist/components/gui/hud/hud.ce.js +21 -30
- package/dist/components/gui/hud/hud.ce.js.map +1 -1
- package/dist/components/gui/menu/equip-menu.ce.js +112 -165
- package/dist/components/gui/menu/equip-menu.ce.js.map +1 -1
- package/dist/components/gui/menu/exit-menu.ce.js +8 -6
- package/dist/components/gui/menu/exit-menu.ce.js.map +1 -1
- package/dist/components/gui/menu/items-menu.ce.js +52 -69
- package/dist/components/gui/menu/items-menu.ce.js.map +1 -1
- package/dist/components/gui/menu/main-menu.ce.js +75 -92
- package/dist/components/gui/menu/main-menu.ce.js.map +1 -1
- package/dist/components/gui/menu/options-menu.ce.js +5 -4
- package/dist/components/gui/menu/options-menu.ce.js.map +1 -1
- package/dist/components/gui/menu/skills-menu.ce.js +12 -17
- package/dist/components/gui/menu/skills-menu.ce.js.map +1 -1
- package/dist/components/gui/mobile/index.js +2 -2
- package/dist/components/gui/mobile/index.js.map +1 -1
- package/dist/components/gui/mobile/mobile.ce.js +5 -4
- package/dist/components/gui/mobile/mobile.ce.js.map +1 -1
- package/dist/components/gui/notification/notification.ce.js +22 -24
- package/dist/components/gui/notification/notification.ce.js.map +1 -1
- package/dist/components/gui/save-load.ce.js +72 -249
- package/dist/components/gui/save-load.ce.js.map +1 -1
- package/dist/components/gui/shop/shop.ce.js +90 -127
- package/dist/components/gui/shop/shop.ce.js.map +1 -1
- package/dist/components/gui/title-screen.ce.js +45 -70
- 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-utils.d.ts +67 -0
- package/dist/components/player-components-utils.js +162 -0
- package/dist/components/player-components-utils.js.map +1 -0
- package/dist/components/player-components-utils.spec.d.ts +1 -0
- package/dist/components/player-components.ce.js +189 -0
- package/dist/components/player-components.ce.js.map +1 -0
- package/dist/components/prebuilt/hp-bar.ce.js +42 -44
- package/dist/components/prebuilt/hp-bar.ce.js.map +1 -1
- package/dist/components/prebuilt/light-halo.ce.js +36 -59
- package/dist/components/prebuilt/light-halo.ce.js.map +1 -1
- package/dist/components/scenes/canvas.ce.js +165 -21
- package/dist/components/scenes/canvas.ce.js.map +1 -1
- package/dist/components/scenes/draw-map.ce.js +25 -32
- package/dist/components/scenes/draw-map.ce.js.map +1 -1
- package/dist/components/scenes/event-layer.ce.js +9 -8
- package/dist/components/scenes/event-layer.ce.js.map +1 -1
- package/dist/core/inject.js +1 -1
- package/dist/core/inject.js.map +1 -1
- package/dist/core/setup.js +1 -1
- package/dist/core/setup.js.map +1 -1
- package/dist/decorators/spritesheet.d.ts +1 -0
- package/dist/decorators/spritesheet.js +11 -0
- package/dist/decorators/spritesheet.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +26 -21
- package/dist/module.js +15 -1
- package/dist/module.js.map +1 -1
- package/dist/node_modules/.pnpm/{@signe_di@2.9.0 → @signe_di@3.0.1}/node_modules/@signe/di/dist/index.js +7 -117
- package/dist/node_modules/.pnpm/@signe_di@3.0.1/node_modules/@signe/di/dist/index.js.map +1 -0
- package/dist/node_modules/.pnpm/@signe_reactive@3.0.1/node_modules/@signe/reactive/dist/index.js +239 -0
- package/dist/node_modules/.pnpm/@signe_reactive@3.0.1/node_modules/@signe/reactive/dist/index.js.map +1 -0
- package/dist/node_modules/.pnpm/@signe_room@3.0.1/node_modules/@signe/room/dist/chunk-EUXUH3YW.js +13 -0
- package/dist/node_modules/.pnpm/@signe_room@3.0.1/node_modules/@signe/room/dist/chunk-EUXUH3YW.js.map +1 -0
- package/dist/node_modules/.pnpm/@signe_room@3.0.1/node_modules/@signe/room/dist/index.js +696 -0
- package/dist/node_modules/.pnpm/@signe_room@3.0.1/node_modules/@signe/room/dist/index.js.map +1 -0
- package/dist/node_modules/.pnpm/@signe_sync@3.0.1/node_modules/@signe/sync/dist/client/index.js +44 -0
- package/dist/node_modules/.pnpm/@signe_sync@3.0.1/node_modules/@signe/sync/dist/client/index.js.map +1 -0
- package/dist/node_modules/.pnpm/{@signe_sync@2.9.0 → @signe_sync@3.0.1}/node_modules/@signe/sync/dist/index.js +57 -141
- package/dist/node_modules/.pnpm/@signe_sync@3.0.1/node_modules/@signe/sync/dist/index.js.map +1 -0
- package/dist/node_modules/.pnpm/partysocket@1.1.3/node_modules/partysocket/dist/chunk-HAC622V3.js.map +1 -1
- package/dist/node_modules/.pnpm/partysocket@1.1.3/node_modules/partysocket/dist/chunk-S74YV6PU.js.map +1 -1
- package/dist/node_modules/.pnpm/zod@3.24.2/node_modules/zod/lib/index.js +27 -27
- package/dist/node_modules/.pnpm/zod@3.24.2/node_modules/zod/lib/index.js.map +1 -1
- package/dist/presets/animation.js.map +1 -1
- package/dist/presets/faceset.js.map +1 -1
- package/dist/presets/icon.js.map +1 -1
- package/dist/presets/index.js.map +1 -1
- package/dist/presets/lpc.js.map +1 -1
- package/dist/presets/rmspritesheet.js.map +1 -1
- package/dist/services/AbstractSocket.js.map +1 -1
- package/dist/services/actionInput.d.ts +12 -0
- package/dist/services/actionInput.js +27 -0
- package/dist/services/actionInput.js.map +1 -0
- package/dist/services/actionInput.spec.d.ts +1 -0
- package/dist/services/keyboardControls.js.map +1 -1
- package/dist/services/loadMap.d.ts +6 -0
- package/dist/services/loadMap.js +1 -1
- package/dist/services/loadMap.js.map +1 -1
- 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 +56 -33
- 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/save.js.map +1 -1
- package/dist/services/save.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.js +4 -3
- package/dist/services/standalone.js.map +1 -1
- package/dist/services/standalone.spec.d.ts +1 -0
- package/dist/utils/getEntityProp.js +4 -3
- package/dist/utils/getEntityProp.js.map +1 -1
- package/dist/utils/getEntityProp.spec.d.ts +1 -0
- package/dist/utils/readPropValue.d.ts +2 -0
- package/dist/utils/readPropValue.js +13 -0
- package/dist/utils/readPropValue.js.map +1 -0
- package/package.json +13 -14
- package/src/Game/AnimationManager.spec.ts +30 -0
- package/src/Game/AnimationManager.ts +22 -10
- package/src/Game/Map.ts +91 -2
- package/src/Game/Object.ts +148 -69
- package/src/Game/ProjectileManager.spec.ts +338 -0
- package/src/Game/ProjectileManager.ts +324 -0
- package/src/Gui/Gui.spec.ts +273 -0
- package/src/Gui/Gui.ts +105 -50
- package/src/Resource.ts +1 -2
- package/src/RpgClient.ts +125 -17
- package/src/RpgClientEngine.ts +457 -87
- package/src/components/character.ce +441 -32
- package/src/components/dynamics/bar.ce +88 -0
- package/src/components/dynamics/image.ce +21 -0
- package/src/components/dynamics/parse-value.spec.ts +83 -0
- package/src/components/dynamics/parse-value.ts +111 -37
- package/src/components/dynamics/shape-utils.spec.ts +46 -0
- package/src/components/dynamics/shape-utils.ts +61 -0
- package/src/components/dynamics/shape.ce +90 -0
- package/src/components/dynamics/text.ce +35 -149
- package/src/components/gui/dialogbox/index.ce +18 -8
- 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-utils.spec.ts +109 -0
- package/src/components/player-components-utils.ts +205 -0
- package/src/components/player-components.ce +222 -0
- package/src/components/prebuilt/hp-bar.ce +4 -3
- package/src/components/prebuilt/light-halo.ce +2 -2
- package/src/components/scenes/canvas.ce +175 -8
- package/src/components/scenes/draw-map.ce +18 -17
- package/src/components/scenes/event-layer.ce +1 -2
- package/src/core/setup.ts +2 -2
- package/src/decorators/spritesheet.ts +8 -0
- package/src/index.ts +4 -0
- package/src/module.ts +18 -1
- package/src/services/actionInput.spec.ts +101 -0
- package/src/services/actionInput.ts +53 -0
- package/src/services/loadMap.ts +2 -0
- package/src/services/mmorpg-connection.spec.ts +99 -0
- package/src/services/mmorpg-connection.ts +69 -0
- package/src/services/mmorpg.ts +68 -36
- package/src/services/pointerContext.spec.ts +36 -0
- package/src/services/pointerContext.ts +84 -0
- package/src/services/save.spec.ts +127 -0
- package/src/services/standalone-message.ts +7 -0
- package/src/services/standalone.spec.ts +34 -0
- package/src/services/standalone.ts +3 -2
- package/src/utils/getEntityProp.spec.ts +96 -0
- package/src/utils/getEntityProp.ts +4 -3
- package/src/utils/readPropValue.ts +16 -0
- package/dist/node_modules/.pnpm/@signe_di@2.9.0/node_modules/@signe/di/dist/index.js.map +0 -1
- package/dist/node_modules/.pnpm/@signe_reactive@2.8.3/node_modules/@signe/reactive/dist/index.js +0 -457
- package/dist/node_modules/.pnpm/@signe_reactive@2.8.3/node_modules/@signe/reactive/dist/index.js.map +0 -1
- package/dist/node_modules/.pnpm/@signe_reactive@2.9.0/node_modules/@signe/reactive/dist/index.js +0 -463
- package/dist/node_modules/.pnpm/@signe_reactive@2.9.0/node_modules/@signe/reactive/dist/index.js.map +0 -1
- package/dist/node_modules/.pnpm/@signe_room@2.9.0/node_modules/@signe/room/dist/index.js +0 -2191
- package/dist/node_modules/.pnpm/@signe_room@2.9.0/node_modules/@signe/room/dist/index.js.map +0 -1
- package/dist/node_modules/.pnpm/@signe_sync@2.9.0/node_modules/@signe/sync/dist/chunk-7QVYU63E.js +0 -10
- package/dist/node_modules/.pnpm/@signe_sync@2.9.0/node_modules/@signe/sync/dist/chunk-7QVYU63E.js.map +0 -1
- package/dist/node_modules/.pnpm/@signe_sync@2.9.0/node_modules/@signe/sync/dist/client/index.js +0 -91
- package/dist/node_modules/.pnpm/@signe_sync@2.9.0/node_modules/@signe/sync/dist/client/index.js.map +0 -1
- package/dist/node_modules/.pnpm/@signe_sync@2.9.0/node_modules/@signe/sync/dist/index.js.map +0 -1
- package/dist/node_modules/.pnpm/dset@3.1.4/node_modules/dset/dist/index.js +0 -14
- package/dist/node_modules/.pnpm/dset@3.1.4/node_modules/dset/dist/index.js.map +0 -1
|
@@ -1,30 +1,38 @@
|
|
|
1
1
|
<Container x={smoothX} y={smoothY} zIndex={z} viewportFollow={shouldFollowCamera} controls onBeforeDestroy visible>
|
|
2
2
|
@for (compConfig of normalizedComponentsBehind) {
|
|
3
3
|
<Container>
|
|
4
|
-
<compConfig.component object ...compConfig.props />
|
|
4
|
+
<compConfig.component object={sprite} ...compConfig.props />
|
|
5
5
|
</Container>
|
|
6
6
|
}
|
|
7
|
+
<PlayerComponents object={sprite} position="bottom" graphicBounds />
|
|
8
|
+
<PlayerComponents object={sprite} position="left" graphicBounds />
|
|
7
9
|
<Particle emit={emitParticleTrigger} settings={particleSettings} zIndex={1000} name={particleName} />
|
|
8
10
|
<Container>
|
|
9
11
|
@for (graphicObj of graphicsSignals) {
|
|
10
|
-
<
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
12
|
+
<Container scale={graphicScale(graphicObj)}>
|
|
13
|
+
<Sprite
|
|
14
|
+
sheet={sheet(graphicObj)}
|
|
15
|
+
direction
|
|
16
|
+
tint
|
|
17
|
+
hitbox
|
|
18
|
+
shadowCaster={shadowCaster(graphicObj)}
|
|
19
|
+
flash={flashConfig}
|
|
20
|
+
/>
|
|
21
|
+
</Container>
|
|
17
22
|
}
|
|
18
23
|
</Container>
|
|
24
|
+
<PlayerComponents object={sprite} position="center" graphicBounds />
|
|
25
|
+
<PlayerComponents object={sprite} position="right" graphicBounds />
|
|
26
|
+
<PlayerComponents object={sprite} position="top" graphicBounds />
|
|
19
27
|
@for (compConfig of normalizedComponentsInFront) {
|
|
20
28
|
<Container dependencies={compConfig.dependencies}>
|
|
21
|
-
<compConfig.component object ...compConfig.props />
|
|
29
|
+
<compConfig.component object={sprite} ...compConfig.props />
|
|
22
30
|
</Container>
|
|
23
31
|
}
|
|
24
32
|
@for (attachedGui of attachedGuis) {
|
|
25
33
|
@if (shouldDisplayAttachedGui) {
|
|
26
34
|
<Container>
|
|
27
|
-
<attachedGui.component ...attachedGui.data() dependencies={attachedGui.dependencies} object={
|
|
35
|
+
<attachedGui.component ...attachedGui.data() dependencies={attachedGui.dependencies} object={sprite} onFinish={(data) => {
|
|
28
36
|
onAttachedGuiFinish(attachedGui, data)
|
|
29
37
|
}} onInteraction={(name, data) => {
|
|
30
38
|
onAttachedGuiInteraction(attachedGui, name, data)
|
|
@@ -36,28 +44,44 @@
|
|
|
36
44
|
|
|
37
45
|
<script>
|
|
38
46
|
import { signal, effect, mount, computed, tick, animatedSignal, on } from "canvasengine";
|
|
47
|
+
import { Assets } from "pixi.js";
|
|
39
48
|
|
|
40
49
|
import { lastValueFrom, combineLatest, pairwise, filter, map, startWith } from "rxjs";
|
|
41
50
|
import { Particle } from "@canvasengine/presets";
|
|
42
51
|
import { GameEngineToken, ModulesToken } from "@rpgjs/common";
|
|
43
52
|
import { RpgClientEngine } from "../RpgClientEngine";
|
|
44
53
|
import { inject } from "../core/inject";
|
|
45
|
-
import { Direction } from "@rpgjs/common";
|
|
54
|
+
import { Direction, Animation } from "@rpgjs/common";
|
|
46
55
|
import Hit from "./effects/hit.ce";
|
|
47
56
|
import PlayerComponents from "./player-components.ce";
|
|
48
57
|
import { RpgGui } from "../Gui/Gui";
|
|
58
|
+
import { getCanMoveValue } from "../utils/readPropValue";
|
|
59
|
+
import { getKeyboardControlBind, resolveKeyboardActionInput } from "../services/actionInput";
|
|
49
60
|
|
|
50
61
|
const { object, id } = defineProps();
|
|
62
|
+
const sprite = object();
|
|
51
63
|
|
|
52
64
|
const client = inject(RpgClientEngine);
|
|
53
65
|
const hooks = inject(ModulesToken);
|
|
54
66
|
const guiService = inject(RpgGui);
|
|
55
67
|
|
|
56
68
|
const spritesheets = client.spritesheets;
|
|
57
|
-
const playerId = client.playerId;
|
|
58
69
|
const componentsBehind = client.spriteComponentsBehind;
|
|
59
70
|
const componentsInFront = client.spriteComponentsInFront;
|
|
60
|
-
const
|
|
71
|
+
const readProp = (value) => typeof value === 'function' ? value() : value;
|
|
72
|
+
const isCurrentPlayer = () => {
|
|
73
|
+
const playerId = client.playerIdSignal();
|
|
74
|
+
const currentPlayer = playerId ? client.sceneMap?.players?.()?.[playerId] : undefined;
|
|
75
|
+
return readProp(id) === playerId
|
|
76
|
+
|| readProp(sprite?.id) === playerId
|
|
77
|
+
|| sprite === currentPlayer
|
|
78
|
+
|| sprite === client.sceneMap?.getCurrentPlayer?.();
|
|
79
|
+
};
|
|
80
|
+
const isMe = computed(isCurrentPlayer);
|
|
81
|
+
const shadowsEnabled = computed(() => {
|
|
82
|
+
const lighting = client.sceneMap?.lighting?.();
|
|
83
|
+
return Boolean(lighting?.shadows?.enabled || (lighting?.spots?.length ?? 0) > 0);
|
|
84
|
+
});
|
|
61
85
|
|
|
62
86
|
/**
|
|
63
87
|
* Normalize a single sprite component configuration
|
|
@@ -128,8 +152,8 @@
|
|
|
128
152
|
// The computed will be created in the template when needed
|
|
129
153
|
return {
|
|
130
154
|
component: componentRef,
|
|
131
|
-
props: typeof propsValue === 'function' ? propsValue(
|
|
132
|
-
dependencies: dependenciesFn ? dependenciesFn(
|
|
155
|
+
props: typeof propsValue === 'function' ? propsValue(sprite) : propsValue || {},
|
|
156
|
+
dependencies: dependenciesFn ? dependenciesFn(sprite) : []
|
|
133
157
|
};
|
|
134
158
|
};
|
|
135
159
|
|
|
@@ -237,7 +261,7 @@
|
|
|
237
261
|
isConnected,
|
|
238
262
|
graphicsSignals,
|
|
239
263
|
flashTrigger
|
|
240
|
-
} =
|
|
264
|
+
} = sprite;
|
|
241
265
|
|
|
242
266
|
/**
|
|
243
267
|
* Flash configuration signals for dynamic options
|
|
@@ -282,17 +306,17 @@
|
|
|
282
306
|
|
|
283
307
|
const particleSettings = client.particleSettings;
|
|
284
308
|
|
|
285
|
-
const canControls = () => isMe() &&
|
|
309
|
+
const canControls = () => isMe() && getCanMoveValue(sprite)
|
|
286
310
|
const keyboardControls = client.globalConfig.keyboardControls;
|
|
287
311
|
|
|
288
312
|
const visible = computed(() => {
|
|
289
|
-
if (
|
|
313
|
+
if (sprite.isEvent()) {
|
|
290
314
|
return true
|
|
291
315
|
}
|
|
292
316
|
return isConnected()
|
|
293
317
|
});
|
|
294
318
|
|
|
295
|
-
const controls =
|
|
319
|
+
const controls = {
|
|
296
320
|
down: {
|
|
297
321
|
repeat: true,
|
|
298
322
|
bind: keyboardControls.down,
|
|
@@ -322,10 +346,10 @@
|
|
|
322
346
|
},
|
|
323
347
|
},
|
|
324
348
|
action: {
|
|
325
|
-
bind: keyboardControls.action,
|
|
349
|
+
bind: getKeyboardControlBind(keyboardControls.action),
|
|
326
350
|
keyDown() {
|
|
327
351
|
if (canControls()) {
|
|
328
|
-
client.processAction(
|
|
352
|
+
client.processAction(resolveKeyboardActionInput(keyboardControls.action, client, sprite))
|
|
329
353
|
}
|
|
330
354
|
},
|
|
331
355
|
},
|
|
@@ -340,7 +364,7 @@
|
|
|
340
364
|
gamepad: {
|
|
341
365
|
enabled: true
|
|
342
366
|
}
|
|
343
|
-
}
|
|
367
|
+
};
|
|
344
368
|
|
|
345
369
|
const smoothX = animatedSignal(x(), {
|
|
346
370
|
duration: isMe() ? 0 : 0
|
|
@@ -351,7 +375,7 @@
|
|
|
351
375
|
});
|
|
352
376
|
|
|
353
377
|
const z = computed(() => {
|
|
354
|
-
return
|
|
378
|
+
return sprite.y() + sprite.z()
|
|
355
379
|
});
|
|
356
380
|
|
|
357
381
|
const realAnimationName = signal(animationName());
|
|
@@ -377,6 +401,315 @@
|
|
|
377
401
|
};
|
|
378
402
|
}
|
|
379
403
|
|
|
404
|
+
const graphicScale = (graphicObject) => {
|
|
405
|
+
const scale = graphicObject?.scale;
|
|
406
|
+
if (Array.isArray(scale)) return scale;
|
|
407
|
+
if (typeof scale === 'number') return [scale, scale];
|
|
408
|
+
if (scale && typeof scale === 'object') {
|
|
409
|
+
const x = typeof scale.x === 'number' ? scale.x : 1;
|
|
410
|
+
const y = typeof scale.y === 'number' ? scale.y : x;
|
|
411
|
+
return [x, y];
|
|
412
|
+
}
|
|
413
|
+
return undefined;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
const shadowCaster = (graphicObject) => {
|
|
417
|
+
const box = hitbox();
|
|
418
|
+
const bounds = graphicBounds();
|
|
419
|
+
const scale = graphicScale(graphicObject);
|
|
420
|
+
const scaleY = Array.isArray(scale) && typeof scale[1] === 'number' ? Math.abs(scale[1]) : 1;
|
|
421
|
+
const height = Math.max(bounds?.height ?? box?.h ?? 32, box?.h ?? 32) * scaleY;
|
|
422
|
+
|
|
423
|
+
return {
|
|
424
|
+
enabled: shadowsEnabled,
|
|
425
|
+
height,
|
|
426
|
+
footAnchor: { x: 0.5, y: 1 },
|
|
427
|
+
footOffset: { x: 0, y: 2 },
|
|
428
|
+
alpha: 0.5,
|
|
429
|
+
blur: 3.5,
|
|
430
|
+
gradientPower: 2,
|
|
431
|
+
hardness: 0.42,
|
|
432
|
+
minLength: Math.max(6, (box?.h ?? 32) * 0.25),
|
|
433
|
+
maxLength: Math.max(90, height * 1.8),
|
|
434
|
+
contactAlpha: 0.3,
|
|
435
|
+
contactScale: 0.3,
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
const imageDimensions = signal({});
|
|
440
|
+
const loadingImageDimensions = new Set();
|
|
441
|
+
|
|
442
|
+
const toPositiveNumber = (value) => {
|
|
443
|
+
const number = typeof value === 'number' ? value : parseFloat(value);
|
|
444
|
+
return Number.isFinite(number) && number > 0 ? number : undefined;
|
|
445
|
+
};
|
|
446
|
+
|
|
447
|
+
const toFiniteNumber = (value, fallback = 0) => {
|
|
448
|
+
const number = typeof value === 'number' ? value : parseFloat(value);
|
|
449
|
+
return Number.isFinite(number) ? number : fallback;
|
|
450
|
+
};
|
|
451
|
+
|
|
452
|
+
const clampRatio = (value) => Math.min(1, Math.max(0, value));
|
|
453
|
+
|
|
454
|
+
const normalizePair = (value, fallback = [1, 1]) => {
|
|
455
|
+
if (Array.isArray(value)) {
|
|
456
|
+
const x = toFiniteNumber(value[0], fallback[0]);
|
|
457
|
+
const y = toFiniteNumber(value[1] ?? value[0], x);
|
|
458
|
+
return [x, y];
|
|
459
|
+
}
|
|
460
|
+
if (typeof value === 'number') {
|
|
461
|
+
return [value, value];
|
|
462
|
+
}
|
|
463
|
+
if (value && typeof value === 'object') {
|
|
464
|
+
const x = toFiniteNumber(value.x, fallback[0]);
|
|
465
|
+
const y = toFiniteNumber(value.y ?? value.x, x);
|
|
466
|
+
return [x, y];
|
|
467
|
+
}
|
|
468
|
+
return fallback;
|
|
469
|
+
};
|
|
470
|
+
|
|
471
|
+
const normalizeAnchor = (value) => {
|
|
472
|
+
if (!Array.isArray(value)) return undefined;
|
|
473
|
+
const [x, y] = normalizePair(value, [0, 0]);
|
|
474
|
+
return [clampRatio(x), clampRatio(y)];
|
|
475
|
+
};
|
|
476
|
+
|
|
477
|
+
const resolveImageSource = (image) => {
|
|
478
|
+
if (typeof image === 'string') return image;
|
|
479
|
+
if (typeof image?.default === 'string') return image.default;
|
|
480
|
+
return undefined;
|
|
481
|
+
};
|
|
482
|
+
|
|
483
|
+
const parentTextureOptions = (graphicObject) => {
|
|
484
|
+
const props = [
|
|
485
|
+
'width',
|
|
486
|
+
'height',
|
|
487
|
+
'framesHeight',
|
|
488
|
+
'framesWidth',
|
|
489
|
+
'rectWidth',
|
|
490
|
+
'rectHeight',
|
|
491
|
+
'offset',
|
|
492
|
+
'image',
|
|
493
|
+
'sound',
|
|
494
|
+
'spriteRealSize',
|
|
495
|
+
'scale',
|
|
496
|
+
'anchor',
|
|
497
|
+
'pivot',
|
|
498
|
+
'x',
|
|
499
|
+
'y',
|
|
500
|
+
'opacity'
|
|
501
|
+
];
|
|
502
|
+
|
|
503
|
+
return props.reduce((options, prop) => {
|
|
504
|
+
if (graphicObject?.[prop] !== undefined) {
|
|
505
|
+
options[prop] = graphicObject[prop];
|
|
506
|
+
}
|
|
507
|
+
return options;
|
|
508
|
+
}, {});
|
|
509
|
+
};
|
|
510
|
+
|
|
511
|
+
const resolveTextureOptions = (graphicObject) => {
|
|
512
|
+
const textures = graphicObject?.textures ?? {};
|
|
513
|
+
const texture =
|
|
514
|
+
textures[realAnimationName()] ??
|
|
515
|
+
textures[Animation.Stand] ??
|
|
516
|
+
Object.values(textures)[0] ??
|
|
517
|
+
{};
|
|
518
|
+
|
|
519
|
+
return {
|
|
520
|
+
...parentTextureOptions(graphicObject),
|
|
521
|
+
...texture
|
|
522
|
+
};
|
|
523
|
+
};
|
|
524
|
+
|
|
525
|
+
const resolveFirstAnimationFrame = (textureOptions) => {
|
|
526
|
+
const animations = textureOptions?.animations;
|
|
527
|
+
if (!animations) return {};
|
|
528
|
+
|
|
529
|
+
try {
|
|
530
|
+
const frames = typeof animations === 'function'
|
|
531
|
+
? animations({ direction: direction() })
|
|
532
|
+
: animations;
|
|
533
|
+
if (!Array.isArray(frames)) return {};
|
|
534
|
+
const firstGroup = frames[0];
|
|
535
|
+
return Array.isArray(firstGroup) ? firstGroup[0] ?? {} : firstGroup ?? {};
|
|
536
|
+
}
|
|
537
|
+
catch {
|
|
538
|
+
return {};
|
|
539
|
+
}
|
|
540
|
+
};
|
|
541
|
+
|
|
542
|
+
const optionValue = (prop, frame, textureOptions, graphicObject) => {
|
|
543
|
+
return frame?.[prop] ?? textureOptions?.[prop] ?? graphicObject?.[prop];
|
|
544
|
+
};
|
|
545
|
+
|
|
546
|
+
const resolveFrameSize = (textureOptions, dimensions) => {
|
|
547
|
+
const framesWidth = toPositiveNumber(textureOptions?.framesWidth) ?? 1;
|
|
548
|
+
const framesHeight = toPositiveNumber(textureOptions?.framesHeight) ?? 1;
|
|
549
|
+
const imageSource = resolveImageSource(textureOptions?.image);
|
|
550
|
+
const loadedSize = imageSource ? dimensions[imageSource] : undefined;
|
|
551
|
+
const fullWidth = toPositiveNumber(textureOptions?.width) ?? loadedSize?.width;
|
|
552
|
+
const fullHeight = toPositiveNumber(textureOptions?.height) ?? loadedSize?.height;
|
|
553
|
+
const width = toPositiveNumber(textureOptions?.rectWidth) ??
|
|
554
|
+
toPositiveNumber(textureOptions?.spriteWidth) ??
|
|
555
|
+
(fullWidth ? fullWidth / framesWidth : undefined);
|
|
556
|
+
const height = toPositiveNumber(textureOptions?.rectHeight) ??
|
|
557
|
+
toPositiveNumber(textureOptions?.spriteHeight) ??
|
|
558
|
+
(fullHeight ? fullHeight / framesHeight : undefined);
|
|
559
|
+
|
|
560
|
+
return {
|
|
561
|
+
width,
|
|
562
|
+
height
|
|
563
|
+
};
|
|
564
|
+
};
|
|
565
|
+
|
|
566
|
+
const resolveHitboxAnchor = (spriteWidth, spriteHeight, realSize, box) => {
|
|
567
|
+
if (!spriteWidth || !spriteHeight || !box) {
|
|
568
|
+
return [0, 0];
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
const heightOfSprite = typeof realSize === 'number' ? realSize : realSize?.height;
|
|
572
|
+
const resolvedHeight = toPositiveNumber(heightOfSprite) ?? spriteHeight;
|
|
573
|
+
const gap = Math.max(0, (spriteHeight - resolvedHeight) / 2);
|
|
574
|
+
const hitboxTopLeftX = clampRatio((spriteWidth - box.w) / 2 / spriteWidth);
|
|
575
|
+
const hitboxTopLeftY = clampRatio((spriteHeight - box.h - gap) / spriteHeight);
|
|
576
|
+
const hitboxCenterX = clampRatio(hitboxTopLeftX + box.w / 2 / spriteWidth);
|
|
577
|
+
const hitboxCenterY = clampRatio(hitboxTopLeftY + box.h / 2 / spriteHeight);
|
|
578
|
+
const footY = clampRatio((spriteHeight - gap) / spriteHeight);
|
|
579
|
+
|
|
580
|
+
switch (box.anchorMode ?? 'top-left') {
|
|
581
|
+
case 'center':
|
|
582
|
+
return [hitboxCenterX, hitboxCenterY];
|
|
583
|
+
case 'foot':
|
|
584
|
+
return [hitboxCenterX, footY];
|
|
585
|
+
case 'top-left':
|
|
586
|
+
default:
|
|
587
|
+
return [hitboxTopLeftX, hitboxTopLeftY];
|
|
588
|
+
}
|
|
589
|
+
};
|
|
590
|
+
|
|
591
|
+
const loadImageDimensions = (image) => {
|
|
592
|
+
const source = resolveImageSource(image);
|
|
593
|
+
if (!source || imageDimensions()[source] || loadingImageDimensions.has(source)) {
|
|
594
|
+
return;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
loadingImageDimensions.add(source);
|
|
598
|
+
Assets.load(source)
|
|
599
|
+
.then((texture) => {
|
|
600
|
+
const width = toPositiveNumber(texture?.width);
|
|
601
|
+
const height = toPositiveNumber(texture?.height);
|
|
602
|
+
if (!width || !height) return;
|
|
603
|
+
|
|
604
|
+
imageDimensions.update((dimensions) => ({
|
|
605
|
+
...dimensions,
|
|
606
|
+
[source]: { width, height }
|
|
607
|
+
}));
|
|
608
|
+
})
|
|
609
|
+
.catch(() => {})
|
|
610
|
+
.finally(() => {
|
|
611
|
+
loadingImageDimensions.delete(source);
|
|
612
|
+
});
|
|
613
|
+
};
|
|
614
|
+
|
|
615
|
+
effect(() => {
|
|
616
|
+
const sources = new Set();
|
|
617
|
+
|
|
618
|
+
graphicsSignals().forEach((graphicObject) => {
|
|
619
|
+
const baseImage = resolveImageSource(graphicObject?.image);
|
|
620
|
+
if (baseImage) sources.add(baseImage);
|
|
621
|
+
|
|
622
|
+
Object.values(graphicObject?.textures ?? {}).forEach((textureOptions) => {
|
|
623
|
+
const image = resolveImageSource(textureOptions?.image ?? graphicObject?.image);
|
|
624
|
+
if (image) sources.add(image);
|
|
625
|
+
});
|
|
626
|
+
});
|
|
627
|
+
|
|
628
|
+
sources.forEach((source) => loadImageDimensions(source));
|
|
629
|
+
});
|
|
630
|
+
|
|
631
|
+
const hitboxBounds = computed(() => {
|
|
632
|
+
const box = hitbox();
|
|
633
|
+
const width = box?.w ?? 0;
|
|
634
|
+
const height = box?.h ?? 0;
|
|
635
|
+
|
|
636
|
+
return {
|
|
637
|
+
left: 0,
|
|
638
|
+
top: 0,
|
|
639
|
+
right: width,
|
|
640
|
+
bottom: height,
|
|
641
|
+
width,
|
|
642
|
+
height,
|
|
643
|
+
centerX: width / 2,
|
|
644
|
+
centerY: height / 2
|
|
645
|
+
};
|
|
646
|
+
});
|
|
647
|
+
|
|
648
|
+
const graphicBounds = computed(() => {
|
|
649
|
+
const box = hitbox();
|
|
650
|
+
const fallback = hitboxBounds();
|
|
651
|
+
const dimensions = imageDimensions();
|
|
652
|
+
const graphics = graphicsSignals();
|
|
653
|
+
let bounds = null;
|
|
654
|
+
|
|
655
|
+
graphics.forEach((graphicObject) => {
|
|
656
|
+
const textureOptions = resolveTextureOptions(graphicObject);
|
|
657
|
+
const frame = resolveFirstAnimationFrame(textureOptions);
|
|
658
|
+
const size = resolveFrameSize(textureOptions, dimensions);
|
|
659
|
+
const spriteWidth = size.width ?? box?.w;
|
|
660
|
+
const spriteHeight = size.height ?? box?.h;
|
|
661
|
+
|
|
662
|
+
if (!spriteWidth || !spriteHeight) {
|
|
663
|
+
return;
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
const explicitAnchor = normalizeAnchor(optionValue('anchor', frame, textureOptions, graphicObject));
|
|
667
|
+
const anchor = explicitAnchor ?? resolveHitboxAnchor(
|
|
668
|
+
spriteWidth,
|
|
669
|
+
spriteHeight,
|
|
670
|
+
optionValue('spriteRealSize', frame, textureOptions, graphicObject),
|
|
671
|
+
box
|
|
672
|
+
);
|
|
673
|
+
const scale = normalizePair(optionValue('scale', frame, textureOptions, graphicObject) ?? graphicScale(graphicObject));
|
|
674
|
+
const x = toFiniteNumber(optionValue('x', frame, textureOptions, graphicObject), 0);
|
|
675
|
+
const y = toFiniteNumber(optionValue('y', frame, textureOptions, graphicObject), 0);
|
|
676
|
+
const leftEdge = -anchor[0] * spriteWidth * scale[0];
|
|
677
|
+
const rightEdge = (1 - anchor[0]) * spriteWidth * scale[0];
|
|
678
|
+
const topEdge = -anchor[1] * spriteHeight * scale[1];
|
|
679
|
+
const bottomEdge = (1 - anchor[1]) * spriteHeight * scale[1];
|
|
680
|
+
const graphic = {
|
|
681
|
+
left: x + Math.min(leftEdge, rightEdge),
|
|
682
|
+
top: y + Math.min(topEdge, bottomEdge),
|
|
683
|
+
right: x + Math.max(leftEdge, rightEdge),
|
|
684
|
+
bottom: y + Math.max(topEdge, bottomEdge)
|
|
685
|
+
};
|
|
686
|
+
|
|
687
|
+
bounds = bounds
|
|
688
|
+
? {
|
|
689
|
+
left: Math.min(bounds.left, graphic.left),
|
|
690
|
+
top: Math.min(bounds.top, graphic.top),
|
|
691
|
+
right: Math.max(bounds.right, graphic.right),
|
|
692
|
+
bottom: Math.max(bounds.bottom, graphic.bottom)
|
|
693
|
+
}
|
|
694
|
+
: graphic;
|
|
695
|
+
});
|
|
696
|
+
|
|
697
|
+
if (!bounds) {
|
|
698
|
+
return fallback;
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
const width = bounds.right - bounds.left;
|
|
702
|
+
const height = bounds.bottom - bounds.top;
|
|
703
|
+
|
|
704
|
+
return {
|
|
705
|
+
...bounds,
|
|
706
|
+
width,
|
|
707
|
+
height,
|
|
708
|
+
centerX: bounds.left + width / 2,
|
|
709
|
+
centerY: bounds.top + height / 2
|
|
710
|
+
};
|
|
711
|
+
});
|
|
712
|
+
|
|
380
713
|
// Combine animation change detection with movement state from smoothX/smoothY
|
|
381
714
|
const movementAnimations = ['walk', 'stand'];
|
|
382
715
|
const epsilon = 0; // movement threshold to consider the easing still running
|
|
@@ -400,6 +733,51 @@
|
|
|
400
733
|
filter(([prev, curr]) => prev !== curr)
|
|
401
734
|
);
|
|
402
735
|
|
|
736
|
+
let beforeRemovePromise = null;
|
|
737
|
+
let beforeRemoveTransitionValue = null;
|
|
738
|
+
const resolveRemoveContext = () => {
|
|
739
|
+
if (!sprite._removeTransition) return null;
|
|
740
|
+
const value = sprite._removeTransition();
|
|
741
|
+
if (!value || typeof value !== 'string') return null;
|
|
742
|
+
try {
|
|
743
|
+
const context = JSON.parse(value);
|
|
744
|
+
if (!context || typeof context !== 'object' || !context.active) return null;
|
|
745
|
+
context.__transitionValue = value;
|
|
746
|
+
return context;
|
|
747
|
+
}
|
|
748
|
+
catch {
|
|
749
|
+
return null;
|
|
750
|
+
}
|
|
751
|
+
};
|
|
752
|
+
|
|
753
|
+
const withTimeout = (promise, timeoutMs = 0) => {
|
|
754
|
+
if (!timeoutMs || timeoutMs <= 0) return promise;
|
|
755
|
+
return Promise.race([
|
|
756
|
+
promise,
|
|
757
|
+
new Promise((resolve) => setTimeout(resolve, timeoutMs)),
|
|
758
|
+
]);
|
|
759
|
+
};
|
|
760
|
+
|
|
761
|
+
const runBeforeRemove = () => {
|
|
762
|
+
const context = resolveRemoveContext();
|
|
763
|
+
if (!context) return Promise.resolve();
|
|
764
|
+
if (beforeRemovePromise && beforeRemoveTransitionValue === context.__transitionValue) {
|
|
765
|
+
return beforeRemovePromise;
|
|
766
|
+
}
|
|
767
|
+
beforeRemoveTransitionValue = context.__transitionValue;
|
|
768
|
+
beforeRemovePromise = withTimeout(
|
|
769
|
+
lastValueFrom(hooks.callHooks("client-sprite-onBeforeRemove", sprite, context)),
|
|
770
|
+
context.timeoutMs
|
|
771
|
+
);
|
|
772
|
+
return beforeRemovePromise;
|
|
773
|
+
};
|
|
774
|
+
|
|
775
|
+
const removeTransitionSubscription = sprite._removeTransition?.observable?.subscribe(() => {
|
|
776
|
+
if (resolveRemoveContext()) {
|
|
777
|
+
runBeforeRemove();
|
|
778
|
+
}
|
|
779
|
+
});
|
|
780
|
+
|
|
403
781
|
const animationMovementSubscription = combineLatest([animationChange$, moving$]).subscribe(([[prev, curr], isMoving]) => {
|
|
404
782
|
if (curr == 'stand' && !isMoving) {
|
|
405
783
|
realAnimationName.set(curr);
|
|
@@ -410,10 +788,10 @@
|
|
|
410
788
|
else if (!movementAnimations.includes(curr)) {
|
|
411
789
|
realAnimationName.set(curr);
|
|
412
790
|
}
|
|
413
|
-
if (!isMoving &&
|
|
791
|
+
if (!isMoving && sprite.animationIsPlaying && sprite.animationIsPlaying()) {
|
|
414
792
|
if (movementAnimations.includes(curr)) {
|
|
415
|
-
if (typeof
|
|
416
|
-
|
|
793
|
+
if (typeof sprite.resetAnimationState === 'function') {
|
|
794
|
+
sprite.resetAnimationState();
|
|
417
795
|
}
|
|
418
796
|
}
|
|
419
797
|
}
|
|
@@ -429,18 +807,49 @@
|
|
|
429
807
|
* @example
|
|
430
808
|
* await onBeforeDestroy();
|
|
431
809
|
*/
|
|
810
|
+
const waitForTemporaryAnimationEnd = (maxDuration = 1200) => {
|
|
811
|
+
if (!sprite.animationIsPlaying || !sprite.animationIsPlaying()) {
|
|
812
|
+
return Promise.resolve();
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
return new Promise((resolve) => {
|
|
816
|
+
let finished = false;
|
|
817
|
+
let timeout;
|
|
818
|
+
let subscription;
|
|
819
|
+
const finish = () => {
|
|
820
|
+
if (finished) return;
|
|
821
|
+
finished = true;
|
|
822
|
+
clearTimeout(timeout);
|
|
823
|
+
subscription?.unsubscribe();
|
|
824
|
+
resolve();
|
|
825
|
+
};
|
|
826
|
+
timeout = setTimeout(finish, maxDuration);
|
|
827
|
+
subscription = sprite.animationIsPlaying.observable.subscribe((isPlaying) => {
|
|
828
|
+
if (!isPlaying) finish();
|
|
829
|
+
});
|
|
830
|
+
if (finished) subscription.unsubscribe();
|
|
831
|
+
});
|
|
832
|
+
};
|
|
833
|
+
|
|
432
834
|
const onBeforeDestroy = async () => {
|
|
835
|
+
await runBeforeRemove();
|
|
836
|
+
await waitForTemporaryAnimationEnd();
|
|
837
|
+
removeTransitionSubscription?.unsubscribe();
|
|
433
838
|
animationMovementSubscription.unsubscribe();
|
|
434
839
|
xSubscription.unsubscribe();
|
|
435
840
|
ySubscription.unsubscribe();
|
|
436
|
-
await lastValueFrom(hooks.callHooks("client-sprite-onDestroy",
|
|
437
|
-
await lastValueFrom(hooks.callHooks("client-sceneMap-onRemoveSprite", client.sceneMap,
|
|
841
|
+
await lastValueFrom(hooks.callHooks("client-sprite-onDestroy", sprite))
|
|
842
|
+
await lastValueFrom(hooks.callHooks("client-sceneMap-onRemoveSprite", client.sceneMap, sprite))
|
|
438
843
|
}
|
|
439
844
|
|
|
440
845
|
mount((element) => {
|
|
441
|
-
hooks.callHooks("client-sprite-onAdd",
|
|
442
|
-
hooks.callHooks("client-sceneMap-onAddSprite", client.sceneMap,
|
|
443
|
-
|
|
846
|
+
hooks.callHooks("client-sprite-onAdd", sprite).subscribe()
|
|
847
|
+
hooks.callHooks("client-sceneMap-onAddSprite", client.sceneMap, sprite).subscribe()
|
|
848
|
+
effect(() => {
|
|
849
|
+
if (isCurrentPlayer()) {
|
|
850
|
+
client.setKeyboardControls(element.directives.controls)
|
|
851
|
+
}
|
|
852
|
+
})
|
|
444
853
|
})
|
|
445
854
|
|
|
446
855
|
/**
|
|
@@ -467,4 +876,4 @@
|
|
|
467
876
|
tick(() => {
|
|
468
877
|
hooks.callHooks("client-sprite-onUpdate").subscribe()
|
|
469
878
|
})
|
|
470
|
-
</script>
|
|
879
|
+
</script>
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
<Container width={width} height={containerHeight} minWidth={width} minHeight={containerHeight}>
|
|
2
|
+
<Graphics width={width} height={containerHeight} draw={drawBar} />
|
|
3
|
+
@if (hasLabel) {
|
|
4
|
+
<Text text={labelText} x={labelPosition.x} y={labelPosition.y} size={labelSize} color={labelColor} />
|
|
5
|
+
}
|
|
6
|
+
</Container>
|
|
7
|
+
|
|
8
|
+
<script>
|
|
9
|
+
import { computed } from "canvasengine";
|
|
10
|
+
import { resolveDynamicValue } from "./parse-value";
|
|
11
|
+
|
|
12
|
+
const { object, current, max, style, text } = defineProps();
|
|
13
|
+
const sprite = object();
|
|
14
|
+
|
|
15
|
+
const read = (prop, fallback) => prop ? prop() : fallback;
|
|
16
|
+
|
|
17
|
+
const toNumber = (value, fallback = 0) => {
|
|
18
|
+
const resolved = resolveDynamicValue(value, sprite);
|
|
19
|
+
const num = typeof resolved === 'number' ? resolved : parseFloat(resolved);
|
|
20
|
+
return Number.isFinite(num) ? num : fallback;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const toColor = (value, fallback) => {
|
|
24
|
+
const resolved = resolveDynamicValue(value, sprite);
|
|
25
|
+
if (typeof resolved === 'number') return resolved;
|
|
26
|
+
if (typeof resolved === 'string' && resolved.startsWith('#')) {
|
|
27
|
+
return parseInt(resolved.slice(1), 16);
|
|
28
|
+
}
|
|
29
|
+
return resolved ?? fallback;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const config = computed(() => read(style, {}) ?? {});
|
|
33
|
+
const width = computed(() => toNumber(config().width, 50));
|
|
34
|
+
const height = computed(() => toNumber(config().height, 8));
|
|
35
|
+
const borderRadius = computed(() => toNumber(config().borderRadius, 3));
|
|
36
|
+
const borderWidth = computed(() => toNumber(config().borderWidth, 1));
|
|
37
|
+
const backgroundColor = computed(() => toColor(config().bgColor, 0x16213e));
|
|
38
|
+
const fillColor = computed(() => toColor(config().fillColor, 0x4ade80));
|
|
39
|
+
const borderColor = computed(() => toColor(config().borderColor, 0x4a5568));
|
|
40
|
+
const opacity = computed(() => Math.max(0, Math.min(1, toNumber(config().opacity, 1))));
|
|
41
|
+
|
|
42
|
+
const currentValue = computed(() => toNumber(read(current, 0), 0));
|
|
43
|
+
const maxValue = computed(() => Math.max(0, toNumber(read(max, 0), 0)));
|
|
44
|
+
const percent = computed(() => {
|
|
45
|
+
const max = maxValue();
|
|
46
|
+
if (max <= 0) return 0;
|
|
47
|
+
return Math.max(0, Math.min(1, currentValue() / max));
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const fillWidth = computed(() => Math.max(0, width() * percent()));
|
|
51
|
+
const labelTemplate = computed(() => read(text, null));
|
|
52
|
+
const labelText = computed(() => {
|
|
53
|
+
const template = labelTemplate();
|
|
54
|
+
if (template == null || template === '') return '';
|
|
55
|
+
|
|
56
|
+
const value = String(template)
|
|
57
|
+
.replace(/\{\$current\}/g, String(currentValue()))
|
|
58
|
+
.replace(/\{\$max\}/g, String(maxValue()))
|
|
59
|
+
.replace(/\{\$percent\}/g, String(Math.round(percent() * 100)));
|
|
60
|
+
|
|
61
|
+
return String(resolveDynamicValue(value, sprite) ?? '');
|
|
62
|
+
});
|
|
63
|
+
const labelSize = computed(() => toNumber(config().fontSize, 10));
|
|
64
|
+
const hasLabel = computed(() => labelText().length > 0);
|
|
65
|
+
const labelOffset = computed(() => hasLabel() ? labelSize() + 2 : 0);
|
|
66
|
+
const containerHeight = computed(() => labelOffset() + height());
|
|
67
|
+
const labelColor = computed(() => toColor(config().textColor, 0xffffff));
|
|
68
|
+
const labelPosition = computed(() => ({
|
|
69
|
+
x: 0,
|
|
70
|
+
y: 0
|
|
71
|
+
}));
|
|
72
|
+
|
|
73
|
+
const drawBar = (g) => {
|
|
74
|
+
g.roundRect(0, labelOffset(), width(), height(), borderRadius());
|
|
75
|
+
g.fill({ color: backgroundColor(), alpha: opacity() });
|
|
76
|
+
|
|
77
|
+
const currentWidth = fillWidth();
|
|
78
|
+
if (currentWidth > 0) {
|
|
79
|
+
g.roundRect(0, labelOffset(), currentWidth, height(), borderRadius());
|
|
80
|
+
g.fill({ color: fillColor(), alpha: opacity() });
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const strokeWidth = borderWidth();
|
|
84
|
+
if (strokeWidth <= 0) return;
|
|
85
|
+
g.roundRect(0, labelOffset(), width(), height(), borderRadius());
|
|
86
|
+
g.stroke({ color: borderColor(), width: strokeWidth, alpha: opacity() });
|
|
87
|
+
};
|
|
88
|
+
</script>
|