@rpgjs/client 5.0.0-beta.13 → 5.0.0-beta.14
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 +8 -0
- package/dist/RpgClientEngine.js +2 -0
- package/dist/RpgClientEngine.js.map +1 -1
- package/dist/components/character.ce.js +2 -2
- package/dist/components/character.ce.js.map +1 -1
- package/dist/components/scenes/canvas.ce.js +54 -26
- package/dist/components/scenes/canvas.ce.js.map +1 -1
- package/dist/components/scenes/event-layer.ce.js +42 -3
- package/dist/components/scenes/event-layer.ce.js.map +1 -1
- package/dist/services/loadMap.d.ts +3 -0
- package/dist/services/loadMap.js.map +1 -1
- package/package.json +3 -3
- package/src/RpgClientEngine.ts +6 -0
- package/src/components/character.ce +2 -2
- package/src/components/scenes/canvas.ce +56 -24
- package/src/components/scenes/event-layer.ce +54 -2
- package/src/services/loadMap.ts +3 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rpgjs/client",
|
|
3
|
-
"version": "5.0.0-beta.
|
|
3
|
+
"version": "5.0.0-beta.14",
|
|
4
4
|
"description": "RPGJS is a framework for creating RPG/MMORPG games",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -22,8 +22,8 @@
|
|
|
22
22
|
"pixi.js": "^8.9.2"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@rpgjs/common": "5.0.0-beta.
|
|
26
|
-
"@rpgjs/server": "5.0.0-beta.
|
|
25
|
+
"@rpgjs/common": "5.0.0-beta.14",
|
|
26
|
+
"@rpgjs/server": "5.0.0-beta.14",
|
|
27
27
|
"@rpgjs/ui-css": "5.0.0-beta.12",
|
|
28
28
|
"@signe/di": "3.0.1",
|
|
29
29
|
"@signe/room": "3.0.1",
|
package/src/RpgClientEngine.ts
CHANGED
|
@@ -953,6 +953,12 @@ export class RpgClientEngine<T = any> {
|
|
|
953
953
|
throw error;
|
|
954
954
|
}
|
|
955
955
|
const res = await this.loadMapService.load(mapId)
|
|
956
|
+
const loadedLighting = typeof res?.lighting !== "undefined"
|
|
957
|
+
? res.lighting
|
|
958
|
+
: res?.data?.lighting;
|
|
959
|
+
if (typeof loadedLighting !== "undefined") {
|
|
960
|
+
this.sceneMap.lightingState.set(normalizeLightingState(loadedLighting));
|
|
961
|
+
}
|
|
956
962
|
this.sceneMap.data.set(res)
|
|
957
963
|
|
|
958
964
|
// Check if playerId is already present
|
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
|
|
75
75
|
import { lastValueFrom, combineLatest, pairwise, filter, map, startWith } from "rxjs";
|
|
76
76
|
import { Particle } from "@canvasengine/presets";
|
|
77
|
-
import { GameEngineToken, ModulesToken } from "@rpgjs/common";
|
|
77
|
+
import { GameEngineToken, ModulesToken, shouldRenderLightingShadows } from "@rpgjs/common";
|
|
78
78
|
import { RpgClientEngine } from "../RpgClientEngine";
|
|
79
79
|
import { inject } from "../core/inject";
|
|
80
80
|
import { Direction, Animation } from "@rpgjs/common";
|
|
@@ -113,7 +113,7 @@
|
|
|
113
113
|
const isMe = computed(isCurrentPlayer);
|
|
114
114
|
const shadowsEnabled = computed(() => {
|
|
115
115
|
const lighting = client.sceneMap?.lighting?.();
|
|
116
|
-
return
|
|
116
|
+
return shouldRenderLightingShadows(lighting);
|
|
117
117
|
});
|
|
118
118
|
|
|
119
119
|
/**
|
|
@@ -49,6 +49,7 @@
|
|
|
49
49
|
import { RpgClientEngine } from "../../RpgClientEngine";
|
|
50
50
|
import { RpgGui } from "../../Gui/Gui";
|
|
51
51
|
import { NightAmbiant, SpriteShadows } from '@canvasengine/presets'
|
|
52
|
+
import { shouldRenderLightingShadows } from "@rpgjs/common";
|
|
52
53
|
|
|
53
54
|
const engine = inject(RpgClientEngine);
|
|
54
55
|
const SceneMap = engine.sceneMapComponent;
|
|
@@ -99,12 +100,20 @@
|
|
|
99
100
|
const NIGHT_SPOT_MIN_INTENSITY = 1
|
|
100
101
|
const SHADOW_SPOT_RADIUS_SCALE = 12
|
|
101
102
|
const SHADOW_SPOT_MIN_RADIUS = 480
|
|
102
|
-
const SHADOW_SPOT_MIN_INTENSITY = 1.35
|
|
103
103
|
|
|
104
|
+
const toFiniteNumber = (value, fallback = null) => {
|
|
105
|
+
const number = Number(value)
|
|
106
|
+
return Number.isFinite(number) ? number : fallback
|
|
107
|
+
}
|
|
108
|
+
const clampNumber = (value, min, max) => Math.max(min, Math.min(max, value))
|
|
104
109
|
const nightSpotRadius = (radius) => Math.max(radius * NIGHT_SPOT_RADIUS_SCALE, NIGHT_SPOT_MIN_RADIUS)
|
|
105
110
|
const shadowSpotRadius = (radius) => Math.max(radius * SHADOW_SPOT_RADIUS_SCALE, SHADOW_SPOT_MIN_RADIUS)
|
|
106
111
|
const nightSpotIntensity = (intensity, fallback) => Math.max(intensity ?? fallback, NIGHT_SPOT_MIN_INTENSITY)
|
|
107
|
-
const
|
|
112
|
+
const shadowLightIntensity = (intensity, fallback = 1) => {
|
|
113
|
+
const value = Number(intensity ?? fallback)
|
|
114
|
+
return Number.isFinite(value) ? Math.max(0, value) : fallback
|
|
115
|
+
}
|
|
116
|
+
const shadowSpotIntensity = (intensity) => shadowLightIntensity(intensity, 1)
|
|
108
117
|
|
|
109
118
|
const lightingAmbient = computed(() => {
|
|
110
119
|
const state = lighting?.()
|
|
@@ -128,11 +137,6 @@
|
|
|
128
137
|
}
|
|
129
138
|
})
|
|
130
139
|
})
|
|
131
|
-
const hasLightSpots = computed(() => {
|
|
132
|
-
const state = lighting?.()
|
|
133
|
-
return (state?.spots?.length ?? 0) > 0
|
|
134
|
-
})
|
|
135
|
-
|
|
136
140
|
const lightingDarkness = computed(() => {
|
|
137
141
|
const darkness = lightingAmbient().darkness
|
|
138
142
|
return typeof darkness === "number" ? darkness : 0
|
|
@@ -161,18 +165,55 @@
|
|
|
161
165
|
const scale = Number(data?.params?.scale ?? 1) || 1
|
|
162
166
|
const mapWidth = width * scale
|
|
163
167
|
const mapHeight = height * scale
|
|
168
|
+
const projectionBase = Math.max(1, mapWidth, mapHeight)
|
|
164
169
|
|
|
165
170
|
return {
|
|
166
|
-
x: -
|
|
167
|
-
y: -
|
|
171
|
+
x: -projectionBase * 24,
|
|
172
|
+
y: -projectionBase * 24,
|
|
168
173
|
z: 520,
|
|
169
|
-
radius:
|
|
174
|
+
radius: projectionBase * 160,
|
|
170
175
|
intensity: 0.85,
|
|
171
176
|
shadowWeight: lightingDarkness() > 0 ? 2.2 : 1,
|
|
172
177
|
enabled: true,
|
|
173
178
|
}
|
|
174
179
|
}
|
|
175
180
|
|
|
181
|
+
const normalizeSunDirection = (sun) => {
|
|
182
|
+
const x = toFiniteNumber(sun?.x, null)
|
|
183
|
+
const y = toFiniteNumber(sun?.y, null)
|
|
184
|
+
if (x !== null && y !== null && Math.hypot(x, y) > 0.001) {
|
|
185
|
+
return { x, y }
|
|
186
|
+
}
|
|
187
|
+
return { x: -0.45, y: -1 }
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const defaultSunAmbientLight = () => {
|
|
191
|
+
const state = lighting?.()
|
|
192
|
+
if (!state?.sun || state.sun.enabled === false) return null
|
|
193
|
+
|
|
194
|
+
const defaultSun = defaultSunLight()
|
|
195
|
+
const sun = {
|
|
196
|
+
...defaultSun,
|
|
197
|
+
...state.sun,
|
|
198
|
+
intensity: shadowLightIntensity(state.sun.intensity, defaultSun.intensity),
|
|
199
|
+
shadowWeight: state.sun.shadowWeight ?? defaultSun.shadowWeight,
|
|
200
|
+
}
|
|
201
|
+
if (sun.intensity <= 0) return null
|
|
202
|
+
|
|
203
|
+
const direction = normalizeSunDirection(sun)
|
|
204
|
+
const shadowWeight = clampNumber(toFiniteNumber(sun.shadowWeight, lightingDarkness() > 0 ? 1.35 : 1) ?? 1, 0, 4)
|
|
205
|
+
const length = clampNumber(30 + sun.intensity * 38 * Math.max(0.75, shadowWeight), 30, 86)
|
|
206
|
+
|
|
207
|
+
return {
|
|
208
|
+
x: direction.x,
|
|
209
|
+
y: direction.y,
|
|
210
|
+
z: toFiniteNumber(sun.z, 520) ?? 520,
|
|
211
|
+
intensity: clampNumber(sun.intensity, 0, 2),
|
|
212
|
+
shadowWeight,
|
|
213
|
+
length,
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
176
217
|
const shadowState = computed(() => {
|
|
177
218
|
const state = lighting?.()
|
|
178
219
|
return state?.shadows ?? null
|
|
@@ -180,12 +221,6 @@
|
|
|
180
221
|
|
|
181
222
|
const shadowLights = computed(() => {
|
|
182
223
|
const state = lighting?.()
|
|
183
|
-
const defaultSun = defaultSunLight()
|
|
184
|
-
const sun = {
|
|
185
|
-
...defaultSun,
|
|
186
|
-
...(state?.sun ?? {}),
|
|
187
|
-
shadowWeight: state?.sun?.shadowWeight ?? defaultSun.shadowWeight,
|
|
188
|
-
}
|
|
189
224
|
const spotLights = (state?.spots ?? []).map((spot) => {
|
|
190
225
|
const radius = spot.radius ?? 180
|
|
191
226
|
return {
|
|
@@ -194,15 +229,12 @@
|
|
|
194
229
|
z: 170,
|
|
195
230
|
radius: shadowSpotRadius(radius),
|
|
196
231
|
intensity: shadowSpotIntensity(spot.intensity),
|
|
197
|
-
shadowWeight:
|
|
232
|
+
shadowWeight: 1,
|
|
198
233
|
enabled: true,
|
|
199
234
|
}
|
|
200
235
|
})
|
|
201
236
|
|
|
202
|
-
return
|
|
203
|
-
...((sun.enabled === false || sun.intensity <= 0) ? [] : [sun]),
|
|
204
|
-
...spotLights,
|
|
205
|
-
]
|
|
237
|
+
return spotLights
|
|
206
238
|
})
|
|
207
239
|
|
|
208
240
|
const shadowAmbientLight = computed(() => {
|
|
@@ -210,12 +242,12 @@
|
|
|
210
242
|
if (shadows?.ambientLight === null || shadows?.ambientLight?.enabled === false) {
|
|
211
243
|
return null
|
|
212
244
|
}
|
|
213
|
-
return shadows?.ambientLight ??
|
|
245
|
+
return shadows?.ambientLight ?? defaultSunAmbientLight()
|
|
214
246
|
})
|
|
215
247
|
|
|
216
248
|
const shadowEnabled = computed(() => {
|
|
217
|
-
const
|
|
218
|
-
return Boolean((
|
|
249
|
+
const state = lighting?.()
|
|
250
|
+
return Boolean(shouldRenderLightingShadows(state) && (shadowLights().length > 0 || shadowAmbientLight()))
|
|
219
251
|
})
|
|
220
252
|
|
|
221
253
|
const shadowMode = computed(() => shadowState()?.mode ?? "strongest")
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<Container sortableChildren={true}>
|
|
1
|
+
<Container sortableChildren={true} onBeforeDestroy={detachPixiChildren}>
|
|
2
2
|
@for ((event,id) of events) {
|
|
3
3
|
<Character id={id} object={event} />
|
|
4
4
|
}
|
|
@@ -13,14 +13,66 @@
|
|
|
13
13
|
</Container>
|
|
14
14
|
|
|
15
15
|
<script>
|
|
16
|
+
import { effect, mount } from "canvasengine";
|
|
16
17
|
import { inject } from "../../core/inject";
|
|
17
18
|
import { RpgClientEngine } from "../../RpgClientEngine";
|
|
18
19
|
import Character from "../character.ce";
|
|
19
20
|
import LightHalo from "../prebuilt/light-halo.ce";
|
|
20
21
|
|
|
21
22
|
const engine = inject(RpgClientEngine);
|
|
22
|
-
const { children } = defineProps(
|
|
23
|
+
const { children, pixiChildren } = defineProps({
|
|
24
|
+
pixiChildren: {
|
|
25
|
+
default: []
|
|
26
|
+
}
|
|
27
|
+
})
|
|
28
|
+
const readValue = (value) => typeof value === "function" ? value() : value
|
|
29
|
+
let rootContainer = null
|
|
30
|
+
let mountedPixiChildren = []
|
|
23
31
|
|
|
24
32
|
const players = engine.sceneMap.players
|
|
25
33
|
const events = engine.sceneMap.events
|
|
34
|
+
|
|
35
|
+
const getPixiChildren = () => {
|
|
36
|
+
const value = readValue(pixiChildren)
|
|
37
|
+
return Array.isArray(value) ? value.filter(Boolean) : []
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const syncPixiChildren = () => {
|
|
41
|
+
if (!rootContainer) return
|
|
42
|
+
const nextPixiChildren = getPixiChildren()
|
|
43
|
+
mountedPixiChildren.forEach((child) => {
|
|
44
|
+
if (!nextPixiChildren.includes(child) && child.parent === rootContainer) {
|
|
45
|
+
rootContainer.removeChild(child)
|
|
46
|
+
}
|
|
47
|
+
})
|
|
48
|
+
nextPixiChildren.forEach((child) => {
|
|
49
|
+
if (child.parent === rootContainer) return
|
|
50
|
+
if (child.parent) {
|
|
51
|
+
child.parent.removeChild(child)
|
|
52
|
+
}
|
|
53
|
+
rootContainer.addChild(child)
|
|
54
|
+
})
|
|
55
|
+
mountedPixiChildren = nextPixiChildren
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const detachPixiChildren = () => {
|
|
59
|
+
if (!rootContainer) return
|
|
60
|
+
mountedPixiChildren.forEach((child) => {
|
|
61
|
+
if (child.parent === rootContainer) {
|
|
62
|
+
rootContainer.removeChild(child)
|
|
63
|
+
}
|
|
64
|
+
})
|
|
65
|
+
mountedPixiChildren = []
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
effect(() => {
|
|
69
|
+
readValue(pixiChildren)
|
|
70
|
+
syncPixiChildren()
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
mount((element) => {
|
|
74
|
+
rootContainer = element.componentInstance
|
|
75
|
+
syncPixiChildren()
|
|
76
|
+
return detachPixiChildren
|
|
77
|
+
})
|
|
26
78
|
</script>
|
package/src/services/loadMap.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Context, inject } from "@signe/di";
|
|
2
|
-
import { UpdateMapToken, UpdateMapService } from "@rpgjs/common";
|
|
2
|
+
import { UpdateMapToken, UpdateMapService, type LightingState } from "@rpgjs/common";
|
|
3
3
|
|
|
4
4
|
export const LoadMapToken = 'LoadMapToken'
|
|
5
5
|
|
|
@@ -24,6 +24,8 @@ type MapData = {
|
|
|
24
24
|
positions?: Record<string, { x: number; y: number; z?: number }>;
|
|
25
25
|
/** Optional map identifier, defaults to the mapId parameter if not provided */
|
|
26
26
|
id?: string;
|
|
27
|
+
/** Optional initial lighting state for the loaded map */
|
|
28
|
+
lighting?: LightingState | null;
|
|
27
29
|
}
|
|
28
30
|
|
|
29
31
|
/**
|