@zylem/game-lib 0.5.1 → 0.6.0
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/LICENSE +21 -0
- package/dist/behaviors.d.ts +1 -3
- package/dist/behaviors.js.map +1 -1
- package/dist/blueprints-BOCc3Wve.d.ts +26 -0
- package/dist/{camera-Dk-fOVZE.d.ts → camera-CpbDr4-V.d.ts} +19 -45
- package/dist/camera.d.ts +2 -2
- package/dist/camera.js +217 -113
- package/dist/camera.js.map +1 -1
- package/dist/{core-C2mjetAd.d.ts → core-CZhozNRH.d.ts} +77 -42
- package/dist/core.d.ts +6 -5
- package/dist/core.js +1676 -695
- package/dist/core.js.map +1 -1
- package/dist/entities-BAxfJOkk.d.ts +307 -0
- package/dist/entities.d.ts +4 -267
- package/dist/entities.js +213 -90
- package/dist/entities.js.map +1 -1
- package/dist/entity-Bq_eNEDI.d.ts +28 -0
- package/dist/{entity-bQElAdpo.d.ts → entity-COvRtFNG.d.ts} +68 -20
- package/dist/main.d.ts +88 -12
- package/dist/main.js +1811 -810
- package/dist/main.js.map +1 -1
- package/dist/{stage-CrmY7V0i.d.ts → stage-types-CD21XoIU.d.ts} +90 -39
- package/dist/stage.d.ts +40 -20
- package/dist/stage.js +1210 -674
- package/dist/stage.js.map +1 -1
- package/package.json +10 -10
- package/dist/entity-spawner-DNnLYnZq.d.ts +0 -11
package/dist/core.js
CHANGED
|
@@ -9,6 +9,18 @@ var __export = (target, all) => {
|
|
|
9
9
|
};
|
|
10
10
|
|
|
11
11
|
// src/lib/core/utility/path-utils.ts
|
|
12
|
+
function getByPath(obj, path) {
|
|
13
|
+
if (!path) return void 0;
|
|
14
|
+
const keys = path.split(".");
|
|
15
|
+
let current = obj;
|
|
16
|
+
for (const key of keys) {
|
|
17
|
+
if (current == null || typeof current !== "object") {
|
|
18
|
+
return void 0;
|
|
19
|
+
}
|
|
20
|
+
current = current[key];
|
|
21
|
+
}
|
|
22
|
+
return current;
|
|
23
|
+
}
|
|
12
24
|
function setByPath(obj, path, value) {
|
|
13
25
|
if (!path) return;
|
|
14
26
|
const keys = path.split(".");
|
|
@@ -28,10 +40,95 @@ var init_path_utils = __esm({
|
|
|
28
40
|
}
|
|
29
41
|
});
|
|
30
42
|
|
|
43
|
+
// src/lib/game/game-event-bus.ts
|
|
44
|
+
var GameEventBus, gameEventBus;
|
|
45
|
+
var init_game_event_bus = __esm({
|
|
46
|
+
"src/lib/game/game-event-bus.ts"() {
|
|
47
|
+
"use strict";
|
|
48
|
+
GameEventBus = class {
|
|
49
|
+
listeners = /* @__PURE__ */ new Map();
|
|
50
|
+
/**
|
|
51
|
+
* Subscribe to an event type.
|
|
52
|
+
*/
|
|
53
|
+
on(event, callback) {
|
|
54
|
+
if (!this.listeners.has(event)) {
|
|
55
|
+
this.listeners.set(event, /* @__PURE__ */ new Set());
|
|
56
|
+
}
|
|
57
|
+
this.listeners.get(event).add(callback);
|
|
58
|
+
return () => this.off(event, callback);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Unsubscribe from an event type.
|
|
62
|
+
*/
|
|
63
|
+
off(event, callback) {
|
|
64
|
+
this.listeners.get(event)?.delete(callback);
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Emit an event to all subscribers.
|
|
68
|
+
*/
|
|
69
|
+
emit(event, payload) {
|
|
70
|
+
const callbacks = this.listeners.get(event);
|
|
71
|
+
if (!callbacks) return;
|
|
72
|
+
for (const cb of callbacks) {
|
|
73
|
+
try {
|
|
74
|
+
cb(payload);
|
|
75
|
+
} catch (e) {
|
|
76
|
+
console.error(`Error in event handler for ${event}`, e);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Clear all listeners.
|
|
82
|
+
*/
|
|
83
|
+
dispose() {
|
|
84
|
+
this.listeners.clear();
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
gameEventBus = new GameEventBus();
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
|
|
31
91
|
// src/lib/game/game-state.ts
|
|
32
92
|
import { proxy, subscribe } from "valtio/vanilla";
|
|
33
93
|
function setGlobal(path, value) {
|
|
94
|
+
const previousValue = getByPath(state.globals, path);
|
|
34
95
|
setByPath(state.globals, path, value);
|
|
96
|
+
gameEventBus.emit("game:state:updated", {
|
|
97
|
+
path,
|
|
98
|
+
value,
|
|
99
|
+
previousValue
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
function onGlobalChange(path, callback) {
|
|
103
|
+
let previous = getByPath(state.globals, path);
|
|
104
|
+
const unsub = subscribe(state.globals, () => {
|
|
105
|
+
const current = getByPath(state.globals, path);
|
|
106
|
+
if (current !== previous) {
|
|
107
|
+
previous = current;
|
|
108
|
+
callback(current);
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
activeSubscriptions.add(unsub);
|
|
112
|
+
return () => {
|
|
113
|
+
unsub();
|
|
114
|
+
activeSubscriptions.delete(unsub);
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
function onGlobalChanges(paths, callback) {
|
|
118
|
+
let previousValues = paths.map((p) => getByPath(state.globals, p));
|
|
119
|
+
const unsub = subscribe(state.globals, () => {
|
|
120
|
+
const currentValues = paths.map((p) => getByPath(state.globals, p));
|
|
121
|
+
const hasChange = currentValues.some((val, i) => val !== previousValues[i]);
|
|
122
|
+
if (hasChange) {
|
|
123
|
+
previousValues = currentValues;
|
|
124
|
+
callback(currentValues);
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
activeSubscriptions.add(unsub);
|
|
128
|
+
return () => {
|
|
129
|
+
unsub();
|
|
130
|
+
activeSubscriptions.delete(unsub);
|
|
131
|
+
};
|
|
35
132
|
}
|
|
36
133
|
function getGlobals() {
|
|
37
134
|
return state.globals;
|
|
@@ -44,16 +141,24 @@ function initGlobals(globals) {
|
|
|
44
141
|
function resetGlobals() {
|
|
45
142
|
state.globals = {};
|
|
46
143
|
}
|
|
47
|
-
|
|
144
|
+
function clearGlobalSubscriptions() {
|
|
145
|
+
for (const unsub of activeSubscriptions) {
|
|
146
|
+
unsub();
|
|
147
|
+
}
|
|
148
|
+
activeSubscriptions.clear();
|
|
149
|
+
}
|
|
150
|
+
var state, activeSubscriptions;
|
|
48
151
|
var init_game_state = __esm({
|
|
49
152
|
"src/lib/game/game-state.ts"() {
|
|
50
153
|
"use strict";
|
|
51
154
|
init_path_utils();
|
|
155
|
+
init_game_event_bus();
|
|
52
156
|
state = proxy({
|
|
53
157
|
id: "",
|
|
54
158
|
globals: {},
|
|
55
159
|
time: 0
|
|
56
160
|
});
|
|
161
|
+
activeSubscriptions = /* @__PURE__ */ new Set();
|
|
57
162
|
}
|
|
58
163
|
});
|
|
59
164
|
|
|
@@ -119,21 +224,76 @@ var init_base_node = __esm({
|
|
|
119
224
|
uuid = "";
|
|
120
225
|
name = "";
|
|
121
226
|
markedForRemoval = false;
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
227
|
+
/**
|
|
228
|
+
* Lifecycle callback arrays - use onSetup(), onUpdate(), etc. to add callbacks
|
|
229
|
+
*/
|
|
230
|
+
lifecycleCallbacks = {
|
|
231
|
+
setup: [],
|
|
232
|
+
loaded: [],
|
|
233
|
+
update: [],
|
|
234
|
+
destroy: [],
|
|
235
|
+
cleanup: []
|
|
131
236
|
};
|
|
132
237
|
constructor(args = []) {
|
|
133
238
|
const options = args.filter((arg) => !(arg instanceof _BaseNode)).reduce((acc, opt) => ({ ...acc, ...opt }), {});
|
|
134
239
|
this.options = options;
|
|
135
240
|
this.uuid = nanoid();
|
|
136
241
|
}
|
|
242
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
243
|
+
// Fluent API for adding lifecycle callbacks
|
|
244
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
245
|
+
/**
|
|
246
|
+
* Add setup callbacks to be executed in order during nodeSetup
|
|
247
|
+
*/
|
|
248
|
+
onSetup(...callbacks) {
|
|
249
|
+
this.lifecycleCallbacks.setup.push(...callbacks);
|
|
250
|
+
return this;
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Add loaded callbacks to be executed in order during nodeLoaded
|
|
254
|
+
*/
|
|
255
|
+
onLoaded(...callbacks) {
|
|
256
|
+
this.lifecycleCallbacks.loaded.push(...callbacks);
|
|
257
|
+
return this;
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Add update callbacks to be executed in order during nodeUpdate
|
|
261
|
+
*/
|
|
262
|
+
onUpdate(...callbacks) {
|
|
263
|
+
this.lifecycleCallbacks.update.push(...callbacks);
|
|
264
|
+
return this;
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Add destroy callbacks to be executed in order during nodeDestroy
|
|
268
|
+
*/
|
|
269
|
+
onDestroy(...callbacks) {
|
|
270
|
+
this.lifecycleCallbacks.destroy.push(...callbacks);
|
|
271
|
+
return this;
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Add cleanup callbacks to be executed in order during nodeCleanup
|
|
275
|
+
*/
|
|
276
|
+
onCleanup(...callbacks) {
|
|
277
|
+
this.lifecycleCallbacks.cleanup.push(...callbacks);
|
|
278
|
+
return this;
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Prepend setup callbacks (run before existing ones)
|
|
282
|
+
*/
|
|
283
|
+
prependSetup(...callbacks) {
|
|
284
|
+
this.lifecycleCallbacks.setup.unshift(...callbacks);
|
|
285
|
+
return this;
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Prepend update callbacks (run before existing ones)
|
|
289
|
+
*/
|
|
290
|
+
prependUpdate(...callbacks) {
|
|
291
|
+
this.lifecycleCallbacks.update.unshift(...callbacks);
|
|
292
|
+
return this;
|
|
293
|
+
}
|
|
294
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
295
|
+
// Tree structure
|
|
296
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
137
297
|
setParent(parent) {
|
|
138
298
|
this.parent = parent;
|
|
139
299
|
}
|
|
@@ -157,6 +317,9 @@ var init_base_node = __esm({
|
|
|
157
317
|
isComposite() {
|
|
158
318
|
return this.children.length > 0;
|
|
159
319
|
}
|
|
320
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
321
|
+
// Node lifecycle execution - runs internal + callback arrays
|
|
322
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
160
323
|
nodeSetup(params) {
|
|
161
324
|
if (DEBUG_FLAG) {
|
|
162
325
|
}
|
|
@@ -164,8 +327,8 @@ var init_base_node = __esm({
|
|
|
164
327
|
if (typeof this._setup === "function") {
|
|
165
328
|
this._setup(params);
|
|
166
329
|
}
|
|
167
|
-
|
|
168
|
-
|
|
330
|
+
for (const callback of this.lifecycleCallbacks.setup) {
|
|
331
|
+
callback(params);
|
|
169
332
|
}
|
|
170
333
|
this.children.forEach((child) => child.nodeSetup(params));
|
|
171
334
|
}
|
|
@@ -176,21 +339,40 @@ var init_base_node = __esm({
|
|
|
176
339
|
if (typeof this._update === "function") {
|
|
177
340
|
this._update(params);
|
|
178
341
|
}
|
|
179
|
-
|
|
180
|
-
|
|
342
|
+
for (const callback of this.lifecycleCallbacks.update) {
|
|
343
|
+
callback(params);
|
|
181
344
|
}
|
|
182
345
|
this.children.forEach((child) => child.nodeUpdate(params));
|
|
183
346
|
}
|
|
184
347
|
nodeDestroy(params) {
|
|
185
348
|
this.children.forEach((child) => child.nodeDestroy(params));
|
|
186
|
-
|
|
187
|
-
|
|
349
|
+
for (const callback of this.lifecycleCallbacks.destroy) {
|
|
350
|
+
callback(params);
|
|
188
351
|
}
|
|
189
352
|
if (typeof this._destroy === "function") {
|
|
190
353
|
this._destroy(params);
|
|
191
354
|
}
|
|
192
355
|
this.markedForRemoval = true;
|
|
193
356
|
}
|
|
357
|
+
async nodeLoaded(params) {
|
|
358
|
+
if (typeof this._loaded === "function") {
|
|
359
|
+
await this._loaded(params);
|
|
360
|
+
}
|
|
361
|
+
for (const callback of this.lifecycleCallbacks.loaded) {
|
|
362
|
+
callback(params);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
async nodeCleanup(params) {
|
|
366
|
+
for (const callback of this.lifecycleCallbacks.cleanup) {
|
|
367
|
+
callback(params);
|
|
368
|
+
}
|
|
369
|
+
if (typeof this._cleanup === "function") {
|
|
370
|
+
await this._cleanup(params);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
374
|
+
// Options
|
|
375
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
194
376
|
getOptions() {
|
|
195
377
|
return this.options;
|
|
196
378
|
}
|
|
@@ -206,57 +388,54 @@ import {
|
|
|
206
388
|
defineSystem,
|
|
207
389
|
defineQuery,
|
|
208
390
|
defineComponent,
|
|
209
|
-
Types
|
|
391
|
+
Types,
|
|
392
|
+
removeQuery
|
|
210
393
|
} from "bitecs";
|
|
211
394
|
import { Quaternion } from "three";
|
|
212
395
|
function createTransformSystem(stage) {
|
|
213
|
-
const
|
|
396
|
+
const queryTerms = [position, rotation];
|
|
397
|
+
const transformQuery = defineQuery(queryTerms);
|
|
214
398
|
const stageEntities = stage._childrenMap;
|
|
215
|
-
|
|
399
|
+
const system = defineSystem((world) => {
|
|
216
400
|
const entities = transformQuery(world);
|
|
217
401
|
if (stageEntities === void 0) {
|
|
218
402
|
return world;
|
|
219
403
|
}
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
const id = entities[key];
|
|
223
|
-
const stageEntity = value;
|
|
224
|
-
if (stageEntity === void 0 || !stageEntity?.body || stageEntity.markedForRemoval) {
|
|
404
|
+
for (const [key, stageEntity] of stageEntities) {
|
|
405
|
+
if (!stageEntity?.body || stageEntity.markedForRemoval) {
|
|
225
406
|
continue;
|
|
226
407
|
}
|
|
227
|
-
const
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
408
|
+
const id = entities[key];
|
|
409
|
+
const body = stageEntity.body;
|
|
410
|
+
const target = stageEntity.group ?? stageEntity.mesh;
|
|
411
|
+
const translation = body.translation();
|
|
412
|
+
position.x[id] = translation.x;
|
|
413
|
+
position.y[id] = translation.y;
|
|
414
|
+
position.z[id] = translation.z;
|
|
415
|
+
if (target) {
|
|
416
|
+
target.position.set(translation.x, translation.y, translation.z);
|
|
235
417
|
}
|
|
236
418
|
if (stageEntity.controlledRotation) {
|
|
237
419
|
continue;
|
|
238
420
|
}
|
|
239
|
-
const
|
|
240
|
-
rotation.x[id] =
|
|
241
|
-
rotation.y[id] =
|
|
242
|
-
rotation.z[id] =
|
|
243
|
-
rotation.w[id] =
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
rotation.z[id],
|
|
248
|
-
rotation.w[id]
|
|
249
|
-
);
|
|
250
|
-
if (stageEntity.group) {
|
|
251
|
-
stageEntity.group.setRotationFromQuaternion(newRotation);
|
|
252
|
-
} else if (stageEntity.mesh) {
|
|
253
|
-
stageEntity.mesh.setRotationFromQuaternion(newRotation);
|
|
421
|
+
const rot = body.rotation();
|
|
422
|
+
rotation.x[id] = rot.x;
|
|
423
|
+
rotation.y[id] = rot.y;
|
|
424
|
+
rotation.z[id] = rot.z;
|
|
425
|
+
rotation.w[id] = rot.w;
|
|
426
|
+
if (target) {
|
|
427
|
+
_tempQuaternion.set(rot.x, rot.y, rot.z, rot.w);
|
|
428
|
+
target.setRotationFromQuaternion(_tempQuaternion);
|
|
254
429
|
}
|
|
255
430
|
}
|
|
256
431
|
return world;
|
|
257
432
|
});
|
|
433
|
+
const destroy = (world) => {
|
|
434
|
+
removeQuery(world, transformQuery);
|
|
435
|
+
};
|
|
436
|
+
return { system, destroy };
|
|
258
437
|
}
|
|
259
|
-
var position, rotation, scale;
|
|
438
|
+
var position, rotation, scale, _tempQuaternion;
|
|
260
439
|
var init_transformable_system = __esm({
|
|
261
440
|
"src/lib/systems/transformable.system.ts"() {
|
|
262
441
|
"use strict";
|
|
@@ -276,6 +455,7 @@ var init_transformable_system = __esm({
|
|
|
276
455
|
y: Types.f32,
|
|
277
456
|
z: Types.f32
|
|
278
457
|
});
|
|
458
|
+
_tempQuaternion = new Quaternion();
|
|
279
459
|
}
|
|
280
460
|
});
|
|
281
461
|
|
|
@@ -299,11 +479,6 @@ var init_entity = __esm({
|
|
|
299
479
|
custom = {};
|
|
300
480
|
debugInfo = {};
|
|
301
481
|
debugMaterial;
|
|
302
|
-
lifeCycleDelegate = {
|
|
303
|
-
setup: [],
|
|
304
|
-
update: [],
|
|
305
|
-
destroy: []
|
|
306
|
-
};
|
|
307
482
|
collisionDelegate = {
|
|
308
483
|
collision: []
|
|
309
484
|
};
|
|
@@ -328,67 +503,40 @@ var init_entity = __esm({
|
|
|
328
503
|
this.name = this.options.name || "";
|
|
329
504
|
return this;
|
|
330
505
|
}
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
...this.lifeCycleDelegate,
|
|
335
|
-
setup: combineCallbacks
|
|
336
|
-
};
|
|
337
|
-
return this;
|
|
338
|
-
}
|
|
339
|
-
onUpdate(...callbacks) {
|
|
340
|
-
const combineCallbacks = [...this.lifeCycleDelegate.update ?? [], ...callbacks];
|
|
341
|
-
this.lifeCycleDelegate = {
|
|
342
|
-
...this.lifeCycleDelegate,
|
|
343
|
-
update: combineCallbacks
|
|
344
|
-
};
|
|
345
|
-
return this;
|
|
346
|
-
}
|
|
347
|
-
onDestroy(...callbacks) {
|
|
348
|
-
this.lifeCycleDelegate = {
|
|
349
|
-
...this.lifeCycleDelegate,
|
|
350
|
-
destroy: callbacks.length > 0 ? callbacks : void 0
|
|
351
|
-
};
|
|
352
|
-
return this;
|
|
353
|
-
}
|
|
506
|
+
/**
|
|
507
|
+
* Add collision callbacks
|
|
508
|
+
*/
|
|
354
509
|
onCollision(...callbacks) {
|
|
355
|
-
this.collisionDelegate
|
|
356
|
-
|
|
357
|
-
};
|
|
510
|
+
const existing = this.collisionDelegate.collision ?? [];
|
|
511
|
+
this.collisionDelegate.collision = [...existing, ...callbacks];
|
|
358
512
|
return this;
|
|
359
513
|
}
|
|
514
|
+
/**
|
|
515
|
+
* Entity-specific setup - runs behavior callbacks
|
|
516
|
+
* (User callbacks are handled by BaseNode's lifecycleCallbacks.setup)
|
|
517
|
+
*/
|
|
360
518
|
_setup(params) {
|
|
361
519
|
this.behaviorCallbackMap.setup.forEach((callback) => {
|
|
362
520
|
callback({ ...params, me: this });
|
|
363
521
|
});
|
|
364
|
-
if (this.lifeCycleDelegate.setup?.length) {
|
|
365
|
-
const callbacks = this.lifeCycleDelegate.setup;
|
|
366
|
-
callbacks.forEach((callback) => {
|
|
367
|
-
callback({ ...params, me: this });
|
|
368
|
-
});
|
|
369
|
-
}
|
|
370
522
|
}
|
|
371
523
|
async _loaded(_params) {
|
|
372
524
|
}
|
|
525
|
+
/**
|
|
526
|
+
* Entity-specific update - updates materials and runs behavior callbacks
|
|
527
|
+
* (User callbacks are handled by BaseNode's lifecycleCallbacks.update)
|
|
528
|
+
*/
|
|
373
529
|
_update(params) {
|
|
374
530
|
this.updateMaterials(params);
|
|
375
|
-
if (this.lifeCycleDelegate.update?.length) {
|
|
376
|
-
const callbacks = this.lifeCycleDelegate.update;
|
|
377
|
-
callbacks.forEach((callback) => {
|
|
378
|
-
callback({ ...params, me: this });
|
|
379
|
-
});
|
|
380
|
-
}
|
|
381
531
|
this.behaviorCallbackMap.update.forEach((callback) => {
|
|
382
532
|
callback({ ...params, me: this });
|
|
383
533
|
});
|
|
384
534
|
}
|
|
535
|
+
/**
|
|
536
|
+
* Entity-specific destroy - runs behavior callbacks
|
|
537
|
+
* (User callbacks are handled by BaseNode's lifecycleCallbacks.destroy)
|
|
538
|
+
*/
|
|
385
539
|
_destroy(params) {
|
|
386
|
-
if (this.lifeCycleDelegate.destroy?.length) {
|
|
387
|
-
const callbacks = this.lifeCycleDelegate.destroy;
|
|
388
|
-
callbacks.forEach((callback) => {
|
|
389
|
-
callback({ ...params, me: this });
|
|
390
|
-
});
|
|
391
|
-
}
|
|
392
540
|
this.behaviorCallbackMap.destroy.forEach((callback) => {
|
|
393
541
|
callback({ ...params, me: this });
|
|
394
542
|
});
|
|
@@ -678,6 +826,23 @@ var init_animation = __esm({
|
|
|
678
826
|
this._currentAction = action;
|
|
679
827
|
this._currentKey = key;
|
|
680
828
|
}
|
|
829
|
+
/**
|
|
830
|
+
* Dispose of all animation resources
|
|
831
|
+
*/
|
|
832
|
+
dispose() {
|
|
833
|
+
Object.values(this._actions).forEach((action) => {
|
|
834
|
+
action.stop();
|
|
835
|
+
});
|
|
836
|
+
if (this._mixer) {
|
|
837
|
+
this._mixer.stopAllAction();
|
|
838
|
+
this._mixer.uncacheRoot(this.target);
|
|
839
|
+
this._mixer = null;
|
|
840
|
+
}
|
|
841
|
+
this._actions = {};
|
|
842
|
+
this._animations = [];
|
|
843
|
+
this._currentAction = null;
|
|
844
|
+
this._currentKey = "";
|
|
845
|
+
}
|
|
681
846
|
get currentAnimationKey() {
|
|
682
847
|
return this._currentKey;
|
|
683
848
|
}
|
|
@@ -1093,7 +1258,7 @@ var init_actor = __esm({
|
|
|
1093
1258
|
animations: [],
|
|
1094
1259
|
models: []
|
|
1095
1260
|
};
|
|
1096
|
-
ACTOR_TYPE = Symbol("Actor");
|
|
1261
|
+
ACTOR_TYPE = /* @__PURE__ */ Symbol("Actor");
|
|
1097
1262
|
ZylemActor = class extends GameEntity {
|
|
1098
1263
|
static type = ACTOR_TYPE;
|
|
1099
1264
|
_object = null;
|
|
@@ -1104,9 +1269,7 @@ var init_actor = __esm({
|
|
|
1104
1269
|
constructor(options) {
|
|
1105
1270
|
super();
|
|
1106
1271
|
this.options = { ...actorDefaults, ...options };
|
|
1107
|
-
this.
|
|
1108
|
-
update: [this.actorUpdate.bind(this)]
|
|
1109
|
-
};
|
|
1272
|
+
this.prependUpdate(this.actorUpdate.bind(this));
|
|
1110
1273
|
this.controlledRotation = true;
|
|
1111
1274
|
}
|
|
1112
1275
|
async load() {
|
|
@@ -1126,6 +1289,34 @@ var init_actor = __esm({
|
|
|
1126
1289
|
async actorUpdate(params) {
|
|
1127
1290
|
this._animationDelegate?.update(params.delta);
|
|
1128
1291
|
}
|
|
1292
|
+
/**
|
|
1293
|
+
* Clean up actor resources including animations, models, and groups
|
|
1294
|
+
*/
|
|
1295
|
+
actorDestroy() {
|
|
1296
|
+
if (this._animationDelegate) {
|
|
1297
|
+
this._animationDelegate.dispose();
|
|
1298
|
+
this._animationDelegate = null;
|
|
1299
|
+
}
|
|
1300
|
+
if (this._object) {
|
|
1301
|
+
this._object.traverse((child) => {
|
|
1302
|
+
if (child.isMesh) {
|
|
1303
|
+
const mesh = child;
|
|
1304
|
+
mesh.geometry?.dispose();
|
|
1305
|
+
if (Array.isArray(mesh.material)) {
|
|
1306
|
+
mesh.material.forEach((m) => m.dispose());
|
|
1307
|
+
} else if (mesh.material) {
|
|
1308
|
+
mesh.material.dispose();
|
|
1309
|
+
}
|
|
1310
|
+
}
|
|
1311
|
+
});
|
|
1312
|
+
this._object = null;
|
|
1313
|
+
}
|
|
1314
|
+
if (this.group) {
|
|
1315
|
+
this.group.clear();
|
|
1316
|
+
this.group = null;
|
|
1317
|
+
}
|
|
1318
|
+
this._modelFileNames = [];
|
|
1319
|
+
}
|
|
1129
1320
|
async loadModels() {
|
|
1130
1321
|
if (this._modelFileNames.length === 0) return;
|
|
1131
1322
|
const promises = this._modelFileNames.map((file) => this._assetLoader.loadFile(file));
|
|
@@ -1185,25 +1376,17 @@ var init_actor = __esm({
|
|
|
1185
1376
|
}
|
|
1186
1377
|
});
|
|
1187
1378
|
|
|
1188
|
-
// src/lib/collision/
|
|
1379
|
+
// src/lib/collision/world.ts
|
|
1380
|
+
import RAPIER from "@dimforge/rapier3d-compat";
|
|
1189
1381
|
function isCollisionHandlerDelegate(obj) {
|
|
1190
1382
|
return typeof obj?.handlePostCollision === "function" && typeof obj?.handleIntersectionEvent === "function";
|
|
1191
1383
|
}
|
|
1192
|
-
var init_collision_delegate = __esm({
|
|
1193
|
-
"src/lib/collision/collision-delegate.ts"() {
|
|
1194
|
-
"use strict";
|
|
1195
|
-
}
|
|
1196
|
-
});
|
|
1197
|
-
|
|
1198
|
-
// src/lib/collision/world.ts
|
|
1199
|
-
import RAPIER from "@dimforge/rapier3d-compat";
|
|
1200
1384
|
var ZylemWorld;
|
|
1201
1385
|
var init_world = __esm({
|
|
1202
1386
|
"src/lib/collision/world.ts"() {
|
|
1203
1387
|
"use strict";
|
|
1204
1388
|
init_game_state();
|
|
1205
1389
|
init_actor();
|
|
1206
|
-
init_collision_delegate();
|
|
1207
1390
|
ZylemWorld = class {
|
|
1208
1391
|
type = "World";
|
|
1209
1392
|
world;
|
|
@@ -1409,7 +1592,11 @@ var init_zylem_scene = __esm({
|
|
|
1409
1592
|
* Setup camera with the scene
|
|
1410
1593
|
*/
|
|
1411
1594
|
setupCamera(scene, camera) {
|
|
1412
|
-
|
|
1595
|
+
if (camera.cameraRig) {
|
|
1596
|
+
scene.add(camera.cameraRig);
|
|
1597
|
+
} else {
|
|
1598
|
+
scene.add(camera.camera);
|
|
1599
|
+
}
|
|
1413
1600
|
camera.setup(scene);
|
|
1414
1601
|
}
|
|
1415
1602
|
/**
|
|
@@ -1470,7 +1657,7 @@ var init_zylem_scene = __esm({
|
|
|
1470
1657
|
|
|
1471
1658
|
// src/lib/stage/stage-state.ts
|
|
1472
1659
|
import { Color as Color5, Vector3 as Vector35 } from "three";
|
|
1473
|
-
import { proxy as proxy3, subscribe as
|
|
1660
|
+
import { proxy as proxy3, subscribe as subscribe3 } from "valtio/vanilla";
|
|
1474
1661
|
function clearVariables(target) {
|
|
1475
1662
|
variableProxyStore.delete(target);
|
|
1476
1663
|
}
|
|
@@ -1558,59 +1745,346 @@ var init_lifecycle_base = __esm({
|
|
|
1558
1745
|
}
|
|
1559
1746
|
});
|
|
1560
1747
|
|
|
1561
|
-
// src/lib/
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
import { Vector3 as Vector37 } from "three";
|
|
1578
|
-
var ThirdPersonCamera;
|
|
1579
|
-
var init_third_person = __esm({
|
|
1580
|
-
"src/lib/camera/third-person.ts"() {
|
|
1748
|
+
// src/lib/stage/debug-entity-cursor.ts
|
|
1749
|
+
import {
|
|
1750
|
+
Box3,
|
|
1751
|
+
BoxGeometry,
|
|
1752
|
+
Color as Color7,
|
|
1753
|
+
EdgesGeometry,
|
|
1754
|
+
Group as Group4,
|
|
1755
|
+
LineBasicMaterial,
|
|
1756
|
+
LineSegments,
|
|
1757
|
+
Mesh as Mesh3,
|
|
1758
|
+
MeshBasicMaterial,
|
|
1759
|
+
Vector3 as Vector37
|
|
1760
|
+
} from "three";
|
|
1761
|
+
var DebugEntityCursor;
|
|
1762
|
+
var init_debug_entity_cursor = __esm({
|
|
1763
|
+
"src/lib/stage/debug-entity-cursor.ts"() {
|
|
1581
1764
|
"use strict";
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
* Setup the third person camera controller
|
|
1593
|
-
*/
|
|
1594
|
-
setup(params) {
|
|
1595
|
-
const { screenResolution, renderer, scene, camera } = params;
|
|
1596
|
-
this.screenResolution = screenResolution;
|
|
1597
|
-
this.renderer = renderer;
|
|
1765
|
+
DebugEntityCursor = class {
|
|
1766
|
+
scene;
|
|
1767
|
+
container;
|
|
1768
|
+
fillMesh;
|
|
1769
|
+
edgeLines;
|
|
1770
|
+
currentColor = new Color7(65280);
|
|
1771
|
+
bbox = new Box3();
|
|
1772
|
+
size = new Vector37();
|
|
1773
|
+
center = new Vector37();
|
|
1774
|
+
constructor(scene) {
|
|
1598
1775
|
this.scene = scene;
|
|
1599
|
-
|
|
1776
|
+
const initialGeometry = new BoxGeometry(1, 1, 1);
|
|
1777
|
+
this.fillMesh = new Mesh3(
|
|
1778
|
+
initialGeometry,
|
|
1779
|
+
new MeshBasicMaterial({
|
|
1780
|
+
color: this.currentColor,
|
|
1781
|
+
transparent: true,
|
|
1782
|
+
opacity: 0.12,
|
|
1783
|
+
depthWrite: false
|
|
1784
|
+
})
|
|
1785
|
+
);
|
|
1786
|
+
const edges = new EdgesGeometry(initialGeometry);
|
|
1787
|
+
this.edgeLines = new LineSegments(
|
|
1788
|
+
edges,
|
|
1789
|
+
new LineBasicMaterial({ color: this.currentColor, linewidth: 1 })
|
|
1790
|
+
);
|
|
1791
|
+
this.container = new Group4();
|
|
1792
|
+
this.container.name = "DebugEntityCursor";
|
|
1793
|
+
this.container.add(this.fillMesh);
|
|
1794
|
+
this.container.add(this.edgeLines);
|
|
1795
|
+
this.container.visible = false;
|
|
1796
|
+
this.scene.add(this.container);
|
|
1797
|
+
}
|
|
1798
|
+
setColor(color) {
|
|
1799
|
+
this.currentColor.set(color);
|
|
1800
|
+
this.fillMesh.material.color.set(this.currentColor);
|
|
1801
|
+
this.edgeLines.material.color.set(this.currentColor);
|
|
1600
1802
|
}
|
|
1601
1803
|
/**
|
|
1602
|
-
* Update the
|
|
1804
|
+
* Update the cursor to enclose the provided Object3D using a world-space AABB.
|
|
1603
1805
|
*/
|
|
1604
|
-
|
|
1605
|
-
if (!
|
|
1806
|
+
updateFromObject(object) {
|
|
1807
|
+
if (!object) {
|
|
1808
|
+
this.hide();
|
|
1606
1809
|
return;
|
|
1607
1810
|
}
|
|
1608
|
-
|
|
1609
|
-
this.
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1811
|
+
this.bbox.setFromObject(object);
|
|
1812
|
+
if (!isFinite(this.bbox.min.x) || !isFinite(this.bbox.max.x)) {
|
|
1813
|
+
this.hide();
|
|
1814
|
+
return;
|
|
1815
|
+
}
|
|
1816
|
+
this.bbox.getSize(this.size);
|
|
1817
|
+
this.bbox.getCenter(this.center);
|
|
1818
|
+
const newGeom = new BoxGeometry(
|
|
1819
|
+
Math.max(this.size.x, 1e-6),
|
|
1820
|
+
Math.max(this.size.y, 1e-6),
|
|
1821
|
+
Math.max(this.size.z, 1e-6)
|
|
1822
|
+
);
|
|
1823
|
+
this.fillMesh.geometry.dispose();
|
|
1824
|
+
this.fillMesh.geometry = newGeom;
|
|
1825
|
+
const newEdges = new EdgesGeometry(newGeom);
|
|
1826
|
+
this.edgeLines.geometry.dispose();
|
|
1827
|
+
this.edgeLines.geometry = newEdges;
|
|
1828
|
+
this.container.position.copy(this.center);
|
|
1829
|
+
this.container.visible = true;
|
|
1830
|
+
}
|
|
1831
|
+
hide() {
|
|
1832
|
+
this.container.visible = false;
|
|
1833
|
+
}
|
|
1834
|
+
dispose() {
|
|
1835
|
+
this.scene.remove(this.container);
|
|
1836
|
+
this.fillMesh.geometry.dispose();
|
|
1837
|
+
this.fillMesh.material.dispose();
|
|
1838
|
+
this.edgeLines.geometry.dispose();
|
|
1839
|
+
this.edgeLines.material.dispose();
|
|
1840
|
+
}
|
|
1841
|
+
};
|
|
1842
|
+
}
|
|
1843
|
+
});
|
|
1844
|
+
|
|
1845
|
+
// src/lib/stage/stage-debug-delegate.ts
|
|
1846
|
+
import { Ray } from "@dimforge/rapier3d-compat";
|
|
1847
|
+
import { BufferAttribute, BufferGeometry as BufferGeometry3, LineBasicMaterial as LineBasicMaterial2, LineSegments as LineSegments2, Raycaster, Vector2 as Vector22 } from "three";
|
|
1848
|
+
var SELECT_TOOL_COLOR, DELETE_TOOL_COLOR, StageDebugDelegate;
|
|
1849
|
+
var init_stage_debug_delegate = __esm({
|
|
1850
|
+
"src/lib/stage/stage-debug-delegate.ts"() {
|
|
1851
|
+
"use strict";
|
|
1852
|
+
init_debug_state();
|
|
1853
|
+
init_debug_entity_cursor();
|
|
1854
|
+
SELECT_TOOL_COLOR = 2293538;
|
|
1855
|
+
DELETE_TOOL_COLOR = 16724787;
|
|
1856
|
+
StageDebugDelegate = class {
|
|
1857
|
+
stage;
|
|
1858
|
+
options;
|
|
1859
|
+
mouseNdc = new Vector22(-2, -2);
|
|
1860
|
+
raycaster = new Raycaster();
|
|
1861
|
+
isMouseDown = false;
|
|
1862
|
+
disposeFns = [];
|
|
1863
|
+
debugCursor = null;
|
|
1864
|
+
debugLines = null;
|
|
1865
|
+
constructor(stage, options) {
|
|
1866
|
+
this.stage = stage;
|
|
1867
|
+
this.options = {
|
|
1868
|
+
maxRayDistance: options?.maxRayDistance ?? 5e3,
|
|
1869
|
+
addEntityFactory: options?.addEntityFactory ?? null
|
|
1870
|
+
};
|
|
1871
|
+
if (this.stage.scene) {
|
|
1872
|
+
this.debugLines = new LineSegments2(
|
|
1873
|
+
new BufferGeometry3(),
|
|
1874
|
+
new LineBasicMaterial2({ vertexColors: true })
|
|
1875
|
+
);
|
|
1876
|
+
this.stage.scene.scene.add(this.debugLines);
|
|
1877
|
+
this.debugLines.visible = true;
|
|
1878
|
+
this.debugCursor = new DebugEntityCursor(this.stage.scene.scene);
|
|
1879
|
+
}
|
|
1880
|
+
this.attachDomListeners();
|
|
1881
|
+
}
|
|
1882
|
+
update() {
|
|
1883
|
+
if (!debugState.enabled) return;
|
|
1884
|
+
if (!this.stage.scene || !this.stage.world || !this.stage.cameraRef) return;
|
|
1885
|
+
const { world, cameraRef } = this.stage;
|
|
1886
|
+
if (this.debugLines) {
|
|
1887
|
+
const { vertices, colors } = world.world.debugRender();
|
|
1888
|
+
this.debugLines.geometry.setAttribute("position", new BufferAttribute(vertices, 3));
|
|
1889
|
+
this.debugLines.geometry.setAttribute("color", new BufferAttribute(colors, 4));
|
|
1890
|
+
}
|
|
1891
|
+
const tool = getDebugTool();
|
|
1892
|
+
const isCursorTool = tool === "select" || tool === "delete";
|
|
1893
|
+
this.raycaster.setFromCamera(this.mouseNdc, cameraRef.camera);
|
|
1894
|
+
const origin = this.raycaster.ray.origin.clone();
|
|
1895
|
+
const direction = this.raycaster.ray.direction.clone().normalize();
|
|
1896
|
+
const rapierRay = new Ray(
|
|
1897
|
+
{ x: origin.x, y: origin.y, z: origin.z },
|
|
1898
|
+
{ x: direction.x, y: direction.y, z: direction.z }
|
|
1899
|
+
);
|
|
1900
|
+
const hit = world.world.castRay(rapierRay, this.options.maxRayDistance, true);
|
|
1901
|
+
if (hit && isCursorTool) {
|
|
1902
|
+
const rigidBody = hit.collider?._parent;
|
|
1903
|
+
const hoveredUuid2 = rigidBody?.userData?.uuid;
|
|
1904
|
+
if (hoveredUuid2) {
|
|
1905
|
+
const entity = this.stage._debugMap.get(hoveredUuid2);
|
|
1906
|
+
if (entity) setHoveredEntity(entity);
|
|
1907
|
+
} else {
|
|
1908
|
+
resetHoveredEntity();
|
|
1909
|
+
}
|
|
1910
|
+
if (this.isMouseDown) {
|
|
1911
|
+
this.handleActionOnHit(hoveredUuid2 ?? null, origin, direction, hit.toi);
|
|
1912
|
+
}
|
|
1913
|
+
}
|
|
1914
|
+
this.isMouseDown = false;
|
|
1915
|
+
const hoveredUuid = getHoveredEntity();
|
|
1916
|
+
if (!hoveredUuid) {
|
|
1917
|
+
this.debugCursor?.hide();
|
|
1918
|
+
return;
|
|
1919
|
+
}
|
|
1920
|
+
const hoveredEntity = this.stage._debugMap.get(`${hoveredUuid}`);
|
|
1921
|
+
const targetObject = hoveredEntity?.group ?? hoveredEntity?.mesh ?? null;
|
|
1922
|
+
if (!targetObject) {
|
|
1923
|
+
this.debugCursor?.hide();
|
|
1924
|
+
return;
|
|
1925
|
+
}
|
|
1926
|
+
switch (tool) {
|
|
1927
|
+
case "select":
|
|
1928
|
+
this.debugCursor?.setColor(SELECT_TOOL_COLOR);
|
|
1929
|
+
break;
|
|
1930
|
+
case "delete":
|
|
1931
|
+
this.debugCursor?.setColor(DELETE_TOOL_COLOR);
|
|
1932
|
+
break;
|
|
1933
|
+
default:
|
|
1934
|
+
this.debugCursor?.setColor(16777215);
|
|
1935
|
+
break;
|
|
1936
|
+
}
|
|
1937
|
+
this.debugCursor?.updateFromObject(targetObject);
|
|
1938
|
+
}
|
|
1939
|
+
dispose() {
|
|
1940
|
+
this.disposeFns.forEach((fn) => fn());
|
|
1941
|
+
this.disposeFns = [];
|
|
1942
|
+
this.debugCursor?.dispose();
|
|
1943
|
+
if (this.debugLines && this.stage.scene) {
|
|
1944
|
+
this.stage.scene.scene.remove(this.debugLines);
|
|
1945
|
+
this.debugLines.geometry.dispose();
|
|
1946
|
+
this.debugLines.material.dispose();
|
|
1947
|
+
this.debugLines = null;
|
|
1948
|
+
}
|
|
1949
|
+
}
|
|
1950
|
+
handleActionOnHit(hoveredUuid, origin, direction, toi) {
|
|
1951
|
+
const tool = getDebugTool();
|
|
1952
|
+
switch (tool) {
|
|
1953
|
+
case "select": {
|
|
1954
|
+
if (hoveredUuid) {
|
|
1955
|
+
const entity = this.stage._debugMap.get(hoveredUuid);
|
|
1956
|
+
if (entity) setSelectedEntity(entity);
|
|
1957
|
+
}
|
|
1958
|
+
break;
|
|
1959
|
+
}
|
|
1960
|
+
case "delete": {
|
|
1961
|
+
if (hoveredUuid) {
|
|
1962
|
+
this.stage.removeEntityByUuid(hoveredUuid);
|
|
1963
|
+
}
|
|
1964
|
+
break;
|
|
1965
|
+
}
|
|
1966
|
+
case "scale": {
|
|
1967
|
+
if (!this.options.addEntityFactory) break;
|
|
1968
|
+
const hitPosition = origin.clone().add(direction.clone().multiplyScalar(toi));
|
|
1969
|
+
const newNode = this.options.addEntityFactory({ position: hitPosition });
|
|
1970
|
+
if (newNode) {
|
|
1971
|
+
Promise.resolve(newNode).then((node) => {
|
|
1972
|
+
if (node) this.stage.spawnEntity(node);
|
|
1973
|
+
}).catch(() => {
|
|
1974
|
+
});
|
|
1975
|
+
}
|
|
1976
|
+
break;
|
|
1977
|
+
}
|
|
1978
|
+
default:
|
|
1979
|
+
break;
|
|
1980
|
+
}
|
|
1981
|
+
}
|
|
1982
|
+
attachDomListeners() {
|
|
1983
|
+
const canvas = this.stage.cameraRef?.renderer.domElement ?? this.stage.scene?.zylemCamera.renderer.domElement;
|
|
1984
|
+
if (!canvas) return;
|
|
1985
|
+
const onMouseMove = (e) => {
|
|
1986
|
+
const rect = canvas.getBoundingClientRect();
|
|
1987
|
+
const x = (e.clientX - rect.left) / rect.width * 2 - 1;
|
|
1988
|
+
const y = -((e.clientY - rect.top) / rect.height * 2 - 1);
|
|
1989
|
+
this.mouseNdc.set(x, y);
|
|
1990
|
+
};
|
|
1991
|
+
const onMouseDown = (e) => {
|
|
1992
|
+
this.isMouseDown = true;
|
|
1993
|
+
};
|
|
1994
|
+
canvas.addEventListener("mousemove", onMouseMove);
|
|
1995
|
+
canvas.addEventListener("mousedown", onMouseDown);
|
|
1996
|
+
this.disposeFns.push(() => canvas.removeEventListener("mousemove", onMouseMove));
|
|
1997
|
+
this.disposeFns.push(() => canvas.removeEventListener("mousedown", onMouseDown));
|
|
1998
|
+
}
|
|
1999
|
+
};
|
|
2000
|
+
}
|
|
2001
|
+
});
|
|
2002
|
+
|
|
2003
|
+
// src/lib/stage/stage-camera-debug-delegate.ts
|
|
2004
|
+
import { subscribe as subscribe4 } from "valtio/vanilla";
|
|
2005
|
+
var StageCameraDebugDelegate;
|
|
2006
|
+
var init_stage_camera_debug_delegate = __esm({
|
|
2007
|
+
"src/lib/stage/stage-camera-debug-delegate.ts"() {
|
|
2008
|
+
"use strict";
|
|
2009
|
+
init_debug_state();
|
|
2010
|
+
StageCameraDebugDelegate = class {
|
|
2011
|
+
stage;
|
|
2012
|
+
constructor(stage) {
|
|
2013
|
+
this.stage = stage;
|
|
2014
|
+
}
|
|
2015
|
+
subscribe(listener) {
|
|
2016
|
+
const notify = () => listener(this.snapshot());
|
|
2017
|
+
notify();
|
|
2018
|
+
return subscribe4(debugState, notify);
|
|
2019
|
+
}
|
|
2020
|
+
resolveTarget(uuid) {
|
|
2021
|
+
const entity = this.stage._debugMap.get(uuid) || this.stage.world?.collisionMap.get(uuid) || null;
|
|
2022
|
+
const target = entity?.group ?? entity?.mesh ?? null;
|
|
2023
|
+
return target ?? null;
|
|
2024
|
+
}
|
|
2025
|
+
snapshot() {
|
|
2026
|
+
return {
|
|
2027
|
+
enabled: debugState.enabled,
|
|
2028
|
+
selected: debugState.selectedEntity ? [debugState.selectedEntity.uuid] : []
|
|
2029
|
+
};
|
|
2030
|
+
}
|
|
2031
|
+
};
|
|
2032
|
+
}
|
|
2033
|
+
});
|
|
2034
|
+
|
|
2035
|
+
// src/lib/camera/perspective.ts
|
|
2036
|
+
var Perspectives;
|
|
2037
|
+
var init_perspective = __esm({
|
|
2038
|
+
"src/lib/camera/perspective.ts"() {
|
|
2039
|
+
"use strict";
|
|
2040
|
+
Perspectives = {
|
|
2041
|
+
FirstPerson: "first-person",
|
|
2042
|
+
ThirdPerson: "third-person",
|
|
2043
|
+
Isometric: "isometric",
|
|
2044
|
+
Flat2D: "flat-2d",
|
|
2045
|
+
Fixed2D: "fixed-2d"
|
|
2046
|
+
};
|
|
2047
|
+
}
|
|
2048
|
+
});
|
|
2049
|
+
|
|
2050
|
+
// src/lib/camera/third-person.ts
|
|
2051
|
+
import { Vector3 as Vector39 } from "three";
|
|
2052
|
+
var ThirdPersonCamera;
|
|
2053
|
+
var init_third_person = __esm({
|
|
2054
|
+
"src/lib/camera/third-person.ts"() {
|
|
2055
|
+
"use strict";
|
|
2056
|
+
ThirdPersonCamera = class {
|
|
2057
|
+
distance;
|
|
2058
|
+
screenResolution = null;
|
|
2059
|
+
renderer = null;
|
|
2060
|
+
scene = null;
|
|
2061
|
+
cameraRef = null;
|
|
2062
|
+
constructor() {
|
|
2063
|
+
this.distance = new Vector39(0, 5, 8);
|
|
2064
|
+
}
|
|
2065
|
+
/**
|
|
2066
|
+
* Setup the third person camera controller
|
|
2067
|
+
*/
|
|
2068
|
+
setup(params) {
|
|
2069
|
+
const { screenResolution, renderer, scene, camera } = params;
|
|
2070
|
+
this.screenResolution = screenResolution;
|
|
2071
|
+
this.renderer = renderer;
|
|
2072
|
+
this.scene = scene;
|
|
2073
|
+
this.cameraRef = camera;
|
|
2074
|
+
}
|
|
2075
|
+
/**
|
|
2076
|
+
* Update the third person camera
|
|
2077
|
+
*/
|
|
2078
|
+
update(delta) {
|
|
2079
|
+
if (!this.cameraRef.target) {
|
|
2080
|
+
return;
|
|
2081
|
+
}
|
|
2082
|
+
const desiredCameraPosition = this.cameraRef.target.group.position.clone().add(this.distance);
|
|
2083
|
+
this.cameraRef.camera.position.lerp(desiredCameraPosition, 0.1);
|
|
2084
|
+
this.cameraRef.camera.lookAt(this.cameraRef.target.group.position);
|
|
2085
|
+
}
|
|
2086
|
+
/**
|
|
2087
|
+
* Handle resize events
|
|
1614
2088
|
*/
|
|
1615
2089
|
resize(width, height) {
|
|
1616
2090
|
if (this.screenResolution) {
|
|
@@ -1765,9 +2239,184 @@ var init_render_pass = __esm({
|
|
|
1765
2239
|
}
|
|
1766
2240
|
});
|
|
1767
2241
|
|
|
1768
|
-
// src/lib/camera/
|
|
1769
|
-
import {
|
|
2242
|
+
// src/lib/camera/camera-debug-delegate.ts
|
|
2243
|
+
import { Vector3 as Vector310 } from "three";
|
|
1770
2244
|
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
|
|
2245
|
+
var CameraOrbitController;
|
|
2246
|
+
var init_camera_debug_delegate = __esm({
|
|
2247
|
+
"src/lib/camera/camera-debug-delegate.ts"() {
|
|
2248
|
+
"use strict";
|
|
2249
|
+
CameraOrbitController = class {
|
|
2250
|
+
camera;
|
|
2251
|
+
domElement;
|
|
2252
|
+
orbitControls = null;
|
|
2253
|
+
orbitTarget = null;
|
|
2254
|
+
orbitTargetWorldPos = new Vector310();
|
|
2255
|
+
debugDelegate = null;
|
|
2256
|
+
debugUnsubscribe = null;
|
|
2257
|
+
debugStateSnapshot = { enabled: false, selected: [] };
|
|
2258
|
+
// Saved camera state for restoration when exiting debug mode
|
|
2259
|
+
savedCameraPosition = null;
|
|
2260
|
+
savedCameraQuaternion = null;
|
|
2261
|
+
savedCameraZoom = null;
|
|
2262
|
+
constructor(camera, domElement) {
|
|
2263
|
+
this.camera = camera;
|
|
2264
|
+
this.domElement = domElement;
|
|
2265
|
+
}
|
|
2266
|
+
/**
|
|
2267
|
+
* Check if debug mode is currently active (orbit controls enabled).
|
|
2268
|
+
*/
|
|
2269
|
+
get isActive() {
|
|
2270
|
+
return this.debugStateSnapshot.enabled;
|
|
2271
|
+
}
|
|
2272
|
+
/**
|
|
2273
|
+
* Update orbit controls each frame.
|
|
2274
|
+
* Should be called from the camera's update loop.
|
|
2275
|
+
*/
|
|
2276
|
+
update() {
|
|
2277
|
+
if (this.orbitControls && this.orbitTarget) {
|
|
2278
|
+
this.orbitTarget.getWorldPosition(this.orbitTargetWorldPos);
|
|
2279
|
+
this.orbitControls.target.copy(this.orbitTargetWorldPos);
|
|
2280
|
+
}
|
|
2281
|
+
this.orbitControls?.update();
|
|
2282
|
+
}
|
|
2283
|
+
/**
|
|
2284
|
+
* Attach a delegate to react to debug state changes.
|
|
2285
|
+
*/
|
|
2286
|
+
setDebugDelegate(delegate) {
|
|
2287
|
+
if (this.debugDelegate === delegate) {
|
|
2288
|
+
return;
|
|
2289
|
+
}
|
|
2290
|
+
this.detachDebugDelegate();
|
|
2291
|
+
this.debugDelegate = delegate;
|
|
2292
|
+
if (!delegate) {
|
|
2293
|
+
this.applyDebugState({ enabled: false, selected: [] });
|
|
2294
|
+
return;
|
|
2295
|
+
}
|
|
2296
|
+
const unsubscribe = delegate.subscribe((state2) => {
|
|
2297
|
+
this.applyDebugState(state2);
|
|
2298
|
+
});
|
|
2299
|
+
this.debugUnsubscribe = () => {
|
|
2300
|
+
unsubscribe?.();
|
|
2301
|
+
};
|
|
2302
|
+
}
|
|
2303
|
+
/**
|
|
2304
|
+
* Clean up resources.
|
|
2305
|
+
*/
|
|
2306
|
+
dispose() {
|
|
2307
|
+
this.disableOrbitControls();
|
|
2308
|
+
this.detachDebugDelegate();
|
|
2309
|
+
}
|
|
2310
|
+
/**
|
|
2311
|
+
* Get the current debug state snapshot.
|
|
2312
|
+
*/
|
|
2313
|
+
get debugState() {
|
|
2314
|
+
return this.debugStateSnapshot;
|
|
2315
|
+
}
|
|
2316
|
+
applyDebugState(state2) {
|
|
2317
|
+
const wasEnabled = this.debugStateSnapshot.enabled;
|
|
2318
|
+
this.debugStateSnapshot = {
|
|
2319
|
+
enabled: state2.enabled,
|
|
2320
|
+
selected: [...state2.selected]
|
|
2321
|
+
};
|
|
2322
|
+
if (state2.enabled && !wasEnabled) {
|
|
2323
|
+
this.saveCameraState();
|
|
2324
|
+
this.enableOrbitControls();
|
|
2325
|
+
this.updateOrbitTargetFromSelection(state2.selected);
|
|
2326
|
+
} else if (!state2.enabled && wasEnabled) {
|
|
2327
|
+
this.orbitTarget = null;
|
|
2328
|
+
this.disableOrbitControls();
|
|
2329
|
+
this.restoreCameraState();
|
|
2330
|
+
} else if (state2.enabled) {
|
|
2331
|
+
this.updateOrbitTargetFromSelection(state2.selected);
|
|
2332
|
+
}
|
|
2333
|
+
}
|
|
2334
|
+
enableOrbitControls() {
|
|
2335
|
+
if (this.orbitControls) {
|
|
2336
|
+
return;
|
|
2337
|
+
}
|
|
2338
|
+
this.orbitControls = new OrbitControls(this.camera, this.domElement);
|
|
2339
|
+
this.orbitControls.enableDamping = true;
|
|
2340
|
+
this.orbitControls.dampingFactor = 0.05;
|
|
2341
|
+
this.orbitControls.screenSpacePanning = false;
|
|
2342
|
+
this.orbitControls.minDistance = 1;
|
|
2343
|
+
this.orbitControls.maxDistance = 500;
|
|
2344
|
+
this.orbitControls.maxPolarAngle = Math.PI / 2;
|
|
2345
|
+
this.orbitControls.target.set(0, 0, 0);
|
|
2346
|
+
}
|
|
2347
|
+
disableOrbitControls() {
|
|
2348
|
+
if (!this.orbitControls) {
|
|
2349
|
+
return;
|
|
2350
|
+
}
|
|
2351
|
+
this.orbitControls.dispose();
|
|
2352
|
+
this.orbitControls = null;
|
|
2353
|
+
}
|
|
2354
|
+
updateOrbitTargetFromSelection(selected) {
|
|
2355
|
+
if (!this.debugDelegate || selected.length === 0) {
|
|
2356
|
+
this.orbitTarget = null;
|
|
2357
|
+
if (this.orbitControls) {
|
|
2358
|
+
this.orbitControls.target.set(0, 0, 0);
|
|
2359
|
+
}
|
|
2360
|
+
return;
|
|
2361
|
+
}
|
|
2362
|
+
for (let i = selected.length - 1; i >= 0; i -= 1) {
|
|
2363
|
+
const uuid = selected[i];
|
|
2364
|
+
const targetObject = this.debugDelegate.resolveTarget(uuid);
|
|
2365
|
+
if (targetObject) {
|
|
2366
|
+
this.orbitTarget = targetObject;
|
|
2367
|
+
if (this.orbitControls) {
|
|
2368
|
+
targetObject.getWorldPosition(this.orbitTargetWorldPos);
|
|
2369
|
+
this.orbitControls.target.copy(this.orbitTargetWorldPos);
|
|
2370
|
+
}
|
|
2371
|
+
return;
|
|
2372
|
+
}
|
|
2373
|
+
}
|
|
2374
|
+
this.orbitTarget = null;
|
|
2375
|
+
}
|
|
2376
|
+
detachDebugDelegate() {
|
|
2377
|
+
if (this.debugUnsubscribe) {
|
|
2378
|
+
try {
|
|
2379
|
+
this.debugUnsubscribe();
|
|
2380
|
+
} catch {
|
|
2381
|
+
}
|
|
2382
|
+
}
|
|
2383
|
+
this.debugUnsubscribe = null;
|
|
2384
|
+
this.debugDelegate = null;
|
|
2385
|
+
}
|
|
2386
|
+
/**
|
|
2387
|
+
* Save camera position, rotation, and zoom before entering debug mode.
|
|
2388
|
+
*/
|
|
2389
|
+
saveCameraState() {
|
|
2390
|
+
this.savedCameraPosition = this.camera.position.clone();
|
|
2391
|
+
this.savedCameraQuaternion = this.camera.quaternion.clone();
|
|
2392
|
+
if ("zoom" in this.camera) {
|
|
2393
|
+
this.savedCameraZoom = this.camera.zoom;
|
|
2394
|
+
}
|
|
2395
|
+
}
|
|
2396
|
+
/**
|
|
2397
|
+
* Restore camera position, rotation, and zoom when exiting debug mode.
|
|
2398
|
+
*/
|
|
2399
|
+
restoreCameraState() {
|
|
2400
|
+
if (this.savedCameraPosition) {
|
|
2401
|
+
this.camera.position.copy(this.savedCameraPosition);
|
|
2402
|
+
this.savedCameraPosition = null;
|
|
2403
|
+
}
|
|
2404
|
+
if (this.savedCameraQuaternion) {
|
|
2405
|
+
this.camera.quaternion.copy(this.savedCameraQuaternion);
|
|
2406
|
+
this.savedCameraQuaternion = null;
|
|
2407
|
+
}
|
|
2408
|
+
if (this.savedCameraZoom !== null && "zoom" in this.camera) {
|
|
2409
|
+
this.camera.zoom = this.savedCameraZoom;
|
|
2410
|
+
this.camera.updateProjectionMatrix?.();
|
|
2411
|
+
this.savedCameraZoom = null;
|
|
2412
|
+
}
|
|
2413
|
+
}
|
|
2414
|
+
};
|
|
2415
|
+
}
|
|
2416
|
+
});
|
|
2417
|
+
|
|
2418
|
+
// src/lib/camera/zylem-camera.ts
|
|
2419
|
+
import { PerspectiveCamera, Vector3 as Vector311, Object3D as Object3D6, OrthographicCamera, WebGLRenderer as WebGLRenderer3 } from "three";
|
|
1771
2420
|
import { EffectComposer } from "three/examples/jsm/postprocessing/EffectComposer.js";
|
|
1772
2421
|
var ZylemCamera;
|
|
1773
2422
|
var init_zylem_camera = __esm({
|
|
@@ -1777,24 +2426,21 @@ var init_zylem_camera = __esm({
|
|
|
1777
2426
|
init_third_person();
|
|
1778
2427
|
init_fixed_2d();
|
|
1779
2428
|
init_render_pass();
|
|
2429
|
+
init_camera_debug_delegate();
|
|
1780
2430
|
ZylemCamera = class {
|
|
1781
|
-
cameraRig;
|
|
2431
|
+
cameraRig = null;
|
|
1782
2432
|
camera;
|
|
1783
2433
|
screenResolution;
|
|
1784
2434
|
renderer;
|
|
1785
2435
|
composer;
|
|
1786
2436
|
_perspective;
|
|
1787
|
-
orbitControls = null;
|
|
1788
2437
|
target = null;
|
|
1789
2438
|
sceneRef = null;
|
|
1790
2439
|
frustumSize = 10;
|
|
1791
2440
|
// Perspective controller delegation
|
|
1792
2441
|
perspectiveController = null;
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
debugStateSnapshot = { enabled: false, selected: [] };
|
|
1796
|
-
orbitTarget = null;
|
|
1797
|
-
orbitTargetWorldPos = new Vector38();
|
|
2442
|
+
// Debug/orbit controls delegation
|
|
2443
|
+
orbitController = null;
|
|
1798
2444
|
constructor(perspective, screenResolution, frustumSize = 10) {
|
|
1799
2445
|
this._perspective = perspective;
|
|
1800
2446
|
this.screenResolution = screenResolution;
|
|
@@ -1805,26 +2451,23 @@ var init_zylem_camera = __esm({
|
|
|
1805
2451
|
this.composer = new EffectComposer(this.renderer);
|
|
1806
2452
|
const aspectRatio = screenResolution.x / screenResolution.y;
|
|
1807
2453
|
this.camera = this.createCameraForPerspective(aspectRatio);
|
|
1808
|
-
this.
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
2454
|
+
if (this.needsRig()) {
|
|
2455
|
+
this.cameraRig = new Object3D6();
|
|
2456
|
+
this.cameraRig.position.set(0, 3, 10);
|
|
2457
|
+
this.cameraRig.add(this.camera);
|
|
2458
|
+
this.camera.lookAt(new Vector311(0, 2, 0));
|
|
2459
|
+
} else {
|
|
2460
|
+
this.camera.position.set(0, 0, 10);
|
|
2461
|
+
this.camera.lookAt(new Vector311(0, 0, 0));
|
|
2462
|
+
}
|
|
1812
2463
|
this.initializePerspectiveController();
|
|
2464
|
+
this.orbitController = new CameraOrbitController(this.camera, this.renderer.domElement);
|
|
1813
2465
|
}
|
|
1814
2466
|
/**
|
|
1815
2467
|
* Setup the camera with a scene
|
|
1816
2468
|
*/
|
|
1817
2469
|
async setup(scene) {
|
|
1818
2470
|
this.sceneRef = scene;
|
|
1819
|
-
if (this.orbitControls === null) {
|
|
1820
|
-
this.orbitControls = new OrbitControls(this.camera, this.renderer.domElement);
|
|
1821
|
-
this.orbitControls.enableDamping = true;
|
|
1822
|
-
this.orbitControls.dampingFactor = 0.05;
|
|
1823
|
-
this.orbitControls.screenSpacePanning = false;
|
|
1824
|
-
this.orbitControls.minDistance = 1;
|
|
1825
|
-
this.orbitControls.maxDistance = 500;
|
|
1826
|
-
this.orbitControls.maxPolarAngle = Math.PI / 2;
|
|
1827
|
-
}
|
|
1828
2471
|
let renderResolution = this.screenResolution.clone().divideScalar(2);
|
|
1829
2472
|
renderResolution.x |= 0;
|
|
1830
2473
|
renderResolution.y |= 0;
|
|
@@ -1846,16 +2489,18 @@ var init_zylem_camera = __esm({
|
|
|
1846
2489
|
* Update camera and render
|
|
1847
2490
|
*/
|
|
1848
2491
|
update(delta) {
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
this.orbitControls.target.copy(this.orbitTargetWorldPos);
|
|
1852
|
-
}
|
|
1853
|
-
this.orbitControls?.update();
|
|
1854
|
-
if (this.perspectiveController) {
|
|
2492
|
+
this.orbitController?.update();
|
|
2493
|
+
if (this.perspectiveController && !this.isDebugModeActive()) {
|
|
1855
2494
|
this.perspectiveController.update(delta);
|
|
1856
2495
|
}
|
|
1857
2496
|
this.composer.render(delta);
|
|
1858
2497
|
}
|
|
2498
|
+
/**
|
|
2499
|
+
* Check if debug mode is active (orbit controls taking over camera)
|
|
2500
|
+
*/
|
|
2501
|
+
isDebugModeActive() {
|
|
2502
|
+
return this.orbitController?.isActive ?? false;
|
|
2503
|
+
}
|
|
1859
2504
|
/**
|
|
1860
2505
|
* Dispose renderer, composer, controls, and detach from scene
|
|
1861
2506
|
*/
|
|
@@ -1865,7 +2510,7 @@ var init_zylem_camera = __esm({
|
|
|
1865
2510
|
} catch {
|
|
1866
2511
|
}
|
|
1867
2512
|
try {
|
|
1868
|
-
this.
|
|
2513
|
+
this.orbitController?.dispose();
|
|
1869
2514
|
} catch {
|
|
1870
2515
|
}
|
|
1871
2516
|
try {
|
|
@@ -1877,28 +2522,13 @@ var init_zylem_camera = __esm({
|
|
|
1877
2522
|
this.renderer.dispose();
|
|
1878
2523
|
} catch {
|
|
1879
2524
|
}
|
|
1880
|
-
this.detachDebugDelegate();
|
|
1881
2525
|
this.sceneRef = null;
|
|
1882
2526
|
}
|
|
1883
2527
|
/**
|
|
1884
2528
|
* Attach a delegate to react to debug state changes.
|
|
1885
2529
|
*/
|
|
1886
2530
|
setDebugDelegate(delegate) {
|
|
1887
|
-
|
|
1888
|
-
return;
|
|
1889
|
-
}
|
|
1890
|
-
this.detachDebugDelegate();
|
|
1891
|
-
this.debugDelegate = delegate;
|
|
1892
|
-
if (!delegate) {
|
|
1893
|
-
this.applyDebugState({ enabled: false, selected: [] });
|
|
1894
|
-
return;
|
|
1895
|
-
}
|
|
1896
|
-
const unsubscribe = delegate.subscribe((state2) => {
|
|
1897
|
-
this.applyDebugState(state2);
|
|
1898
|
-
});
|
|
1899
|
-
this.debugUnsubscribe = () => {
|
|
1900
|
-
unsubscribe?.();
|
|
1901
|
-
};
|
|
2531
|
+
this.orbitController?.setDebugDelegate(delegate);
|
|
1902
2532
|
}
|
|
1903
2533
|
/**
|
|
1904
2534
|
* Resize camera and renderer
|
|
@@ -1990,15 +2620,31 @@ var init_zylem_camera = __esm({
|
|
|
1990
2620
|
if (this._perspective === Perspectives.Flat2D || this._perspective === Perspectives.Fixed2D) {
|
|
1991
2621
|
this.frustumSize = position2.z;
|
|
1992
2622
|
}
|
|
1993
|
-
this.cameraRig
|
|
2623
|
+
if (this.cameraRig) {
|
|
2624
|
+
this.cameraRig.position.set(position2.x, position2.y, position2.z);
|
|
2625
|
+
} else {
|
|
2626
|
+
this.camera.position.set(position2.x, position2.y, position2.z);
|
|
2627
|
+
}
|
|
1994
2628
|
}
|
|
1995
2629
|
move(position2) {
|
|
1996
2630
|
this.moveCamera(position2);
|
|
1997
2631
|
}
|
|
1998
2632
|
rotate(pitch, yaw, roll) {
|
|
1999
|
-
this.cameraRig
|
|
2000
|
-
|
|
2001
|
-
|
|
2633
|
+
if (this.cameraRig) {
|
|
2634
|
+
this.cameraRig.rotateX(pitch);
|
|
2635
|
+
this.cameraRig.rotateY(yaw);
|
|
2636
|
+
this.cameraRig.rotateZ(roll);
|
|
2637
|
+
} else {
|
|
2638
|
+
this.camera.rotateX(pitch);
|
|
2639
|
+
this.camera.rotateY(yaw);
|
|
2640
|
+
this.camera.rotateZ(roll);
|
|
2641
|
+
}
|
|
2642
|
+
}
|
|
2643
|
+
/**
|
|
2644
|
+
* Check if this perspective type needs a camera rig
|
|
2645
|
+
*/
|
|
2646
|
+
needsRig() {
|
|
2647
|
+
return this._perspective === Perspectives.ThirdPerson;
|
|
2002
2648
|
}
|
|
2003
2649
|
/**
|
|
2004
2650
|
* Get the DOM element for the renderer
|
|
@@ -2006,336 +2652,221 @@ var init_zylem_camera = __esm({
|
|
|
2006
2652
|
getDomElement() {
|
|
2007
2653
|
return this.renderer.domElement;
|
|
2008
2654
|
}
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
}
|
|
2026
|
-
this.orbitControls = new OrbitControls(this.camera, this.renderer.domElement);
|
|
2027
|
-
this.orbitControls.enableDamping = true;
|
|
2028
|
-
this.orbitControls.dampingFactor = 0.05;
|
|
2029
|
-
this.orbitControls.screenSpacePanning = false;
|
|
2030
|
-
this.orbitControls.minDistance = 1;
|
|
2031
|
-
this.orbitControls.maxDistance = 500;
|
|
2032
|
-
this.orbitControls.maxPolarAngle = Math.PI / 2;
|
|
2655
|
+
};
|
|
2656
|
+
}
|
|
2657
|
+
});
|
|
2658
|
+
|
|
2659
|
+
// src/lib/stage/stage-camera-delegate.ts
|
|
2660
|
+
import { Vector2 as Vector25 } from "three";
|
|
2661
|
+
var StageCameraDelegate;
|
|
2662
|
+
var init_stage_camera_delegate = __esm({
|
|
2663
|
+
"src/lib/stage/stage-camera-delegate.ts"() {
|
|
2664
|
+
"use strict";
|
|
2665
|
+
init_zylem_camera();
|
|
2666
|
+
init_perspective();
|
|
2667
|
+
StageCameraDelegate = class {
|
|
2668
|
+
stage;
|
|
2669
|
+
constructor(stage) {
|
|
2670
|
+
this.stage = stage;
|
|
2033
2671
|
}
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2672
|
+
/**
|
|
2673
|
+
* Create a default third-person camera based on window size.
|
|
2674
|
+
*/
|
|
2675
|
+
createDefaultCamera() {
|
|
2676
|
+
const width = window.innerWidth;
|
|
2677
|
+
const height = window.innerHeight;
|
|
2678
|
+
const screenResolution = new Vector25(width, height);
|
|
2679
|
+
return new ZylemCamera(Perspectives.ThirdPerson, screenResolution);
|
|
2040
2680
|
}
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
targetObject.getWorldPosition(this.orbitTargetWorldPos);
|
|
2053
|
-
this.orbitControls.target.copy(this.orbitTargetWorldPos);
|
|
2054
|
-
}
|
|
2055
|
-
return;
|
|
2056
|
-
}
|
|
2681
|
+
/**
|
|
2682
|
+
* Resolve the camera to use for the stage.
|
|
2683
|
+
* Uses the provided camera, stage camera wrapper, or creates a default.
|
|
2684
|
+
*
|
|
2685
|
+
* @param cameraOverride Optional camera override
|
|
2686
|
+
* @param cameraWrapper Optional camera wrapper from stage options
|
|
2687
|
+
* @returns The resolved ZylemCamera instance
|
|
2688
|
+
*/
|
|
2689
|
+
resolveCamera(cameraOverride, cameraWrapper) {
|
|
2690
|
+
if (cameraOverride) {
|
|
2691
|
+
return cameraOverride;
|
|
2057
2692
|
}
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
detachDebugDelegate() {
|
|
2061
|
-
if (this.debugUnsubscribe) {
|
|
2062
|
-
try {
|
|
2063
|
-
this.debugUnsubscribe();
|
|
2064
|
-
} catch {
|
|
2065
|
-
}
|
|
2693
|
+
if (cameraWrapper) {
|
|
2694
|
+
return cameraWrapper.cameraRef;
|
|
2066
2695
|
}
|
|
2067
|
-
this.
|
|
2068
|
-
this.debugDelegate = null;
|
|
2696
|
+
return this.createDefaultCamera();
|
|
2069
2697
|
}
|
|
2070
2698
|
};
|
|
2071
2699
|
}
|
|
2072
2700
|
});
|
|
2073
2701
|
|
|
2074
|
-
// src/lib/
|
|
2075
|
-
|
|
2076
|
-
var
|
|
2077
|
-
|
|
2078
|
-
"src/lib/camera/camera.ts"() {
|
|
2702
|
+
// src/lib/stage/stage-loading-delegate.ts
|
|
2703
|
+
var StageLoadingDelegate;
|
|
2704
|
+
var init_stage_loading_delegate = __esm({
|
|
2705
|
+
"src/lib/stage/stage-loading-delegate.ts"() {
|
|
2079
2706
|
"use strict";
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2707
|
+
init_game_event_bus();
|
|
2708
|
+
StageLoadingDelegate = class {
|
|
2709
|
+
loadingHandlers = [];
|
|
2710
|
+
stageName;
|
|
2711
|
+
stageIndex;
|
|
2712
|
+
/**
|
|
2713
|
+
* Set stage context for event bus emissions.
|
|
2714
|
+
*/
|
|
2715
|
+
setStageContext(stageName, stageIndex) {
|
|
2716
|
+
this.stageName = stageName;
|
|
2717
|
+
this.stageIndex = stageIndex;
|
|
2084
2718
|
}
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
LineBasicMaterial,
|
|
2097
|
-
LineSegments,
|
|
2098
|
-
Mesh as Mesh3,
|
|
2099
|
-
MeshBasicMaterial,
|
|
2100
|
-
Vector3 as Vector310
|
|
2101
|
-
} from "three";
|
|
2102
|
-
var DebugEntityCursor;
|
|
2103
|
-
var init_debug_entity_cursor = __esm({
|
|
2104
|
-
"src/lib/stage/debug-entity-cursor.ts"() {
|
|
2105
|
-
"use strict";
|
|
2106
|
-
DebugEntityCursor = class {
|
|
2107
|
-
scene;
|
|
2108
|
-
container;
|
|
2109
|
-
fillMesh;
|
|
2110
|
-
edgeLines;
|
|
2111
|
-
currentColor = new Color7(65280);
|
|
2112
|
-
bbox = new Box3();
|
|
2113
|
-
size = new Vector310();
|
|
2114
|
-
center = new Vector310();
|
|
2115
|
-
constructor(scene) {
|
|
2116
|
-
this.scene = scene;
|
|
2117
|
-
const initialGeometry = new BoxGeometry(1, 1, 1);
|
|
2118
|
-
this.fillMesh = new Mesh3(
|
|
2119
|
-
initialGeometry,
|
|
2120
|
-
new MeshBasicMaterial({
|
|
2121
|
-
color: this.currentColor,
|
|
2122
|
-
transparent: true,
|
|
2123
|
-
opacity: 0.12,
|
|
2124
|
-
depthWrite: false
|
|
2125
|
-
})
|
|
2126
|
-
);
|
|
2127
|
-
const edges = new EdgesGeometry(initialGeometry);
|
|
2128
|
-
this.edgeLines = new LineSegments(
|
|
2129
|
-
edges,
|
|
2130
|
-
new LineBasicMaterial({ color: this.currentColor, linewidth: 1 })
|
|
2131
|
-
);
|
|
2132
|
-
this.container = new Group4();
|
|
2133
|
-
this.container.name = "DebugEntityCursor";
|
|
2134
|
-
this.container.add(this.fillMesh);
|
|
2135
|
-
this.container.add(this.edgeLines);
|
|
2136
|
-
this.container.visible = false;
|
|
2137
|
-
this.scene.add(this.container);
|
|
2138
|
-
}
|
|
2139
|
-
setColor(color) {
|
|
2140
|
-
this.currentColor.set(color);
|
|
2141
|
-
this.fillMesh.material.color.set(this.currentColor);
|
|
2142
|
-
this.edgeLines.material.color.set(this.currentColor);
|
|
2719
|
+
/**
|
|
2720
|
+
* Subscribe to loading events.
|
|
2721
|
+
*
|
|
2722
|
+
* @param callback Invoked for each loading event (start, progress, complete)
|
|
2723
|
+
* @returns Unsubscribe function
|
|
2724
|
+
*/
|
|
2725
|
+
onLoading(callback) {
|
|
2726
|
+
this.loadingHandlers.push(callback);
|
|
2727
|
+
return () => {
|
|
2728
|
+
this.loadingHandlers = this.loadingHandlers.filter((h) => h !== callback);
|
|
2729
|
+
};
|
|
2143
2730
|
}
|
|
2144
2731
|
/**
|
|
2145
|
-
*
|
|
2732
|
+
* Emit a loading event to all subscribers and to the game event bus.
|
|
2733
|
+
*
|
|
2734
|
+
* @param event The loading event to broadcast
|
|
2146
2735
|
*/
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2736
|
+
emit(event) {
|
|
2737
|
+
for (const handler of this.loadingHandlers) {
|
|
2738
|
+
try {
|
|
2739
|
+
handler(event);
|
|
2740
|
+
} catch (e) {
|
|
2741
|
+
console.error("Loading handler failed", e);
|
|
2742
|
+
}
|
|
2151
2743
|
}
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
this.
|
|
2155
|
-
|
|
2744
|
+
const payload = {
|
|
2745
|
+
...event,
|
|
2746
|
+
stageName: this.stageName,
|
|
2747
|
+
stageIndex: this.stageIndex
|
|
2748
|
+
};
|
|
2749
|
+
if (event.type === "start") {
|
|
2750
|
+
gameEventBus.emit("stage:loading:start", payload);
|
|
2751
|
+
} else if (event.type === "progress") {
|
|
2752
|
+
gameEventBus.emit("stage:loading:progress", payload);
|
|
2753
|
+
} else if (event.type === "complete") {
|
|
2754
|
+
gameEventBus.emit("stage:loading:complete", payload);
|
|
2156
2755
|
}
|
|
2157
|
-
this.bbox.getSize(this.size);
|
|
2158
|
-
this.bbox.getCenter(this.center);
|
|
2159
|
-
const newGeom = new BoxGeometry(
|
|
2160
|
-
Math.max(this.size.x, 1e-6),
|
|
2161
|
-
Math.max(this.size.y, 1e-6),
|
|
2162
|
-
Math.max(this.size.z, 1e-6)
|
|
2163
|
-
);
|
|
2164
|
-
this.fillMesh.geometry.dispose();
|
|
2165
|
-
this.fillMesh.geometry = newGeom;
|
|
2166
|
-
const newEdges = new EdgesGeometry(newGeom);
|
|
2167
|
-
this.edgeLines.geometry.dispose();
|
|
2168
|
-
this.edgeLines.geometry = newEdges;
|
|
2169
|
-
this.container.position.copy(this.center);
|
|
2170
|
-
this.container.visible = true;
|
|
2171
2756
|
}
|
|
2172
|
-
|
|
2173
|
-
|
|
2757
|
+
/**
|
|
2758
|
+
* Emit a start loading event.
|
|
2759
|
+
*/
|
|
2760
|
+
emitStart(message = "Loading stage...") {
|
|
2761
|
+
this.emit({ type: "start", message, progress: 0 });
|
|
2174
2762
|
}
|
|
2763
|
+
/**
|
|
2764
|
+
* Emit a progress loading event.
|
|
2765
|
+
*/
|
|
2766
|
+
emitProgress(message, current, total) {
|
|
2767
|
+
const progress = total > 0 ? current / total : 0;
|
|
2768
|
+
this.emit({ type: "progress", message, progress, current, total });
|
|
2769
|
+
}
|
|
2770
|
+
/**
|
|
2771
|
+
* Emit a complete loading event.
|
|
2772
|
+
*/
|
|
2773
|
+
emitComplete(message = "Stage loaded") {
|
|
2774
|
+
this.emit({ type: "complete", message, progress: 1 });
|
|
2775
|
+
}
|
|
2776
|
+
/**
|
|
2777
|
+
* Clear all loading handlers.
|
|
2778
|
+
*/
|
|
2175
2779
|
dispose() {
|
|
2176
|
-
this.
|
|
2177
|
-
this.fillMesh.geometry.dispose();
|
|
2178
|
-
this.fillMesh.material.dispose();
|
|
2179
|
-
this.edgeLines.geometry.dispose();
|
|
2180
|
-
this.edgeLines.material.dispose();
|
|
2780
|
+
this.loadingHandlers = [];
|
|
2181
2781
|
}
|
|
2182
2782
|
};
|
|
2183
2783
|
}
|
|
2184
2784
|
});
|
|
2185
2785
|
|
|
2186
|
-
// src/lib/
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2786
|
+
// src/lib/core/utility/options-parser.ts
|
|
2787
|
+
function isBaseNode(item) {
|
|
2788
|
+
return !!item && typeof item === "object" && typeof item.create === "function";
|
|
2789
|
+
}
|
|
2790
|
+
function isThenable(item) {
|
|
2791
|
+
return !!item && typeof item.then === "function";
|
|
2792
|
+
}
|
|
2793
|
+
function isCameraWrapper(item) {
|
|
2794
|
+
return !!item && typeof item === "object" && item.constructor?.name === "CameraWrapper";
|
|
2795
|
+
}
|
|
2796
|
+
function isConfigObject(item) {
|
|
2797
|
+
if (!item || typeof item !== "object") return false;
|
|
2798
|
+
if (isBaseNode(item)) return false;
|
|
2799
|
+
if (isCameraWrapper(item)) return false;
|
|
2800
|
+
if (isThenable(item)) return false;
|
|
2801
|
+
if (typeof item.then === "function") return false;
|
|
2802
|
+
return item.constructor === Object || item.constructor?.name === "Object";
|
|
2803
|
+
}
|
|
2804
|
+
function isEntityInput(item) {
|
|
2805
|
+
if (!item) return false;
|
|
2806
|
+
if (isBaseNode(item)) return true;
|
|
2807
|
+
if (typeof item === "function") return true;
|
|
2808
|
+
if (isThenable(item)) return true;
|
|
2809
|
+
return false;
|
|
2810
|
+
}
|
|
2811
|
+
var init_options_parser = __esm({
|
|
2812
|
+
"src/lib/core/utility/options-parser.ts"() {
|
|
2192
2813
|
"use strict";
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
resetHoveredEntity();
|
|
2250
|
-
}
|
|
2251
|
-
if (this.isMouseDown) {
|
|
2252
|
-
this.handleActionOnHit(hoveredUuid2 ?? null, origin, direction, hit.toi);
|
|
2253
|
-
}
|
|
2254
|
-
}
|
|
2255
|
-
this.isMouseDown = false;
|
|
2256
|
-
const hoveredUuid = getHoveredEntity();
|
|
2257
|
-
if (!hoveredUuid) {
|
|
2258
|
-
this.debugCursor?.hide();
|
|
2259
|
-
return;
|
|
2260
|
-
}
|
|
2261
|
-
const hoveredEntity = this.stage._debugMap.get(`${hoveredUuid}`);
|
|
2262
|
-
const targetObject = hoveredEntity?.group ?? hoveredEntity?.mesh ?? null;
|
|
2263
|
-
if (!targetObject) {
|
|
2264
|
-
this.debugCursor?.hide();
|
|
2265
|
-
return;
|
|
2266
|
-
}
|
|
2267
|
-
switch (tool) {
|
|
2268
|
-
case "select":
|
|
2269
|
-
this.debugCursor?.setColor(SELECT_TOOL_COLOR);
|
|
2270
|
-
break;
|
|
2271
|
-
case "delete":
|
|
2272
|
-
this.debugCursor?.setColor(DELETE_TOOL_COLOR);
|
|
2273
|
-
break;
|
|
2274
|
-
default:
|
|
2275
|
-
this.debugCursor?.setColor(16777215);
|
|
2276
|
-
break;
|
|
2277
|
-
}
|
|
2278
|
-
this.debugCursor?.updateFromObject(targetObject);
|
|
2279
|
-
}
|
|
2280
|
-
dispose() {
|
|
2281
|
-
this.disposeFns.forEach((fn) => fn());
|
|
2282
|
-
this.disposeFns = [];
|
|
2283
|
-
this.debugCursor?.dispose();
|
|
2284
|
-
if (this.debugLines && this.stage.scene) {
|
|
2285
|
-
this.stage.scene.scene.remove(this.debugLines);
|
|
2286
|
-
this.debugLines.geometry.dispose();
|
|
2287
|
-
this.debugLines.material.dispose();
|
|
2288
|
-
this.debugLines = null;
|
|
2289
|
-
}
|
|
2290
|
-
}
|
|
2291
|
-
handleActionOnHit(hoveredUuid, origin, direction, toi) {
|
|
2292
|
-
const tool = getDebugTool();
|
|
2293
|
-
switch (tool) {
|
|
2294
|
-
case "select": {
|
|
2295
|
-
if (hoveredUuid) {
|
|
2296
|
-
const entity = this.stage._debugMap.get(hoveredUuid);
|
|
2297
|
-
if (entity) setSelectedEntity(entity);
|
|
2298
|
-
}
|
|
2299
|
-
break;
|
|
2300
|
-
}
|
|
2301
|
-
case "delete": {
|
|
2302
|
-
if (hoveredUuid) {
|
|
2303
|
-
this.stage.removeEntityByUuid(hoveredUuid);
|
|
2304
|
-
}
|
|
2305
|
-
break;
|
|
2306
|
-
}
|
|
2307
|
-
case "scale": {
|
|
2308
|
-
if (!this.options.addEntityFactory) break;
|
|
2309
|
-
const hitPosition = origin.clone().add(direction.clone().multiplyScalar(toi));
|
|
2310
|
-
const newNode = this.options.addEntityFactory({ position: hitPosition });
|
|
2311
|
-
if (newNode) {
|
|
2312
|
-
Promise.resolve(newNode).then((node) => {
|
|
2313
|
-
if (node) this.stage.spawnEntity(node);
|
|
2314
|
-
}).catch(() => {
|
|
2315
|
-
});
|
|
2316
|
-
}
|
|
2317
|
-
break;
|
|
2318
|
-
}
|
|
2319
|
-
default:
|
|
2320
|
-
break;
|
|
2321
|
-
}
|
|
2322
|
-
}
|
|
2323
|
-
attachDomListeners() {
|
|
2324
|
-
const canvas = this.stage.cameraRef?.renderer.domElement ?? this.stage.scene?.zylemCamera.renderer.domElement;
|
|
2325
|
-
if (!canvas) return;
|
|
2326
|
-
const onMouseMove = (e) => {
|
|
2327
|
-
const rect = canvas.getBoundingClientRect();
|
|
2328
|
-
const x = (e.clientX - rect.left) / rect.width * 2 - 1;
|
|
2329
|
-
const y = -((e.clientY - rect.top) / rect.height * 2 - 1);
|
|
2330
|
-
this.mouseNdc.set(x, y);
|
|
2331
|
-
};
|
|
2332
|
-
const onMouseDown = (e) => {
|
|
2333
|
-
this.isMouseDown = true;
|
|
2334
|
-
};
|
|
2335
|
-
canvas.addEventListener("mousemove", onMouseMove);
|
|
2336
|
-
canvas.addEventListener("mousedown", onMouseDown);
|
|
2337
|
-
this.disposeFns.push(() => canvas.removeEventListener("mousemove", onMouseMove));
|
|
2338
|
-
this.disposeFns.push(() => canvas.removeEventListener("mousedown", onMouseDown));
|
|
2814
|
+
}
|
|
2815
|
+
});
|
|
2816
|
+
|
|
2817
|
+
// src/lib/stage/stage-config.ts
|
|
2818
|
+
import { Vector3 as Vector312 } from "three";
|
|
2819
|
+
function createDefaultStageConfig() {
|
|
2820
|
+
return new StageConfig(
|
|
2821
|
+
{
|
|
2822
|
+
p1: ["gamepad-1", "keyboard-1"],
|
|
2823
|
+
p2: ["gamepad-2", "keyboard-2"]
|
|
2824
|
+
},
|
|
2825
|
+
ZylemBlueColor,
|
|
2826
|
+
null,
|
|
2827
|
+
new Vector312(0, 0, 0),
|
|
2828
|
+
{}
|
|
2829
|
+
);
|
|
2830
|
+
}
|
|
2831
|
+
function parseStageOptions(options = []) {
|
|
2832
|
+
const defaults = createDefaultStageConfig();
|
|
2833
|
+
let config = {};
|
|
2834
|
+
const entities = [];
|
|
2835
|
+
const asyncEntities = [];
|
|
2836
|
+
let camera;
|
|
2837
|
+
for (const item of options) {
|
|
2838
|
+
if (isCameraWrapper(item)) {
|
|
2839
|
+
camera = item;
|
|
2840
|
+
} else if (isBaseNode(item)) {
|
|
2841
|
+
entities.push(item);
|
|
2842
|
+
} else if (isEntityInput(item) && !isBaseNode(item)) {
|
|
2843
|
+
asyncEntities.push(item);
|
|
2844
|
+
} else if (isConfigObject(item)) {
|
|
2845
|
+
config = { ...config, ...item };
|
|
2846
|
+
}
|
|
2847
|
+
}
|
|
2848
|
+
const resolvedConfig = new StageConfig(
|
|
2849
|
+
config.inputs ?? defaults.inputs,
|
|
2850
|
+
config.backgroundColor ?? defaults.backgroundColor,
|
|
2851
|
+
config.backgroundImage ?? defaults.backgroundImage,
|
|
2852
|
+
config.gravity ?? defaults.gravity,
|
|
2853
|
+
config.variables ?? defaults.variables
|
|
2854
|
+
);
|
|
2855
|
+
return { config: resolvedConfig, entities, asyncEntities, camera };
|
|
2856
|
+
}
|
|
2857
|
+
var StageConfig;
|
|
2858
|
+
var init_stage_config = __esm({
|
|
2859
|
+
"src/lib/stage/stage-config.ts"() {
|
|
2860
|
+
"use strict";
|
|
2861
|
+
init_options_parser();
|
|
2862
|
+
init_vector();
|
|
2863
|
+
StageConfig = class {
|
|
2864
|
+
constructor(inputs, backgroundColor, backgroundImage, gravity, variables) {
|
|
2865
|
+
this.inputs = inputs;
|
|
2866
|
+
this.backgroundColor = backgroundColor;
|
|
2867
|
+
this.backgroundImage = backgroundImage;
|
|
2868
|
+
this.gravity = gravity;
|
|
2869
|
+
this.variables = variables;
|
|
2339
2870
|
}
|
|
2340
2871
|
};
|
|
2341
2872
|
}
|
|
@@ -2343,7 +2874,8 @@ var init_stage_debug_delegate = __esm({
|
|
|
2343
2874
|
|
|
2344
2875
|
// src/lib/stage/zylem-stage.ts
|
|
2345
2876
|
import { addComponent, addEntity, createWorld as createECS, removeEntity } from "bitecs";
|
|
2346
|
-
import { Color as
|
|
2877
|
+
import { Color as Color9, Vector3 as Vector313 } from "three";
|
|
2878
|
+
import { subscribe as subscribe5 } from "valtio/vanilla";
|
|
2347
2879
|
import { nanoid as nanoid2 } from "nanoid";
|
|
2348
2880
|
var STAGE_TYPE, ZylemStage;
|
|
2349
2881
|
var init_zylem_stage = __esm({
|
|
@@ -2357,12 +2889,13 @@ var init_zylem_stage = __esm({
|
|
|
2357
2889
|
init_game_state();
|
|
2358
2890
|
init_lifecycle_base();
|
|
2359
2891
|
init_transformable_system();
|
|
2360
|
-
init_base_node();
|
|
2361
|
-
init_perspective();
|
|
2362
|
-
init_camera();
|
|
2363
2892
|
init_stage_debug_delegate();
|
|
2893
|
+
init_stage_camera_debug_delegate();
|
|
2894
|
+
init_stage_camera_delegate();
|
|
2895
|
+
init_stage_loading_delegate();
|
|
2364
2896
|
init_entity();
|
|
2365
|
-
|
|
2897
|
+
init_stage_config();
|
|
2898
|
+
init_options_parser();
|
|
2366
2899
|
STAGE_TYPE = "Stage";
|
|
2367
2900
|
ZylemStage = class extends LifeCycleBase {
|
|
2368
2901
|
type = STAGE_TYPE;
|
|
@@ -2373,7 +2906,7 @@ var init_zylem_stage = __esm({
|
|
|
2373
2906
|
p1: ["gamepad-1", "keyboard"],
|
|
2374
2907
|
p2: ["gamepad-2", "keyboard"]
|
|
2375
2908
|
},
|
|
2376
|
-
gravity: new
|
|
2909
|
+
gravity: new Vector313(0, 0, 0),
|
|
2377
2910
|
variables: {},
|
|
2378
2911
|
entities: []
|
|
2379
2912
|
};
|
|
@@ -2388,16 +2921,19 @@ var init_zylem_stage = __esm({
|
|
|
2388
2921
|
isLoaded = false;
|
|
2389
2922
|
_debugMap = /* @__PURE__ */ new Map();
|
|
2390
2923
|
entityAddedHandlers = [];
|
|
2391
|
-
loadingHandlers = [];
|
|
2392
2924
|
ecs = createECS();
|
|
2393
2925
|
testSystem = null;
|
|
2394
2926
|
transformSystem = null;
|
|
2395
2927
|
debugDelegate = null;
|
|
2396
2928
|
cameraDebugDelegate = null;
|
|
2929
|
+
debugStateUnsubscribe = null;
|
|
2397
2930
|
uuid;
|
|
2398
2931
|
wrapperRef = null;
|
|
2399
2932
|
camera;
|
|
2400
2933
|
cameraRef = null;
|
|
2934
|
+
// Delegates
|
|
2935
|
+
cameraDelegate;
|
|
2936
|
+
loadingDelegate;
|
|
2401
2937
|
/**
|
|
2402
2938
|
* Create a new stage.
|
|
2403
2939
|
* @param options Stage options: partial config, camera, and initial entities or factories
|
|
@@ -2407,49 +2943,22 @@ var init_zylem_stage = __esm({
|
|
|
2407
2943
|
this.world = null;
|
|
2408
2944
|
this.scene = null;
|
|
2409
2945
|
this.uuid = nanoid2();
|
|
2410
|
-
|
|
2411
|
-
this.
|
|
2412
|
-
|
|
2413
|
-
this.
|
|
2414
|
-
this.
|
|
2415
|
-
this.
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
entities.push(item);
|
|
2427
|
-
} else if (this.isEntityInput(item)) {
|
|
2428
|
-
asyncEntities.push(item);
|
|
2429
|
-
} else if (this.isZylemStageConfig(item)) {
|
|
2430
|
-
config = { ...config, ...item };
|
|
2431
|
-
}
|
|
2432
|
-
}
|
|
2433
|
-
return { config, entities, asyncEntities, camera };
|
|
2434
|
-
}
|
|
2435
|
-
isZylemStageConfig(item) {
|
|
2436
|
-
return item && typeof item === "object" && !(item instanceof BaseNode) && !(item instanceof CameraWrapper);
|
|
2437
|
-
}
|
|
2438
|
-
isBaseNode(item) {
|
|
2439
|
-
return item && typeof item === "object" && typeof item.create === "function";
|
|
2440
|
-
}
|
|
2441
|
-
isCameraWrapper(item) {
|
|
2442
|
-
return item && typeof item === "object" && item.constructor.name === "CameraWrapper";
|
|
2443
|
-
}
|
|
2444
|
-
isEntityInput(item) {
|
|
2445
|
-
if (!item) return false;
|
|
2446
|
-
if (this.isBaseNode(item)) return true;
|
|
2447
|
-
if (typeof item === "function") return true;
|
|
2448
|
-
if (typeof item === "object" && typeof item.then === "function") return true;
|
|
2449
|
-
return false;
|
|
2450
|
-
}
|
|
2451
|
-
isThenable(value) {
|
|
2452
|
-
return !!value && typeof value.then === "function";
|
|
2946
|
+
this.cameraDelegate = new StageCameraDelegate(this);
|
|
2947
|
+
this.loadingDelegate = new StageLoadingDelegate();
|
|
2948
|
+
const parsed = parseStageOptions(options);
|
|
2949
|
+
this.camera = parsed.camera;
|
|
2950
|
+
this.children = parsed.entities;
|
|
2951
|
+
this.pendingEntities = parsed.asyncEntities;
|
|
2952
|
+
this.saveState({
|
|
2953
|
+
...this.state,
|
|
2954
|
+
inputs: parsed.config.inputs,
|
|
2955
|
+
backgroundColor: parsed.config.backgroundColor,
|
|
2956
|
+
backgroundImage: parsed.config.backgroundImage,
|
|
2957
|
+
gravity: parsed.config.gravity,
|
|
2958
|
+
variables: parsed.config.variables,
|
|
2959
|
+
entities: []
|
|
2960
|
+
});
|
|
2961
|
+
this.gravity = parsed.config.gravity ?? new Vector313(0, 0, 0);
|
|
2453
2962
|
}
|
|
2454
2963
|
handleEntityImmediatelyOrQueue(entity) {
|
|
2455
2964
|
if (this.isLoaded) {
|
|
@@ -2470,42 +2979,47 @@ var init_zylem_stage = __esm({
|
|
|
2470
2979
|
}
|
|
2471
2980
|
setState() {
|
|
2472
2981
|
const { backgroundColor, backgroundImage } = this.state;
|
|
2473
|
-
const color = backgroundColor instanceof
|
|
2982
|
+
const color = backgroundColor instanceof Color9 ? backgroundColor : new Color9(backgroundColor);
|
|
2474
2983
|
setStageBackgroundColor(color);
|
|
2475
2984
|
setStageBackgroundImage(backgroundImage);
|
|
2476
2985
|
setStageVariables(this.state.variables ?? {});
|
|
2477
2986
|
}
|
|
2478
2987
|
/**
|
|
2479
2988
|
* Load and initialize the stage's scene and world.
|
|
2989
|
+
* Uses generator pattern to yield control to event loop for real-time progress.
|
|
2480
2990
|
* @param id DOM element id for the renderer container
|
|
2481
2991
|
* @param camera Optional camera override
|
|
2482
2992
|
*/
|
|
2483
2993
|
async load(id, camera) {
|
|
2484
2994
|
this.setState();
|
|
2485
|
-
const zylemCamera =
|
|
2995
|
+
const zylemCamera = this.cameraDelegate.resolveCamera(camera, this.camera);
|
|
2486
2996
|
this.cameraRef = zylemCamera;
|
|
2487
2997
|
this.scene = new ZylemScene(id, zylemCamera, this.state);
|
|
2488
|
-
const physicsWorld = await ZylemWorld.loadPhysics(this.gravity ?? new
|
|
2998
|
+
const physicsWorld = await ZylemWorld.loadPhysics(this.gravity ?? new Vector313(0, 0, 0));
|
|
2489
2999
|
this.world = new ZylemWorld(physicsWorld);
|
|
2490
3000
|
this.scene.setup();
|
|
2491
|
-
this.
|
|
3001
|
+
this.loadingDelegate.emitStart();
|
|
3002
|
+
await this.runEntityLoadGenerator();
|
|
3003
|
+
this.transformSystem = createTransformSystem(this);
|
|
3004
|
+
this.isLoaded = true;
|
|
3005
|
+
this.loadingDelegate.emitComplete();
|
|
3006
|
+
}
|
|
3007
|
+
/**
|
|
3008
|
+
* Generator that yields between entity loads for real-time progress updates.
|
|
3009
|
+
*/
|
|
3010
|
+
*entityLoadGenerator() {
|
|
2492
3011
|
const total = this.children.length + this.pendingEntities.length + this.pendingPromises.length;
|
|
2493
3012
|
let current = 0;
|
|
2494
|
-
for (
|
|
3013
|
+
for (const child of this.children) {
|
|
2495
3014
|
this.spawnEntity(child);
|
|
2496
3015
|
current++;
|
|
2497
|
-
|
|
2498
|
-
type: "progress",
|
|
2499
|
-
message: `Loaded entity ${child.name || "unknown"}`,
|
|
2500
|
-
progress: current / total,
|
|
2501
|
-
current,
|
|
2502
|
-
total
|
|
2503
|
-
});
|
|
3016
|
+
yield { current, total, name: child.name || "unknown" };
|
|
2504
3017
|
}
|
|
2505
3018
|
if (this.pendingEntities.length) {
|
|
2506
3019
|
this.enqueue(...this.pendingEntities);
|
|
2507
3020
|
current += this.pendingEntities.length;
|
|
2508
3021
|
this.pendingEntities = [];
|
|
3022
|
+
yield { current, total, name: "pending entities" };
|
|
2509
3023
|
}
|
|
2510
3024
|
if (this.pendingPromises.length) {
|
|
2511
3025
|
for (const promise of this.pendingPromises) {
|
|
@@ -2515,24 +3029,44 @@ var init_zylem_stage = __esm({
|
|
|
2515
3029
|
}
|
|
2516
3030
|
current += this.pendingPromises.length;
|
|
2517
3031
|
this.pendingPromises = [];
|
|
3032
|
+
yield { current, total, name: "async entities" };
|
|
2518
3033
|
}
|
|
2519
|
-
this.transformSystem = createTransformSystem(this);
|
|
2520
|
-
this.isLoaded = true;
|
|
2521
|
-
this.emitLoading({ type: "complete", message: "Stage loaded", progress: 1 });
|
|
2522
3034
|
}
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
3035
|
+
/**
|
|
3036
|
+
* Runs the entity load generator, yielding to the event loop between loads.
|
|
3037
|
+
* This allows the browser to process events and update the UI in real-time.
|
|
3038
|
+
*/
|
|
3039
|
+
async runEntityLoadGenerator() {
|
|
3040
|
+
const gen = this.entityLoadGenerator();
|
|
3041
|
+
for (const progress of gen) {
|
|
3042
|
+
this.loadingDelegate.emitProgress(`Loaded ${progress.name}`, progress.current, progress.total);
|
|
3043
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
3044
|
+
}
|
|
2528
3045
|
}
|
|
2529
3046
|
_setup(params) {
|
|
2530
3047
|
if (!this.scene || !this.world) {
|
|
2531
3048
|
this.logMissingEntities();
|
|
2532
3049
|
return;
|
|
2533
3050
|
}
|
|
2534
|
-
|
|
3051
|
+
this.updateDebugDelegate();
|
|
3052
|
+
this.debugStateUnsubscribe = subscribe5(debugState, () => {
|
|
3053
|
+
this.updateDebugDelegate();
|
|
3054
|
+
});
|
|
3055
|
+
}
|
|
3056
|
+
updateDebugDelegate() {
|
|
3057
|
+
if (debugState.enabled && !this.debugDelegate && this.scene && this.world) {
|
|
2535
3058
|
this.debugDelegate = new StageDebugDelegate(this);
|
|
3059
|
+
if (this.cameraRef && !this.cameraDebugDelegate) {
|
|
3060
|
+
this.cameraDebugDelegate = new StageCameraDebugDelegate(this);
|
|
3061
|
+
this.cameraRef.setDebugDelegate(this.cameraDebugDelegate);
|
|
3062
|
+
}
|
|
3063
|
+
} else if (!debugState.enabled && this.debugDelegate) {
|
|
3064
|
+
this.debugDelegate.dispose();
|
|
3065
|
+
this.debugDelegate = null;
|
|
3066
|
+
if (this.cameraRef) {
|
|
3067
|
+
this.cameraRef.setDebugDelegate(null);
|
|
3068
|
+
}
|
|
3069
|
+
this.cameraDebugDelegate = null;
|
|
2536
3070
|
}
|
|
2537
3071
|
}
|
|
2538
3072
|
_update(params) {
|
|
@@ -2542,7 +3076,7 @@ var init_zylem_stage = __esm({
|
|
|
2542
3076
|
return;
|
|
2543
3077
|
}
|
|
2544
3078
|
this.world.update(params);
|
|
2545
|
-
this.transformSystem(this.ecs);
|
|
3079
|
+
this.transformSystem?.system(this.ecs);
|
|
2546
3080
|
this._childrenMap.forEach((child, eid) => {
|
|
2547
3081
|
child.nodeUpdate({
|
|
2548
3082
|
...params,
|
|
@@ -2576,13 +3110,20 @@ var init_zylem_stage = __esm({
|
|
|
2576
3110
|
this._debugMap.clear();
|
|
2577
3111
|
this.world?.destroy();
|
|
2578
3112
|
this.scene?.destroy();
|
|
3113
|
+
if (this.debugStateUnsubscribe) {
|
|
3114
|
+
this.debugStateUnsubscribe();
|
|
3115
|
+
this.debugStateUnsubscribe = null;
|
|
3116
|
+
}
|
|
2579
3117
|
this.debugDelegate?.dispose();
|
|
3118
|
+
this.debugDelegate = null;
|
|
2580
3119
|
this.cameraRef?.setDebugDelegate(null);
|
|
2581
3120
|
this.cameraDebugDelegate = null;
|
|
2582
3121
|
this.isLoaded = false;
|
|
2583
3122
|
this.world = null;
|
|
2584
3123
|
this.scene = null;
|
|
2585
3124
|
this.cameraRef = null;
|
|
3125
|
+
this.transformSystem?.destroy(this.ecs);
|
|
3126
|
+
this.transformSystem = null;
|
|
2586
3127
|
resetStageVariables();
|
|
2587
3128
|
clearVariables(this);
|
|
2588
3129
|
}
|
|
@@ -2663,13 +3204,7 @@ var init_zylem_stage = __esm({
|
|
|
2663
3204
|
};
|
|
2664
3205
|
}
|
|
2665
3206
|
onLoading(callback) {
|
|
2666
|
-
this.
|
|
2667
|
-
return () => {
|
|
2668
|
-
this.loadingHandlers = this.loadingHandlers.filter((h) => h !== callback);
|
|
2669
|
-
};
|
|
2670
|
-
}
|
|
2671
|
-
emitLoading(event) {
|
|
2672
|
-
this.loadingHandlers.forEach((h) => h(event));
|
|
3207
|
+
return this.loadingDelegate.onLoading(callback);
|
|
2673
3208
|
}
|
|
2674
3209
|
/**
|
|
2675
3210
|
* Remove an entity and its resources by its UUID.
|
|
@@ -2726,16 +3261,16 @@ var init_zylem_stage = __esm({
|
|
|
2726
3261
|
enqueue(...items) {
|
|
2727
3262
|
for (const item of items) {
|
|
2728
3263
|
if (!item) continue;
|
|
2729
|
-
if (
|
|
3264
|
+
if (isBaseNode(item)) {
|
|
2730
3265
|
this.handleEntityImmediatelyOrQueue(item);
|
|
2731
3266
|
continue;
|
|
2732
3267
|
}
|
|
2733
3268
|
if (typeof item === "function") {
|
|
2734
3269
|
try {
|
|
2735
3270
|
const result = item();
|
|
2736
|
-
if (
|
|
3271
|
+
if (isBaseNode(result)) {
|
|
2737
3272
|
this.handleEntityImmediatelyOrQueue(result);
|
|
2738
|
-
} else if (
|
|
3273
|
+
} else if (isThenable(result)) {
|
|
2739
3274
|
this.handlePromiseWithSpawnOnResolve(result);
|
|
2740
3275
|
}
|
|
2741
3276
|
} catch (error) {
|
|
@@ -2743,7 +3278,7 @@ var init_zylem_stage = __esm({
|
|
|
2743
3278
|
}
|
|
2744
3279
|
continue;
|
|
2745
3280
|
}
|
|
2746
|
-
if (
|
|
3281
|
+
if (isThenable(item)) {
|
|
2747
3282
|
this.handlePromiseWithSpawnOnResolve(item);
|
|
2748
3283
|
}
|
|
2749
3284
|
}
|
|
@@ -2752,9 +3287,24 @@ var init_zylem_stage = __esm({
|
|
|
2752
3287
|
}
|
|
2753
3288
|
});
|
|
2754
3289
|
|
|
3290
|
+
// src/lib/camera/camera.ts
|
|
3291
|
+
import { Vector2 as Vector27, Vector3 as Vector314 } from "three";
|
|
3292
|
+
var CameraWrapper;
|
|
3293
|
+
var init_camera = __esm({
|
|
3294
|
+
"src/lib/camera/camera.ts"() {
|
|
3295
|
+
"use strict";
|
|
3296
|
+
CameraWrapper = class {
|
|
3297
|
+
cameraRef;
|
|
3298
|
+
constructor(camera) {
|
|
3299
|
+
this.cameraRef = camera;
|
|
3300
|
+
}
|
|
3301
|
+
};
|
|
3302
|
+
}
|
|
3303
|
+
});
|
|
3304
|
+
|
|
2755
3305
|
// src/lib/stage/stage-default.ts
|
|
2756
3306
|
import { proxy as proxy4 } from "valtio/vanilla";
|
|
2757
|
-
import { Vector3 as
|
|
3307
|
+
import { Vector3 as Vector315 } from "three";
|
|
2758
3308
|
function getStageOptions(options) {
|
|
2759
3309
|
const defaults = getStageDefaultConfig();
|
|
2760
3310
|
let originalConfig = {};
|
|
@@ -2785,7 +3335,7 @@ var init_stage_default = __esm({
|
|
|
2785
3335
|
p1: ["gamepad-1", "keyboard"],
|
|
2786
3336
|
p2: ["gamepad-2", "keyboard"]
|
|
2787
3337
|
},
|
|
2788
|
-
gravity: new
|
|
3338
|
+
gravity: new Vector315(0, 0, 0),
|
|
2789
3339
|
variables: {}
|
|
2790
3340
|
};
|
|
2791
3341
|
stageDefaultsState = proxy4({
|
|
@@ -2810,30 +3360,58 @@ var init_stage = __esm({
|
|
|
2810
3360
|
Stage = class {
|
|
2811
3361
|
wrappedStage;
|
|
2812
3362
|
options = [];
|
|
2813
|
-
//
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
3363
|
+
// Entities added after construction, consumed on each load
|
|
3364
|
+
_pendingEntities = [];
|
|
3365
|
+
// Lifecycle callback arrays
|
|
3366
|
+
setupCallbacks = [];
|
|
3367
|
+
updateCallbacks = [];
|
|
3368
|
+
destroyCallbacks = [];
|
|
3369
|
+
pendingLoadingCallbacks = [];
|
|
2820
3370
|
constructor(options) {
|
|
2821
3371
|
this.options = options;
|
|
2822
3372
|
this.wrappedStage = null;
|
|
2823
3373
|
}
|
|
2824
3374
|
async load(id, camera) {
|
|
2825
3375
|
stageState.entities = [];
|
|
2826
|
-
|
|
3376
|
+
const loadOptions = [...this.options, ...this._pendingEntities];
|
|
3377
|
+
this._pendingEntities = [];
|
|
3378
|
+
this.wrappedStage = new ZylemStage(loadOptions);
|
|
2827
3379
|
this.wrappedStage.wrapperRef = this;
|
|
3380
|
+
this.pendingLoadingCallbacks.forEach((cb) => {
|
|
3381
|
+
this.wrappedStage.onLoading(cb);
|
|
3382
|
+
});
|
|
3383
|
+
this.pendingLoadingCallbacks = [];
|
|
2828
3384
|
const zylemCamera = camera instanceof CameraWrapper ? camera.cameraRef : camera;
|
|
2829
3385
|
await this.wrappedStage.load(id, zylemCamera);
|
|
2830
3386
|
this.wrappedStage.onEntityAdded((child) => {
|
|
2831
3387
|
const next = this.wrappedStage.buildEntityState(child);
|
|
2832
3388
|
stageState.entities = [...stageState.entities, next];
|
|
2833
3389
|
}, { replayExisting: true });
|
|
3390
|
+
this.applyLifecycleCallbacks();
|
|
3391
|
+
}
|
|
3392
|
+
applyLifecycleCallbacks() {
|
|
3393
|
+
if (!this.wrappedStage) return;
|
|
3394
|
+
if (this.setupCallbacks.length > 0) {
|
|
3395
|
+
this.wrappedStage.setup = (params) => {
|
|
3396
|
+
const extended = { ...params, stage: this };
|
|
3397
|
+
this.setupCallbacks.forEach((cb) => cb(extended));
|
|
3398
|
+
};
|
|
3399
|
+
}
|
|
3400
|
+
if (this.updateCallbacks.length > 0) {
|
|
3401
|
+
this.wrappedStage.update = (params) => {
|
|
3402
|
+
const extended = { ...params, stage: this };
|
|
3403
|
+
this.updateCallbacks.forEach((cb) => cb(extended));
|
|
3404
|
+
};
|
|
3405
|
+
}
|
|
3406
|
+
if (this.destroyCallbacks.length > 0) {
|
|
3407
|
+
this.wrappedStage.destroy = (params) => {
|
|
3408
|
+
const extended = { ...params, stage: this };
|
|
3409
|
+
this.destroyCallbacks.forEach((cb) => cb(extended));
|
|
3410
|
+
};
|
|
3411
|
+
}
|
|
2834
3412
|
}
|
|
2835
3413
|
async addEntities(entities) {
|
|
2836
|
-
this.
|
|
3414
|
+
this._pendingEntities.push(...entities);
|
|
2837
3415
|
if (!this.wrappedStage) {
|
|
2838
3416
|
return;
|
|
2839
3417
|
}
|
|
@@ -2858,28 +3436,57 @@ var init_stage = __esm({
|
|
|
2858
3436
|
start(params) {
|
|
2859
3437
|
this.wrappedStage?.nodeSetup(params);
|
|
2860
3438
|
}
|
|
3439
|
+
// Fluent API for adding lifecycle callbacks
|
|
2861
3440
|
onUpdate(...callbacks) {
|
|
2862
|
-
|
|
2863
|
-
|
|
3441
|
+
this.updateCallbacks.push(...callbacks);
|
|
3442
|
+
if (this.wrappedStage) {
|
|
3443
|
+
this.wrappedStage.update = (params) => {
|
|
3444
|
+
const extended = { ...params, stage: this };
|
|
3445
|
+
this.updateCallbacks.forEach((cb) => cb(extended));
|
|
3446
|
+
};
|
|
2864
3447
|
}
|
|
2865
|
-
this
|
|
2866
|
-
const extended = { ...params, stage: this };
|
|
2867
|
-
callbacks.forEach((cb) => cb(extended));
|
|
2868
|
-
};
|
|
3448
|
+
return this;
|
|
2869
3449
|
}
|
|
2870
|
-
onSetup(
|
|
2871
|
-
this.
|
|
3450
|
+
onSetup(...callbacks) {
|
|
3451
|
+
this.setupCallbacks.push(...callbacks);
|
|
3452
|
+
if (this.wrappedStage) {
|
|
3453
|
+
this.wrappedStage.setup = (params) => {
|
|
3454
|
+
const extended = { ...params, stage: this };
|
|
3455
|
+
this.setupCallbacks.forEach((cb) => cb(extended));
|
|
3456
|
+
};
|
|
3457
|
+
}
|
|
3458
|
+
return this;
|
|
2872
3459
|
}
|
|
2873
|
-
onDestroy(
|
|
2874
|
-
this.
|
|
3460
|
+
onDestroy(...callbacks) {
|
|
3461
|
+
this.destroyCallbacks.push(...callbacks);
|
|
3462
|
+
if (this.wrappedStage) {
|
|
3463
|
+
this.wrappedStage.destroy = (params) => {
|
|
3464
|
+
const extended = { ...params, stage: this };
|
|
3465
|
+
this.destroyCallbacks.forEach((cb) => cb(extended));
|
|
3466
|
+
};
|
|
3467
|
+
}
|
|
3468
|
+
return this;
|
|
2875
3469
|
}
|
|
2876
3470
|
onLoading(callback) {
|
|
2877
3471
|
if (!this.wrappedStage) {
|
|
3472
|
+
this.pendingLoadingCallbacks.push(callback);
|
|
2878
3473
|
return () => {
|
|
3474
|
+
this.pendingLoadingCallbacks = this.pendingLoadingCallbacks.filter((c) => c !== callback);
|
|
2879
3475
|
};
|
|
2880
3476
|
}
|
|
2881
3477
|
return this.wrappedStage.onLoading(callback);
|
|
2882
3478
|
}
|
|
3479
|
+
/**
|
|
3480
|
+
* Find an entity by name on the current stage.
|
|
3481
|
+
* @param name The name of the entity to find
|
|
3482
|
+
* @param type Optional type symbol for type inference (e.g., TEXT_TYPE, SPRITE_TYPE)
|
|
3483
|
+
* @returns The entity if found, or undefined
|
|
3484
|
+
* @example stage.getEntityByName('scoreText', TEXT_TYPE)
|
|
3485
|
+
*/
|
|
3486
|
+
getEntityByName(name, type) {
|
|
3487
|
+
const entity = this.wrappedStage?.children.find((c) => c.name === name);
|
|
3488
|
+
return entity;
|
|
3489
|
+
}
|
|
2883
3490
|
};
|
|
2884
3491
|
}
|
|
2885
3492
|
});
|
|
@@ -3717,8 +4324,202 @@ var GameCanvas = class {
|
|
|
3717
4324
|
}
|
|
3718
4325
|
};
|
|
3719
4326
|
|
|
3720
|
-
// src/lib/game/
|
|
4327
|
+
// src/lib/game/game-debug-delegate.ts
|
|
4328
|
+
init_debug_state();
|
|
3721
4329
|
import Stats from "stats.js";
|
|
4330
|
+
import { subscribe as subscribe2 } from "valtio/vanilla";
|
|
4331
|
+
var GameDebugDelegate = class {
|
|
4332
|
+
statsRef = null;
|
|
4333
|
+
unsubscribe = null;
|
|
4334
|
+
constructor() {
|
|
4335
|
+
this.updateDebugUI();
|
|
4336
|
+
this.unsubscribe = subscribe2(debugState, () => {
|
|
4337
|
+
this.updateDebugUI();
|
|
4338
|
+
});
|
|
4339
|
+
}
|
|
4340
|
+
/**
|
|
4341
|
+
* Called every frame - wraps stats.begin()
|
|
4342
|
+
*/
|
|
4343
|
+
begin() {
|
|
4344
|
+
this.statsRef?.begin();
|
|
4345
|
+
}
|
|
4346
|
+
/**
|
|
4347
|
+
* Called every frame - wraps stats.end()
|
|
4348
|
+
*/
|
|
4349
|
+
end() {
|
|
4350
|
+
this.statsRef?.end();
|
|
4351
|
+
}
|
|
4352
|
+
updateDebugUI() {
|
|
4353
|
+
if (debugState.enabled && !this.statsRef) {
|
|
4354
|
+
this.statsRef = new Stats();
|
|
4355
|
+
this.statsRef.showPanel(0);
|
|
4356
|
+
this.statsRef.dom.style.position = "absolute";
|
|
4357
|
+
this.statsRef.dom.style.bottom = "0";
|
|
4358
|
+
this.statsRef.dom.style.right = "0";
|
|
4359
|
+
this.statsRef.dom.style.top = "auto";
|
|
4360
|
+
this.statsRef.dom.style.left = "auto";
|
|
4361
|
+
document.body.appendChild(this.statsRef.dom);
|
|
4362
|
+
} else if (!debugState.enabled && this.statsRef) {
|
|
4363
|
+
if (this.statsRef.dom.parentNode) {
|
|
4364
|
+
this.statsRef.dom.parentNode.removeChild(this.statsRef.dom);
|
|
4365
|
+
}
|
|
4366
|
+
this.statsRef = null;
|
|
4367
|
+
}
|
|
4368
|
+
}
|
|
4369
|
+
dispose() {
|
|
4370
|
+
if (this.unsubscribe) {
|
|
4371
|
+
this.unsubscribe();
|
|
4372
|
+
this.unsubscribe = null;
|
|
4373
|
+
}
|
|
4374
|
+
if (this.statsRef?.dom?.parentNode) {
|
|
4375
|
+
this.statsRef.dom.parentNode.removeChild(this.statsRef.dom);
|
|
4376
|
+
}
|
|
4377
|
+
this.statsRef = null;
|
|
4378
|
+
}
|
|
4379
|
+
};
|
|
4380
|
+
|
|
4381
|
+
// src/lib/game/game-loading-delegate.ts
|
|
4382
|
+
var GAME_LOADING_EVENT = "GAME_LOADING_EVENT";
|
|
4383
|
+
var GameLoadingDelegate = class {
|
|
4384
|
+
loadingHandlers = [];
|
|
4385
|
+
stageLoadingUnsubscribes = [];
|
|
4386
|
+
/**
|
|
4387
|
+
* Subscribe to loading events from the game.
|
|
4388
|
+
* Events include stage context (stageName, stageIndex).
|
|
4389
|
+
*
|
|
4390
|
+
* @param callback Invoked for each loading event
|
|
4391
|
+
* @returns Unsubscribe function
|
|
4392
|
+
*/
|
|
4393
|
+
onLoading(callback) {
|
|
4394
|
+
this.loadingHandlers.push(callback);
|
|
4395
|
+
return () => {
|
|
4396
|
+
this.loadingHandlers = this.loadingHandlers.filter((h) => h !== callback);
|
|
4397
|
+
};
|
|
4398
|
+
}
|
|
4399
|
+
/**
|
|
4400
|
+
* Emit a loading event to all subscribers and dispatch to window.
|
|
4401
|
+
*/
|
|
4402
|
+
emit(event) {
|
|
4403
|
+
for (const handler of this.loadingHandlers) {
|
|
4404
|
+
console.log("Game loading event", event);
|
|
4405
|
+
try {
|
|
4406
|
+
handler(event);
|
|
4407
|
+
} catch (e) {
|
|
4408
|
+
console.error("Game loading handler failed", e);
|
|
4409
|
+
}
|
|
4410
|
+
}
|
|
4411
|
+
if (typeof window !== "undefined") {
|
|
4412
|
+
window.dispatchEvent(new CustomEvent(GAME_LOADING_EVENT, { detail: event }));
|
|
4413
|
+
}
|
|
4414
|
+
}
|
|
4415
|
+
/**
|
|
4416
|
+
* Wire up a stage's loading events to this delegate.
|
|
4417
|
+
*
|
|
4418
|
+
* @param stage The stage to wire up
|
|
4419
|
+
* @param stageIndex The index of the stage
|
|
4420
|
+
*/
|
|
4421
|
+
wireStageLoading(stage, stageIndex) {
|
|
4422
|
+
const unsub = stage.onLoading((event) => {
|
|
4423
|
+
this.emit({
|
|
4424
|
+
type: event.type,
|
|
4425
|
+
message: event.message ?? "",
|
|
4426
|
+
progress: event.progress ?? 0,
|
|
4427
|
+
current: event.current,
|
|
4428
|
+
total: event.total,
|
|
4429
|
+
stageName: stage.uuid ?? `Stage ${stageIndex}`,
|
|
4430
|
+
stageIndex
|
|
4431
|
+
});
|
|
4432
|
+
});
|
|
4433
|
+
if (typeof unsub === "function") {
|
|
4434
|
+
this.stageLoadingUnsubscribes.push(unsub);
|
|
4435
|
+
}
|
|
4436
|
+
}
|
|
4437
|
+
/**
|
|
4438
|
+
* Unsubscribe from all stage loading events.
|
|
4439
|
+
*/
|
|
4440
|
+
unwireAllStages() {
|
|
4441
|
+
for (const unsub of this.stageLoadingUnsubscribes) {
|
|
4442
|
+
try {
|
|
4443
|
+
unsub();
|
|
4444
|
+
} catch {
|
|
4445
|
+
}
|
|
4446
|
+
}
|
|
4447
|
+
this.stageLoadingUnsubscribes = [];
|
|
4448
|
+
}
|
|
4449
|
+
/**
|
|
4450
|
+
* Clean up all handlers.
|
|
4451
|
+
*/
|
|
4452
|
+
dispose() {
|
|
4453
|
+
this.unwireAllStages();
|
|
4454
|
+
this.loadingHandlers = [];
|
|
4455
|
+
}
|
|
4456
|
+
};
|
|
4457
|
+
|
|
4458
|
+
// src/lib/game/zylem-game.ts
|
|
4459
|
+
init_game_event_bus();
|
|
4460
|
+
|
|
4461
|
+
// src/lib/game/game-renderer-observer.ts
|
|
4462
|
+
var GameRendererObserver = class {
|
|
4463
|
+
container = null;
|
|
4464
|
+
camera = null;
|
|
4465
|
+
gameCanvas = null;
|
|
4466
|
+
config = null;
|
|
4467
|
+
mounted = false;
|
|
4468
|
+
setGameCanvas(canvas) {
|
|
4469
|
+
this.gameCanvas = canvas;
|
|
4470
|
+
this.tryMount();
|
|
4471
|
+
}
|
|
4472
|
+
setConfig(config) {
|
|
4473
|
+
this.config = config;
|
|
4474
|
+
this.tryMount();
|
|
4475
|
+
}
|
|
4476
|
+
setContainer(container) {
|
|
4477
|
+
this.container = container;
|
|
4478
|
+
this.tryMount();
|
|
4479
|
+
}
|
|
4480
|
+
setCamera(camera) {
|
|
4481
|
+
this.camera = camera;
|
|
4482
|
+
this.tryMount();
|
|
4483
|
+
}
|
|
4484
|
+
/**
|
|
4485
|
+
* Attempt to mount renderer if all dependencies are available.
|
|
4486
|
+
*/
|
|
4487
|
+
tryMount() {
|
|
4488
|
+
if (this.mounted) return;
|
|
4489
|
+
if (!this.container || !this.camera || !this.gameCanvas) return;
|
|
4490
|
+
const dom = this.camera.getDomElement();
|
|
4491
|
+
const internal = this.config?.internalResolution;
|
|
4492
|
+
this.gameCanvas.mountRenderer(dom, (cssW, cssH) => {
|
|
4493
|
+
if (!this.camera) return;
|
|
4494
|
+
if (internal) {
|
|
4495
|
+
this.camera.setPixelRatio(1);
|
|
4496
|
+
this.camera.resize(internal.width, internal.height);
|
|
4497
|
+
} else {
|
|
4498
|
+
const dpr = window.devicePixelRatio || 1;
|
|
4499
|
+
this.camera.setPixelRatio(dpr);
|
|
4500
|
+
this.camera.resize(cssW, cssH);
|
|
4501
|
+
}
|
|
4502
|
+
});
|
|
4503
|
+
this.mounted = true;
|
|
4504
|
+
}
|
|
4505
|
+
/**
|
|
4506
|
+
* Reset state for stage transitions.
|
|
4507
|
+
*/
|
|
4508
|
+
reset() {
|
|
4509
|
+
this.camera = null;
|
|
4510
|
+
this.mounted = false;
|
|
4511
|
+
}
|
|
4512
|
+
dispose() {
|
|
4513
|
+
this.container = null;
|
|
4514
|
+
this.camera = null;
|
|
4515
|
+
this.gameCanvas = null;
|
|
4516
|
+
this.config = null;
|
|
4517
|
+
this.mounted = false;
|
|
4518
|
+
}
|
|
4519
|
+
};
|
|
4520
|
+
|
|
4521
|
+
// src/lib/game/zylem-game.ts
|
|
4522
|
+
var ZYLEM_STATE_DISPATCH = "zylem:state:dispatch";
|
|
3722
4523
|
var ZylemGame = class _ZylemGame {
|
|
3723
4524
|
id;
|
|
3724
4525
|
initialGlobals = {};
|
|
@@ -3733,7 +4534,6 @@ var ZylemGame = class _ZylemGame {
|
|
|
3733
4534
|
timer;
|
|
3734
4535
|
inputManager;
|
|
3735
4536
|
wrapperRef;
|
|
3736
|
-
statsRef = null;
|
|
3737
4537
|
defaultCamera = null;
|
|
3738
4538
|
container = null;
|
|
3739
4539
|
canvas = null;
|
|
@@ -3742,6 +4542,10 @@ var ZylemGame = class _ZylemGame {
|
|
|
3742
4542
|
gameCanvas = null;
|
|
3743
4543
|
animationFrameId = null;
|
|
3744
4544
|
isDisposed = false;
|
|
4545
|
+
debugDelegate = null;
|
|
4546
|
+
loadingDelegate = new GameLoadingDelegate();
|
|
4547
|
+
rendererObserver = new GameRendererObserver();
|
|
4548
|
+
eventBusUnsubscribes = [];
|
|
3745
4549
|
static FRAME_LIMIT = 120;
|
|
3746
4550
|
static FRAME_DURATION = 1e3 / _ZylemGame.FRAME_LIMIT;
|
|
3747
4551
|
static MAX_DELTA_SECONDS = 1 / 30;
|
|
@@ -3773,42 +4577,33 @@ var ZylemGame = class _ZylemGame {
|
|
|
3773
4577
|
this.gameCanvas.applyBodyBackground();
|
|
3774
4578
|
this.gameCanvas.mountCanvas();
|
|
3775
4579
|
this.gameCanvas.centerIfFullscreen();
|
|
4580
|
+
this.rendererObserver.setGameCanvas(this.gameCanvas);
|
|
4581
|
+
if (this.resolvedConfig) {
|
|
4582
|
+
this.rendererObserver.setConfig(this.resolvedConfig);
|
|
4583
|
+
}
|
|
4584
|
+
if (this.container) {
|
|
4585
|
+
this.rendererObserver.setContainer(this.container);
|
|
4586
|
+
}
|
|
4587
|
+
this.subscribeToEventBus();
|
|
3776
4588
|
}
|
|
3777
4589
|
loadDebugOptions(options) {
|
|
3778
|
-
|
|
3779
|
-
|
|
3780
|
-
this.statsRef = new Stats();
|
|
3781
|
-
this.statsRef.showPanel(0);
|
|
3782
|
-
this.statsRef.dom.style.position = "absolute";
|
|
3783
|
-
this.statsRef.dom.style.bottom = "0";
|
|
3784
|
-
this.statsRef.dom.style.right = "0";
|
|
3785
|
-
this.statsRef.dom.style.top = "auto";
|
|
3786
|
-
this.statsRef.dom.style.left = "auto";
|
|
3787
|
-
document.body.appendChild(this.statsRef.dom);
|
|
4590
|
+
if (options.debug !== void 0) {
|
|
4591
|
+
debugState.enabled = Boolean(options.debug);
|
|
3788
4592
|
}
|
|
4593
|
+
this.debugDelegate = new GameDebugDelegate();
|
|
3789
4594
|
}
|
|
3790
|
-
|
|
4595
|
+
loadStage(stage, stageIndex = 0) {
|
|
3791
4596
|
this.unloadCurrentStage();
|
|
3792
4597
|
const config = stage.options[0];
|
|
3793
|
-
|
|
3794
|
-
|
|
3795
|
-
|
|
3796
|
-
|
|
3797
|
-
|
|
3798
|
-
|
|
3799
|
-
|
|
3800
|
-
|
|
3801
|
-
|
|
3802
|
-
if (internal) {
|
|
3803
|
-
this.defaultCamera.setPixelRatio(1);
|
|
3804
|
-
this.defaultCamera.resize(internal.width, internal.height);
|
|
3805
|
-
} else {
|
|
3806
|
-
const dpr = window.devicePixelRatio || 1;
|
|
3807
|
-
this.defaultCamera.setPixelRatio(dpr);
|
|
3808
|
-
this.defaultCamera.resize(cssW, cssH);
|
|
3809
|
-
}
|
|
3810
|
-
});
|
|
3811
|
-
}
|
|
4598
|
+
this.loadingDelegate.wireStageLoading(stage, stageIndex);
|
|
4599
|
+
return stage.load(this.id, config?.camera).then(() => {
|
|
4600
|
+
this.stageMap.set(stage.wrappedStage.uuid, stage);
|
|
4601
|
+
this.currentStageId = stage.wrappedStage.uuid;
|
|
4602
|
+
this.defaultCamera = stage.wrappedStage.cameraRef;
|
|
4603
|
+
if (this.defaultCamera) {
|
|
4604
|
+
this.rendererObserver.setCamera(this.defaultCamera);
|
|
4605
|
+
}
|
|
4606
|
+
});
|
|
3812
4607
|
}
|
|
3813
4608
|
unloadCurrentStage() {
|
|
3814
4609
|
if (!this.currentStageId) return;
|
|
@@ -3823,8 +4618,12 @@ var ZylemGame = class _ZylemGame {
|
|
|
3823
4618
|
} catch (e) {
|
|
3824
4619
|
console.error("Failed to destroy previous stage", e);
|
|
3825
4620
|
}
|
|
4621
|
+
current.wrappedStage = null;
|
|
3826
4622
|
}
|
|
3827
4623
|
this.stageMap.delete(this.currentStageId);
|
|
4624
|
+
this.currentStageId = "";
|
|
4625
|
+
this.defaultCamera = null;
|
|
4626
|
+
this.rendererObserver.reset();
|
|
3828
4627
|
}
|
|
3829
4628
|
setGlobals(options) {
|
|
3830
4629
|
this.initialGlobals = { ...options.globals };
|
|
@@ -3859,7 +4658,7 @@ var ZylemGame = class _ZylemGame {
|
|
|
3859
4658
|
this.loop(0);
|
|
3860
4659
|
}
|
|
3861
4660
|
loop(timestamp) {
|
|
3862
|
-
this.
|
|
4661
|
+
this.debugDelegate?.begin();
|
|
3863
4662
|
if (!isPaused()) {
|
|
3864
4663
|
this.timer.update(timestamp);
|
|
3865
4664
|
const stage = this.currentStage();
|
|
@@ -3869,14 +4668,14 @@ var ZylemGame = class _ZylemGame {
|
|
|
3869
4668
|
if (this.customUpdate) {
|
|
3870
4669
|
this.customUpdate(clampedParams);
|
|
3871
4670
|
}
|
|
3872
|
-
if (stage) {
|
|
4671
|
+
if (stage && stage.wrappedStage) {
|
|
3873
4672
|
stage.wrappedStage.nodeUpdate({ ...clampedParams, me: stage.wrappedStage });
|
|
3874
4673
|
}
|
|
3875
4674
|
this.totalTime += clampedParams.delta;
|
|
3876
4675
|
state.time = this.totalTime;
|
|
3877
4676
|
this.previousTimeStamp = timestamp;
|
|
3878
4677
|
}
|
|
3879
|
-
this.
|
|
4678
|
+
this.debugDelegate?.end();
|
|
3880
4679
|
this.outOfLoop();
|
|
3881
4680
|
if (!this.isDisposed) {
|
|
3882
4681
|
this.animationFrameId = requestAnimationFrame(this.loop.bind(this));
|
|
@@ -3889,9 +4688,13 @@ var ZylemGame = class _ZylemGame {
|
|
|
3889
4688
|
this.animationFrameId = null;
|
|
3890
4689
|
}
|
|
3891
4690
|
this.unloadCurrentStage();
|
|
3892
|
-
if (this.
|
|
3893
|
-
this.
|
|
4691
|
+
if (this.debugDelegate) {
|
|
4692
|
+
this.debugDelegate.dispose();
|
|
4693
|
+
this.debugDelegate = null;
|
|
3894
4694
|
}
|
|
4695
|
+
this.eventBusUnsubscribes.forEach((unsub) => unsub());
|
|
4696
|
+
this.eventBusUnsubscribes = [];
|
|
4697
|
+
this.rendererObserver.dispose();
|
|
3895
4698
|
this.timer.dispose();
|
|
3896
4699
|
if (this.customDestroy) {
|
|
3897
4700
|
this.customDestroy({
|
|
@@ -3912,6 +4715,52 @@ var ZylemGame = class _ZylemGame {
|
|
|
3912
4715
|
currentStage() {
|
|
3913
4716
|
return this.getStage(this.currentStageId);
|
|
3914
4717
|
}
|
|
4718
|
+
/**
|
|
4719
|
+
* Subscribe to loading events from the game.
|
|
4720
|
+
* Events include stage context (stageName, stageIndex).
|
|
4721
|
+
* @param callback Invoked for each loading event
|
|
4722
|
+
* @returns Unsubscribe function
|
|
4723
|
+
*/
|
|
4724
|
+
onLoading(callback) {
|
|
4725
|
+
return this.loadingDelegate.onLoading(callback);
|
|
4726
|
+
}
|
|
4727
|
+
/**
|
|
4728
|
+
* Subscribe to the game event bus for stage loading and state events.
|
|
4729
|
+
* Emits window events for cross-application communication.
|
|
4730
|
+
*/
|
|
4731
|
+
subscribeToEventBus() {
|
|
4732
|
+
const emitLoadingWindowEvent = (payload) => {
|
|
4733
|
+
if (typeof window !== "undefined") {
|
|
4734
|
+
const event = {
|
|
4735
|
+
type: payload.type,
|
|
4736
|
+
message: payload.message ?? "",
|
|
4737
|
+
progress: payload.progress ?? 0,
|
|
4738
|
+
current: payload.current,
|
|
4739
|
+
total: payload.total,
|
|
4740
|
+
stageName: payload.stageName,
|
|
4741
|
+
stageIndex: payload.stageIndex
|
|
4742
|
+
};
|
|
4743
|
+
window.dispatchEvent(new CustomEvent(GAME_LOADING_EVENT, { detail: event }));
|
|
4744
|
+
}
|
|
4745
|
+
};
|
|
4746
|
+
const emitStateDispatchEvent = (payload) => {
|
|
4747
|
+
if (typeof window !== "undefined") {
|
|
4748
|
+
const detail = {
|
|
4749
|
+
scope: "game",
|
|
4750
|
+
path: payload.path,
|
|
4751
|
+
value: payload.value,
|
|
4752
|
+
previousValue: payload.previousValue
|
|
4753
|
+
};
|
|
4754
|
+
window.dispatchEvent(new CustomEvent(ZYLEM_STATE_DISPATCH, { detail }));
|
|
4755
|
+
}
|
|
4756
|
+
};
|
|
4757
|
+
this.eventBusUnsubscribes.push(
|
|
4758
|
+
gameEventBus.on("stage:loading:start", emitLoadingWindowEvent),
|
|
4759
|
+
gameEventBus.on("stage:loading:progress", emitLoadingWindowEvent),
|
|
4760
|
+
gameEventBus.on("stage:loading:complete", emitLoadingWindowEvent),
|
|
4761
|
+
gameEventBus.on("game:state:updated", emitStateDispatchEvent)
|
|
4762
|
+
);
|
|
4763
|
+
}
|
|
3915
4764
|
};
|
|
3916
4765
|
|
|
3917
4766
|
// src/lib/game/game.ts
|
|
@@ -4042,7 +4891,7 @@ init_stage();
|
|
|
4042
4891
|
init_entity();
|
|
4043
4892
|
init_builder();
|
|
4044
4893
|
init_create();
|
|
4045
|
-
import { Color as
|
|
4894
|
+
import { Color as Color10, Group as Group5, Sprite as ThreeSprite, SpriteMaterial, CanvasTexture, LinearFilter, Vector2 as Vector28, ClampToEdgeWrapping } from "three";
|
|
4046
4895
|
|
|
4047
4896
|
// src/lib/entities/delegates/debug.ts
|
|
4048
4897
|
import { MeshStandardMaterial as MeshStandardMaterial2, MeshBasicMaterial as MeshBasicMaterial2, MeshPhongMaterial as MeshPhongMaterial2 } from "three";
|
|
@@ -4149,7 +4998,7 @@ var textDefaults = {
|
|
|
4149
4998
|
backgroundColor: null,
|
|
4150
4999
|
padding: 4,
|
|
4151
5000
|
stickToViewport: true,
|
|
4152
|
-
screenPosition: new
|
|
5001
|
+
screenPosition: new Vector28(24, 24),
|
|
4153
5002
|
zDistance: 1
|
|
4154
5003
|
};
|
|
4155
5004
|
var TextBuilder = class extends EntityBuilder {
|
|
@@ -4157,7 +5006,7 @@ var TextBuilder = class extends EntityBuilder {
|
|
|
4157
5006
|
return new ZylemText(options);
|
|
4158
5007
|
}
|
|
4159
5008
|
};
|
|
4160
|
-
var TEXT_TYPE = Symbol("Text");
|
|
5009
|
+
var TEXT_TYPE = /* @__PURE__ */ Symbol("Text");
|
|
4161
5010
|
var ZylemText = class _ZylemText extends GameEntity {
|
|
4162
5011
|
static type = TEXT_TYPE;
|
|
4163
5012
|
_sprite = null;
|
|
@@ -4170,12 +5019,20 @@ var ZylemText = class _ZylemText extends GameEntity {
|
|
|
4170
5019
|
constructor(options) {
|
|
4171
5020
|
super();
|
|
4172
5021
|
this.options = { ...textDefaults, ...options };
|
|
5022
|
+
this.prependSetup(this.textSetup.bind(this));
|
|
5023
|
+
this.prependUpdate(this.textUpdate.bind(this));
|
|
5024
|
+
this.onDestroy(this.textDestroy.bind(this));
|
|
5025
|
+
}
|
|
5026
|
+
create() {
|
|
5027
|
+
this._sprite = null;
|
|
5028
|
+
this._texture = null;
|
|
5029
|
+
this._canvas = null;
|
|
5030
|
+
this._ctx = null;
|
|
5031
|
+
this._lastCanvasW = 0;
|
|
5032
|
+
this._lastCanvasH = 0;
|
|
4173
5033
|
this.group = new Group5();
|
|
4174
5034
|
this.createSprite();
|
|
4175
|
-
|
|
4176
|
-
setup: [this.textSetup.bind(this)],
|
|
4177
|
-
update: [this.textUpdate.bind(this)]
|
|
4178
|
-
};
|
|
5035
|
+
return super.create();
|
|
4179
5036
|
}
|
|
4180
5037
|
createSprite() {
|
|
4181
5038
|
this._canvas = document.createElement("canvas");
|
|
@@ -4253,7 +5110,7 @@ var ZylemText = class _ZylemText extends GameEntity {
|
|
|
4253
5110
|
}
|
|
4254
5111
|
toCssColor(color) {
|
|
4255
5112
|
if (typeof color === "string") return color;
|
|
4256
|
-
const c = color instanceof
|
|
5113
|
+
const c = color instanceof Color10 ? color : new Color10(color);
|
|
4257
5114
|
return `#${c.getHexString()}`;
|
|
4258
5115
|
}
|
|
4259
5116
|
textSetup(params) {
|
|
@@ -4313,7 +5170,7 @@ var ZylemText = class _ZylemText extends GameEntity {
|
|
|
4313
5170
|
if (!this._sprite || !this._cameraRef) return;
|
|
4314
5171
|
const camera = this._cameraRef.camera;
|
|
4315
5172
|
const { width, height } = this.getResolution();
|
|
4316
|
-
const sp = this.options.screenPosition ?? new
|
|
5173
|
+
const sp = this.options.screenPosition ?? new Vector28(24, 24);
|
|
4317
5174
|
const { px, py } = this.getScreenPixels(sp, width, height);
|
|
4318
5175
|
const zDist = Math.max(1e-3, this.options.zDistance ?? 1);
|
|
4319
5176
|
const { worldHalfW, worldHalfH } = this.computeWorldExtents(camera, zDist);
|
|
@@ -4341,6 +5198,24 @@ var ZylemText = class _ZylemText extends GameEntity {
|
|
|
4341
5198
|
sticky: this.options.stickToViewport
|
|
4342
5199
|
};
|
|
4343
5200
|
}
|
|
5201
|
+
/**
|
|
5202
|
+
* Dispose of Three.js resources when the entity is destroyed.
|
|
5203
|
+
*/
|
|
5204
|
+
async textDestroy() {
|
|
5205
|
+
this._texture?.dispose();
|
|
5206
|
+
if (this._sprite?.material) {
|
|
5207
|
+
this._sprite.material.dispose();
|
|
5208
|
+
}
|
|
5209
|
+
if (this._sprite) {
|
|
5210
|
+
this._sprite.removeFromParent();
|
|
5211
|
+
}
|
|
5212
|
+
this.group?.removeFromParent();
|
|
5213
|
+
this._sprite = null;
|
|
5214
|
+
this._texture = null;
|
|
5215
|
+
this._canvas = null;
|
|
5216
|
+
this._ctx = null;
|
|
5217
|
+
this._cameraRef = null;
|
|
5218
|
+
}
|
|
4344
5219
|
};
|
|
4345
5220
|
async function text(...args) {
|
|
4346
5221
|
return createEntity({
|
|
@@ -4358,20 +5233,20 @@ init_builder();
|
|
|
4358
5233
|
init_builder();
|
|
4359
5234
|
init_create();
|
|
4360
5235
|
import { ColliderDesc as ColliderDesc3 } from "@dimforge/rapier3d-compat";
|
|
4361
|
-
import { Color as
|
|
5236
|
+
import { Color as Color11, Euler, Group as Group6, Quaternion as Quaternion3, Vector3 as Vector316 } from "three";
|
|
4362
5237
|
import {
|
|
4363
5238
|
TextureLoader as TextureLoader3,
|
|
4364
5239
|
SpriteMaterial as SpriteMaterial2,
|
|
4365
5240
|
Sprite as ThreeSprite2
|
|
4366
5241
|
} from "three";
|
|
4367
5242
|
var spriteDefaults = {
|
|
4368
|
-
size: new
|
|
4369
|
-
position: new
|
|
5243
|
+
size: new Vector316(1, 1, 1),
|
|
5244
|
+
position: new Vector316(0, 0, 0),
|
|
4370
5245
|
collision: {
|
|
4371
5246
|
static: false
|
|
4372
5247
|
},
|
|
4373
5248
|
material: {
|
|
4374
|
-
color: new
|
|
5249
|
+
color: new Color11("#ffffff"),
|
|
4375
5250
|
shader: "standard"
|
|
4376
5251
|
},
|
|
4377
5252
|
images: [],
|
|
@@ -4379,7 +5254,7 @@ var spriteDefaults = {
|
|
|
4379
5254
|
};
|
|
4380
5255
|
var SpriteCollisionBuilder = class extends EntityCollisionBuilder {
|
|
4381
5256
|
collider(options) {
|
|
4382
|
-
const size = options.collisionSize || options.size || new
|
|
5257
|
+
const size = options.collisionSize || options.size || new Vector316(1, 1, 1);
|
|
4383
5258
|
const half = { x: size.x / 2, y: size.y / 2, z: size.z / 2 };
|
|
4384
5259
|
let colliderDesc = ColliderDesc3.cuboid(half.x, half.y, half.z);
|
|
4385
5260
|
return colliderDesc;
|
|
@@ -4390,7 +5265,7 @@ var SpriteBuilder = class extends EntityBuilder {
|
|
|
4390
5265
|
return new ZylemSprite(options);
|
|
4391
5266
|
}
|
|
4392
5267
|
};
|
|
4393
|
-
var SPRITE_TYPE = Symbol("Sprite");
|
|
5268
|
+
var SPRITE_TYPE = /* @__PURE__ */ Symbol("Sprite");
|
|
4394
5269
|
var ZylemSprite = class _ZylemSprite extends GameEntity {
|
|
4395
5270
|
static type = SPRITE_TYPE;
|
|
4396
5271
|
sprites = [];
|
|
@@ -4404,12 +5279,21 @@ var ZylemSprite = class _ZylemSprite extends GameEntity {
|
|
|
4404
5279
|
constructor(options) {
|
|
4405
5280
|
super();
|
|
4406
5281
|
this.options = { ...spriteDefaults, ...options };
|
|
4407
|
-
this.
|
|
4408
|
-
this.
|
|
4409
|
-
|
|
4410
|
-
|
|
4411
|
-
|
|
4412
|
-
|
|
5282
|
+
this.prependUpdate(this.spriteUpdate.bind(this));
|
|
5283
|
+
this.onDestroy(this.spriteDestroy.bind(this));
|
|
5284
|
+
}
|
|
5285
|
+
create() {
|
|
5286
|
+
this.sprites = [];
|
|
5287
|
+
this.spriteMap.clear();
|
|
5288
|
+
this.animations.clear();
|
|
5289
|
+
this.currentAnimation = null;
|
|
5290
|
+
this.currentAnimationFrame = "";
|
|
5291
|
+
this.currentAnimationIndex = 0;
|
|
5292
|
+
this.currentAnimationTime = 0;
|
|
5293
|
+
this.group = void 0;
|
|
5294
|
+
this.createSpritesFromImages(this.options?.images || []);
|
|
5295
|
+
this.createAnimations(this.options?.animations || []);
|
|
5296
|
+
return super.create();
|
|
4413
5297
|
}
|
|
4414
5298
|
createSpritesFromImages(images) {
|
|
4415
5299
|
const textureLoader = new TextureLoader3();
|
|
@@ -4479,7 +5363,7 @@ var ZylemSprite = class _ZylemSprite extends GameEntity {
|
|
|
4479
5363
|
if (_sprite.material) {
|
|
4480
5364
|
const q = this.body?.rotation();
|
|
4481
5365
|
if (q) {
|
|
4482
|
-
const quat = new
|
|
5366
|
+
const quat = new Quaternion3(q.x, q.y, q.z, q.w);
|
|
4483
5367
|
const euler = new Euler().setFromQuaternion(quat, "XYZ");
|
|
4484
5368
|
_sprite.material.rotation = euler.z;
|
|
4485
5369
|
}
|
|
@@ -4563,12 +5447,15 @@ init_game_state();
|
|
|
4563
5447
|
var Game = class {
|
|
4564
5448
|
wrappedGame = null;
|
|
4565
5449
|
options;
|
|
4566
|
-
|
|
4567
|
-
|
|
4568
|
-
|
|
4569
|
-
|
|
4570
|
-
|
|
4571
|
-
|
|
5450
|
+
// Lifecycle callback arrays
|
|
5451
|
+
setupCallbacks = [];
|
|
5452
|
+
updateCallbacks = [];
|
|
5453
|
+
destroyCallbacks = [];
|
|
5454
|
+
pendingLoadingCallbacks = [];
|
|
5455
|
+
// Game-scoped global change subscriptions
|
|
5456
|
+
globalChangeCallbacks = [];
|
|
5457
|
+
globalChangesCallbacks = [];
|
|
5458
|
+
activeGlobalSubscriptions = [];
|
|
4572
5459
|
refErrorMessage = "lost reference to game";
|
|
4573
5460
|
constructor(options) {
|
|
4574
5461
|
this.options = options;
|
|
@@ -4580,10 +5467,29 @@ var Game = class {
|
|
|
4580
5467
|
initGlobals(globals);
|
|
4581
5468
|
}
|
|
4582
5469
|
}
|
|
5470
|
+
// Fluent API for adding lifecycle callbacks
|
|
5471
|
+
onSetup(...callbacks) {
|
|
5472
|
+
this.setupCallbacks.push(...callbacks);
|
|
5473
|
+
return this;
|
|
5474
|
+
}
|
|
5475
|
+
onUpdate(...callbacks) {
|
|
5476
|
+
this.updateCallbacks.push(...callbacks);
|
|
5477
|
+
return this;
|
|
5478
|
+
}
|
|
5479
|
+
onDestroy(...callbacks) {
|
|
5480
|
+
this.destroyCallbacks.push(...callbacks);
|
|
5481
|
+
return this;
|
|
5482
|
+
}
|
|
4583
5483
|
async start() {
|
|
5484
|
+
resetGlobals();
|
|
5485
|
+
const globals = extractGlobalsFromOptions(this.options);
|
|
5486
|
+
if (globals) {
|
|
5487
|
+
initGlobals(globals);
|
|
5488
|
+
}
|
|
4584
5489
|
const game = await this.load();
|
|
4585
5490
|
this.wrappedGame = game;
|
|
4586
5491
|
this.setOverrides();
|
|
5492
|
+
this.registerGlobalSubscriptions();
|
|
4587
5493
|
game.start();
|
|
4588
5494
|
return this;
|
|
4589
5495
|
}
|
|
@@ -4594,6 +5500,9 @@ var Game = class {
|
|
|
4594
5500
|
...options,
|
|
4595
5501
|
...resolved
|
|
4596
5502
|
}, this);
|
|
5503
|
+
for (const callback of this.pendingLoadingCallbacks) {
|
|
5504
|
+
game.onLoading(callback);
|
|
5505
|
+
}
|
|
4597
5506
|
await game.loadStage(options.stages[0]);
|
|
4598
5507
|
return game;
|
|
4599
5508
|
}
|
|
@@ -4602,9 +5511,59 @@ var Game = class {
|
|
|
4602
5511
|
console.error(this.refErrorMessage);
|
|
4603
5512
|
return;
|
|
4604
5513
|
}
|
|
4605
|
-
this.wrappedGame.customSetup =
|
|
4606
|
-
|
|
4607
|
-
|
|
5514
|
+
this.wrappedGame.customSetup = (params) => {
|
|
5515
|
+
this.setupCallbacks.forEach((cb) => cb(params));
|
|
5516
|
+
};
|
|
5517
|
+
this.wrappedGame.customUpdate = (params) => {
|
|
5518
|
+
this.updateCallbacks.forEach((cb) => cb(params));
|
|
5519
|
+
};
|
|
5520
|
+
this.wrappedGame.customDestroy = (params) => {
|
|
5521
|
+
this.destroyCallbacks.forEach((cb) => cb(params));
|
|
5522
|
+
};
|
|
5523
|
+
}
|
|
5524
|
+
/**
|
|
5525
|
+
* Subscribe to changes on a global value. Subscriptions are registered
|
|
5526
|
+
* when the game starts and cleaned up when disposed.
|
|
5527
|
+
* The callback receives the value and the current stage.
|
|
5528
|
+
* @example game.onGlobalChange('score', (val, stage) => console.log(val));
|
|
5529
|
+
*/
|
|
5530
|
+
onGlobalChange(path, callback) {
|
|
5531
|
+
this.globalChangeCallbacks.push({ path, callback });
|
|
5532
|
+
return this;
|
|
5533
|
+
}
|
|
5534
|
+
/**
|
|
5535
|
+
* Subscribe to changes on multiple global paths. Subscriptions are registered
|
|
5536
|
+
* when the game starts and cleaned up when disposed.
|
|
5537
|
+
* The callback receives the values and the current stage.
|
|
5538
|
+
* @example game.onGlobalChanges(['score', 'lives'], ([score, lives], stage) => console.log(score, lives));
|
|
5539
|
+
*/
|
|
5540
|
+
onGlobalChanges(paths, callback) {
|
|
5541
|
+
this.globalChangesCallbacks.push({ paths, callback });
|
|
5542
|
+
return this;
|
|
5543
|
+
}
|
|
5544
|
+
/**
|
|
5545
|
+
* Register all stored global change callbacks.
|
|
5546
|
+
* Called internally during start().
|
|
5547
|
+
*/
|
|
5548
|
+
registerGlobalSubscriptions() {
|
|
5549
|
+
for (const { path, callback } of this.globalChangeCallbacks) {
|
|
5550
|
+
const unsub = onGlobalChange(path, (value) => {
|
|
5551
|
+
callback(value, this.getCurrentStage());
|
|
5552
|
+
});
|
|
5553
|
+
this.activeGlobalSubscriptions.push(unsub);
|
|
5554
|
+
}
|
|
5555
|
+
for (const { paths, callback } of this.globalChangesCallbacks) {
|
|
5556
|
+
const unsub = onGlobalChanges(paths, (values) => {
|
|
5557
|
+
callback(values, this.getCurrentStage());
|
|
5558
|
+
});
|
|
5559
|
+
this.activeGlobalSubscriptions.push(unsub);
|
|
5560
|
+
}
|
|
5561
|
+
}
|
|
5562
|
+
/**
|
|
5563
|
+
* Get the current stage wrapper.
|
|
5564
|
+
*/
|
|
5565
|
+
getCurrentStage() {
|
|
5566
|
+
return this.wrappedGame?.currentStage() ?? null;
|
|
4608
5567
|
}
|
|
4609
5568
|
async pause() {
|
|
4610
5569
|
setPaused(true);
|
|
@@ -4623,19 +5582,19 @@ var Game = class {
|
|
|
4623
5582
|
}
|
|
4624
5583
|
await this.wrappedGame.loadStage(this.wrappedGame.stages[0]);
|
|
4625
5584
|
}
|
|
4626
|
-
|
|
5585
|
+
previousStage() {
|
|
4627
5586
|
if (!this.wrappedGame) {
|
|
4628
5587
|
console.error(this.refErrorMessage);
|
|
4629
5588
|
return;
|
|
4630
5589
|
}
|
|
4631
5590
|
const currentStageId = this.wrappedGame.currentStageId;
|
|
4632
|
-
const currentIndex = this.wrappedGame.stages.findIndex((s) => s.wrappedStage
|
|
5591
|
+
const currentIndex = this.wrappedGame.stages.findIndex((s) => s.wrappedStage?.uuid === currentStageId);
|
|
4633
5592
|
const previousStage = this.wrappedGame.stages[currentIndex - 1];
|
|
4634
5593
|
if (!previousStage) {
|
|
4635
5594
|
console.error("previous stage called on first stage");
|
|
4636
5595
|
return;
|
|
4637
5596
|
}
|
|
4638
|
-
|
|
5597
|
+
this.wrappedGame.loadStage(previousStage);
|
|
4639
5598
|
}
|
|
4640
5599
|
async loadStageFromId(stageId) {
|
|
4641
5600
|
if (!this.wrappedGame) {
|
|
@@ -4651,39 +5610,61 @@ var Game = class {
|
|
|
4651
5610
|
console.error(`Failed to load stage ${stageId}`, e);
|
|
4652
5611
|
}
|
|
4653
5612
|
}
|
|
4654
|
-
|
|
5613
|
+
nextStage() {
|
|
4655
5614
|
if (!this.wrappedGame) {
|
|
4656
5615
|
console.error(this.refErrorMessage);
|
|
4657
5616
|
return;
|
|
4658
5617
|
}
|
|
4659
5618
|
if (stageState2.next) {
|
|
4660
5619
|
const nextId = stageState2.next.id;
|
|
4661
|
-
|
|
5620
|
+
StageManager.transitionForward(nextId);
|
|
4662
5621
|
if (stageState2.current) {
|
|
4663
|
-
|
|
4664
|
-
|
|
5622
|
+
StageFactory.createFromBlueprint(stageState2.current).then((stage) => {
|
|
5623
|
+
this.wrappedGame?.loadStage(stage);
|
|
5624
|
+
});
|
|
4665
5625
|
return;
|
|
4666
5626
|
}
|
|
4667
5627
|
}
|
|
4668
5628
|
const currentStageId = this.wrappedGame.currentStageId;
|
|
4669
|
-
const currentIndex = this.wrappedGame.stages.findIndex((s) => s.wrappedStage
|
|
5629
|
+
const currentIndex = this.wrappedGame.stages.findIndex((s) => s.wrappedStage?.uuid === currentStageId);
|
|
4670
5630
|
const nextStage = this.wrappedGame.stages[currentIndex + 1];
|
|
4671
5631
|
if (!nextStage) {
|
|
4672
5632
|
console.error("next stage called on last stage");
|
|
4673
5633
|
return;
|
|
4674
5634
|
}
|
|
4675
|
-
|
|
5635
|
+
this.wrappedGame.loadStage(nextStage);
|
|
4676
5636
|
}
|
|
4677
5637
|
async goToStage() {
|
|
4678
5638
|
}
|
|
4679
5639
|
async end() {
|
|
4680
5640
|
}
|
|
4681
5641
|
dispose() {
|
|
5642
|
+
for (const unsub of this.activeGlobalSubscriptions) {
|
|
5643
|
+
unsub();
|
|
5644
|
+
}
|
|
5645
|
+
this.activeGlobalSubscriptions = [];
|
|
4682
5646
|
if (this.wrappedGame) {
|
|
4683
5647
|
this.wrappedGame.dispose();
|
|
4684
5648
|
}
|
|
5649
|
+
clearGlobalSubscriptions();
|
|
5650
|
+
resetGlobals();
|
|
4685
5651
|
}
|
|
5652
|
+
/**
|
|
5653
|
+
* Subscribe to loading events from the game.
|
|
5654
|
+
* Events include stage context (stageName, stageIndex).
|
|
5655
|
+
* @param callback Invoked for each loading event
|
|
5656
|
+
* @returns Unsubscribe function
|
|
5657
|
+
*/
|
|
4686
5658
|
onLoading(callback) {
|
|
5659
|
+
if (this.wrappedGame) {
|
|
5660
|
+
return this.wrappedGame.onLoading(callback);
|
|
5661
|
+
}
|
|
5662
|
+
this.pendingLoadingCallbacks.push(callback);
|
|
5663
|
+
return () => {
|
|
5664
|
+
this.pendingLoadingCallbacks = this.pendingLoadingCallbacks.filter((c) => c !== callback);
|
|
5665
|
+
if (this.wrappedGame) {
|
|
5666
|
+
}
|
|
5667
|
+
};
|
|
4687
5668
|
}
|
|
4688
5669
|
};
|
|
4689
5670
|
function createGame(...options) {
|
|
@@ -4692,7 +5673,7 @@ function createGame(...options) {
|
|
|
4692
5673
|
|
|
4693
5674
|
// src/lib/core/vessel.ts
|
|
4694
5675
|
init_base_node();
|
|
4695
|
-
var VESSEL_TYPE = Symbol("vessel");
|
|
5676
|
+
var VESSEL_TYPE = /* @__PURE__ */ Symbol("vessel");
|
|
4696
5677
|
var Vessel = class extends BaseNode {
|
|
4697
5678
|
static type = VESSEL_TYPE;
|
|
4698
5679
|
_setup(_params) {
|