@skewedaspect/sage 0.3.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/Readme.md +53 -0
- package/dist/classes/bindings/toggle.d.ts +122 -0
- package/dist/classes/bindings/trigger.d.ts +79 -0
- package/dist/classes/bindings/value.d.ts +104 -0
- package/dist/classes/entity.d.ts +83 -0
- package/dist/classes/eventBus.d.ts +94 -0
- package/dist/classes/gameEngine.d.ts +57 -0
- package/dist/classes/input/gamepad.d.ts +94 -0
- package/dist/classes/input/keyboard.d.ts +66 -0
- package/dist/classes/input/mouse.d.ts +80 -0
- package/dist/classes/input/readers/gamepad.d.ts +77 -0
- package/dist/classes/input/readers/keyboard.d.ts +60 -0
- package/dist/classes/input/readers/mouse.d.ts +45 -0
- package/dist/classes/loggers/consoleBackend.d.ts +29 -0
- package/dist/classes/loggers/nullBackend.d.ts +14 -0
- package/dist/engines/scene.d.ts +11 -0
- package/dist/interfaces/action.d.ts +20 -0
- package/dist/interfaces/binding.d.ts +144 -0
- package/dist/interfaces/entity.d.ts +9 -0
- package/dist/interfaces/game.d.ts +26 -0
- package/dist/interfaces/input.d.ts +181 -0
- package/dist/interfaces/logger.d.ts +88 -0
- package/dist/managers/binding.d.ts +185 -0
- package/dist/managers/entity.d.ts +70 -0
- package/dist/managers/game.d.ts +20 -0
- package/dist/managers/input.d.ts +56 -0
- package/dist/managers/level.d.ts +55 -0
- package/dist/sage.d.ts +20 -0
- package/dist/sage.es.js +2208 -0
- package/dist/sage.es.js.map +1 -0
- package/dist/sage.umd.js +2 -0
- package/dist/sage.umd.js.map +1 -0
- package/dist/utils/capabilities.d.ts +2 -0
- package/dist/utils/graphics.d.ts +10 -0
- package/dist/utils/logger.d.ts +66 -0
- package/dist/utils/physics.d.ts +2 -0
- package/dist/utils/version.d.ts +5 -0
- package/docs/architecture.md +129 -0
- package/docs/behaviors.md +706 -0
- package/docs/binding_system.md +820 -0
- package/docs/design/input.md +86 -0
- package/docs/entity_system.md +538 -0
- package/docs/eventbus.md +225 -0
- package/docs/getting_started.md +264 -0
- package/docs/images/sage_logo.png +0 -0
- package/docs/images/sage_logo_shape.png +0 -0
- package/docs/overview.md +38 -0
- package/docs/physics_system.md +686 -0
- package/docs/scene_system.md +513 -0
- package/package.json +69 -0
- package/src/classes/bindings/toggle.ts +261 -0
- package/src/classes/bindings/trigger.ts +211 -0
- package/src/classes/bindings/value.ts +227 -0
- package/src/classes/entity.ts +256 -0
- package/src/classes/eventBus.ts +259 -0
- package/src/classes/gameEngine.ts +125 -0
- package/src/classes/input/gamepad.ts +388 -0
- package/src/classes/input/keyboard.ts +189 -0
- package/src/classes/input/mouse.ts +276 -0
- package/src/classes/input/readers/gamepad.ts +179 -0
- package/src/classes/input/readers/keyboard.ts +123 -0
- package/src/classes/input/readers/mouse.ts +133 -0
- package/src/classes/loggers/consoleBackend.ts +135 -0
- package/src/classes/loggers/nullBackend.ts +51 -0
- package/src/engines/scene.ts +112 -0
- package/src/images/sage_logo.svg +172 -0
- package/src/images/sage_logo_shape.svg +146 -0
- package/src/interfaces/action.ts +30 -0
- package/src/interfaces/binding.ts +191 -0
- package/src/interfaces/entity.ts +21 -0
- package/src/interfaces/game.ts +44 -0
- package/src/interfaces/input.ts +221 -0
- package/src/interfaces/logger.ts +118 -0
- package/src/managers/binding.ts +729 -0
- package/src/managers/entity.ts +252 -0
- package/src/managers/game.ts +111 -0
- package/src/managers/input.ts +233 -0
- package/src/managers/level.ts +261 -0
- package/src/sage.ts +119 -0
- package/src/types/global.d.ts +11 -0
- package/src/utils/capabilities.ts +16 -0
- package/src/utils/graphics.ts +148 -0
- package/src/utils/logger.ts +225 -0
- package/src/utils/physics.ts +16 -0
- package/src/utils/version.ts +11 -0
package/dist/sage.es.js
ADDED
|
@@ -0,0 +1,2208 @@
|
|
|
1
|
+
var I = Object.defineProperty;
|
|
2
|
+
var k = (a, e, t) => e in a ? I(a, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : a[e] = t;
|
|
3
|
+
var n = (a, e, t) => k(a, typeof e != "symbol" ? e + "" : e, t);
|
|
4
|
+
import { FreeCamera as V, Vector3 as b, HemisphericLight as G, MeshBuilder as D, PhysicsAggregate as E, PhysicsShapeType as C, Scene as L, NullEngine as B, WebGPUEngine as K, Engine as U, HavokPlugin as R } from "@babylonjs/core";
|
|
5
|
+
import z from "@babylonjs/havok";
|
|
6
|
+
const m = "0.3.0";
|
|
7
|
+
class T {
|
|
8
|
+
/**
|
|
9
|
+
* Creates an instance of SkewedAspectGameEngine.
|
|
10
|
+
* @param canvas - The game canvas.
|
|
11
|
+
* @param renderEngine - The rendering engine.
|
|
12
|
+
* @param physics - The physics engine.
|
|
13
|
+
* @param eventBus - The event bus.
|
|
14
|
+
* @param logger - The logging utility.
|
|
15
|
+
* @param engines - The engines used in the game.
|
|
16
|
+
* @param managers - The managers used in the game.
|
|
17
|
+
*/
|
|
18
|
+
constructor(e, t, s, i, o, r, c) {
|
|
19
|
+
n(this, "canvas");
|
|
20
|
+
n(this, "renderEngine");
|
|
21
|
+
n(this, "physics");
|
|
22
|
+
n(this, "managers");
|
|
23
|
+
n(this, "engines");
|
|
24
|
+
n(this, "eventBus");
|
|
25
|
+
n(this, "logger");
|
|
26
|
+
n(this, "_log");
|
|
27
|
+
this.canvas = e, this.renderEngine = t, this.physics = s, this.eventBus = i, this.logger = o, this.engines = r, this.managers = c, this._log = o.getLogger("GameEngine");
|
|
28
|
+
}
|
|
29
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
30
|
+
// Public API
|
|
31
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
32
|
+
/**
|
|
33
|
+
* Starts the game engine.
|
|
34
|
+
*/
|
|
35
|
+
async start() {
|
|
36
|
+
this._log.info(`Starting SkewedAspect Game Engine (Version: ${m})...`), await this.managers.gameManager.start(this.canvas), this._log.info("Game engine started successfully");
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Stops the game engine.
|
|
40
|
+
*/
|
|
41
|
+
async stop() {
|
|
42
|
+
this._log.info(`Stopping SkewedAspect Game Engine (Version: ${m})...`), await this.managers.gameManager.stop(), this._log.info("Game engine stopped successfully");
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
class W {
|
|
46
|
+
constructor() {
|
|
47
|
+
n(this, "timers");
|
|
48
|
+
this.timers = /* @__PURE__ */ new Map();
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Get the CSS style for a specific log level
|
|
52
|
+
*/
|
|
53
|
+
getStyleForLevel(e) {
|
|
54
|
+
const t = {
|
|
55
|
+
trace: "color: #999999",
|
|
56
|
+
// Dim gray
|
|
57
|
+
debug: "color: #00AAAA",
|
|
58
|
+
// Cyan
|
|
59
|
+
info: "color: #00AA00",
|
|
60
|
+
// Green
|
|
61
|
+
warn: "color: #AAAA00",
|
|
62
|
+
// Yellow
|
|
63
|
+
error: "color: #AA0000",
|
|
64
|
+
// Red
|
|
65
|
+
timer: "color: #AA00AA",
|
|
66
|
+
// Magenta
|
|
67
|
+
none: "color: inherit"
|
|
68
|
+
// Default
|
|
69
|
+
};
|
|
70
|
+
return t[e] || t.none;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Format a log message with category, timestamp and log level
|
|
74
|
+
*/
|
|
75
|
+
formatMessage(e, t) {
|
|
76
|
+
const s = /* @__PURE__ */ new Date(), i = s.getHours() % 12 || 12, o = s.getMinutes().toString().padStart(2, "0"), r = s.getSeconds().toString().padStart(2, "0"), c = s.getHours() >= 12 ? "PM" : "AM", h = `[${i}:${o}:${r} ${c}]`, u = t.toUpperCase();
|
|
77
|
+
return [
|
|
78
|
+
`${h} %c${u}%c (${e}): `,
|
|
79
|
+
// Format string with placeholders
|
|
80
|
+
this.getStyleForLevel(t),
|
|
81
|
+
// Level style
|
|
82
|
+
"",
|
|
83
|
+
// Default style
|
|
84
|
+
""
|
|
85
|
+
// Reset style
|
|
86
|
+
];
|
|
87
|
+
}
|
|
88
|
+
trace(e, t, ...s) {
|
|
89
|
+
const [i, o, r, c] = this.formatMessage(e, "trace");
|
|
90
|
+
console.debug(i, o, r, c, t, ...s);
|
|
91
|
+
}
|
|
92
|
+
debug(e, t, ...s) {
|
|
93
|
+
const [i, o, r, c] = this.formatMessage(e, "debug");
|
|
94
|
+
console.debug(i, o, r, c, t, ...s);
|
|
95
|
+
}
|
|
96
|
+
info(e, t, ...s) {
|
|
97
|
+
const [i, o, r, c] = this.formatMessage(e, "info");
|
|
98
|
+
console.info(i, o, r, c, t, ...s);
|
|
99
|
+
}
|
|
100
|
+
warn(e, t, ...s) {
|
|
101
|
+
const [i, o, r, c] = this.formatMessage(e, "warn");
|
|
102
|
+
console.warn(i, o, r, c, t, ...s);
|
|
103
|
+
}
|
|
104
|
+
error(e, t, ...s) {
|
|
105
|
+
const [i, o, r, c] = this.formatMessage(e, "error");
|
|
106
|
+
console.error(i, o, r, c, t, ...s);
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Start a timer with the specified label.
|
|
110
|
+
*/
|
|
111
|
+
time(e, t) {
|
|
112
|
+
const s = `${e}:${t}`;
|
|
113
|
+
this.timers.set(s, performance.now());
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* End a timer and log the elapsed time.
|
|
117
|
+
*/
|
|
118
|
+
timeEnd(e, t) {
|
|
119
|
+
const s = `${e}:${t}`, i = this.timers.get(s);
|
|
120
|
+
if (i !== void 0) {
|
|
121
|
+
const o = performance.now() - i;
|
|
122
|
+
this.timers.delete(s);
|
|
123
|
+
const [r, c, h, u] = this.formatMessage(e, "timer");
|
|
124
|
+
console.info(`${r} Timer '${t}' completed in ${o.toFixed(2)}ms`, c, h, u);
|
|
125
|
+
} else
|
|
126
|
+
console.warn(`[${e}]: Timer '${t}' does not exist`);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
class P {
|
|
130
|
+
trace(e, t, ...s) {
|
|
131
|
+
}
|
|
132
|
+
debug(e, t, ...s) {
|
|
133
|
+
}
|
|
134
|
+
info(e, t, ...s) {
|
|
135
|
+
}
|
|
136
|
+
warn(e, t, ...s) {
|
|
137
|
+
}
|
|
138
|
+
error(e, t, ...s) {
|
|
139
|
+
}
|
|
140
|
+
time(e, t) {
|
|
141
|
+
}
|
|
142
|
+
timeEnd(e, t) {
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
class l {
|
|
146
|
+
constructor(e, t = "none", s = new P()) {
|
|
147
|
+
n(this, "category");
|
|
148
|
+
n(this, "backend");
|
|
149
|
+
n(this, "minLevel");
|
|
150
|
+
this.category = e, this.backend = s, this.minLevel = t;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Update the logger's backend and minimum level
|
|
154
|
+
*/
|
|
155
|
+
updateSettings(e, t) {
|
|
156
|
+
this.backend = e, this.minLevel = t;
|
|
157
|
+
}
|
|
158
|
+
trace(e, ...t) {
|
|
159
|
+
this.shouldLog("trace") && this.backend.trace(this.category, e, ...t);
|
|
160
|
+
}
|
|
161
|
+
debug(e, ...t) {
|
|
162
|
+
this.shouldLog("debug") && this.backend.debug(this.category, e, ...t);
|
|
163
|
+
}
|
|
164
|
+
info(e, ...t) {
|
|
165
|
+
this.shouldLog("info") && this.backend.info(this.category, e, ...t);
|
|
166
|
+
}
|
|
167
|
+
warn(e, ...t) {
|
|
168
|
+
this.shouldLog("warn") && this.backend.warn(this.category, e, ...t);
|
|
169
|
+
}
|
|
170
|
+
error(e, ...t) {
|
|
171
|
+
this.shouldLog("error") && this.backend.error(this.category, e, ...t);
|
|
172
|
+
}
|
|
173
|
+
time(e) {
|
|
174
|
+
this.minLevel !== "none" && this.backend.time(this.category, e);
|
|
175
|
+
}
|
|
176
|
+
timeEnd(e) {
|
|
177
|
+
this.minLevel !== "none" && this.backend.timeEnd(this.category, e);
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Determine if a message at the given level should be logged based on the current minimum level.
|
|
181
|
+
*/
|
|
182
|
+
shouldLog(e) {
|
|
183
|
+
if (this.minLevel === "none")
|
|
184
|
+
return !1;
|
|
185
|
+
switch (this.minLevel) {
|
|
186
|
+
case "trace":
|
|
187
|
+
return !0;
|
|
188
|
+
case "debug":
|
|
189
|
+
return e !== "trace";
|
|
190
|
+
case "info":
|
|
191
|
+
return e === "info" || e === "warn" || e === "error";
|
|
192
|
+
case "warn":
|
|
193
|
+
return e === "warn" || e === "error";
|
|
194
|
+
case "error":
|
|
195
|
+
return e === "error";
|
|
196
|
+
default:
|
|
197
|
+
return !1;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
class F {
|
|
202
|
+
/**
|
|
203
|
+
* Create a new LoggingUtility.
|
|
204
|
+
*
|
|
205
|
+
* @param level - The minimum logging level (defaults to INFO)
|
|
206
|
+
* @param backend - The logging backend to use (defaults to ConsoleBackend)
|
|
207
|
+
*/
|
|
208
|
+
constructor(e = "debug", t = new W()) {
|
|
209
|
+
n(this, "backend");
|
|
210
|
+
n(this, "level");
|
|
211
|
+
n(this, "loggers");
|
|
212
|
+
this.backend = t, this.level = e, this.loggers = /* @__PURE__ */ new Map();
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Set the logging backend.
|
|
216
|
+
*
|
|
217
|
+
* @param backend - The logging backend to use
|
|
218
|
+
*/
|
|
219
|
+
setBackend(e) {
|
|
220
|
+
this.backend = e, this.loggers.forEach((t) => {
|
|
221
|
+
t instanceof l && t.updateSettings(this.backend, this.level);
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Set the minimum logging level.
|
|
226
|
+
*
|
|
227
|
+
* @param level - The minimum logging level to display
|
|
228
|
+
*/
|
|
229
|
+
setLevel(e) {
|
|
230
|
+
this.level = e, this.loggers.forEach((t) => {
|
|
231
|
+
t instanceof l && t.updateSettings(this.backend, this.level);
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Get the current minimum logging level.
|
|
236
|
+
*/
|
|
237
|
+
getLevel() {
|
|
238
|
+
return this.level;
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Get a logger for the specified category.
|
|
242
|
+
*
|
|
243
|
+
* @param category - The category for this logger (typically class/module name)
|
|
244
|
+
* @returns A Logger instance for the specified category
|
|
245
|
+
*/
|
|
246
|
+
getLogger(e) {
|
|
247
|
+
this.loggers.has(e) || this.loggers.set(e, new l(e, this.level, this.backend));
|
|
248
|
+
const t = this.loggers.get(e);
|
|
249
|
+
if (t === void 0)
|
|
250
|
+
throw new Error(`Failed to create logger for category: ${e}`);
|
|
251
|
+
return t;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
class O {
|
|
255
|
+
/**
|
|
256
|
+
* Creates a new GameEventBus instance
|
|
257
|
+
*
|
|
258
|
+
* @param logger - The logging utility to use
|
|
259
|
+
*/
|
|
260
|
+
constructor(e) {
|
|
261
|
+
/**
|
|
262
|
+
* For exact event-type matches:
|
|
263
|
+
* - Key = event type string (e.g. "input:keydown")
|
|
264
|
+
* - Value = set of subscriptions for that exact type
|
|
265
|
+
*/
|
|
266
|
+
n(this, "directMap", /* @__PURE__ */ new Map());
|
|
267
|
+
/**
|
|
268
|
+
* For pattern-based (wildcard or RegExp) subscriptions.
|
|
269
|
+
*/
|
|
270
|
+
n(this, "patternSubs", /* @__PURE__ */ new Set());
|
|
271
|
+
/**
|
|
272
|
+
* Logger instance
|
|
273
|
+
*/
|
|
274
|
+
n(this, "_log");
|
|
275
|
+
this._log = (e == null ? void 0 : e.getLogger("EventBus")) || new l("EventBus");
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Subscribe with automatic detection:
|
|
279
|
+
* - If `eventTypeOrPattern` is a `RegExp` or a string containing `*`, treat it as a pattern subscription.
|
|
280
|
+
* - Otherwise, treat it as an exact subscription.
|
|
281
|
+
*
|
|
282
|
+
* @template P - The payload shape you want to expect in the callback.
|
|
283
|
+
* @param {string | RegExp} eventTypeOrPattern - The exact event type or a wildcard/RegExp pattern.
|
|
284
|
+
* @param {GameEventCallback<P>} callback - A callback expecting a `GameEvent<P>`.
|
|
285
|
+
* @returns {Unsubscribe} function that, when called, removes this subscription.
|
|
286
|
+
*/
|
|
287
|
+
subscribe(e, t) {
|
|
288
|
+
return this._log.trace(`Subscribe request for: ${e.toString()}`), e instanceof RegExp || typeof e == "string" && e.includes("*") ? this.subscribePattern(e, t) : this.subscribeExact(e, t);
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Subscribes to an exact event type (e.g. "input:keydown").
|
|
292
|
+
*
|
|
293
|
+
* @template P - The payload shape you want to expect in the callback.
|
|
294
|
+
* @param {string} eventType - The exact string to match.
|
|
295
|
+
* @param {GameEventCallback<P>} callback - A callback expecting a `GameEvent<P>`.
|
|
296
|
+
* @returns {Unsubscribe} function that unsubscribes this exact subscription.
|
|
297
|
+
*/
|
|
298
|
+
subscribeExact(e, t) {
|
|
299
|
+
this._log.debug(`Adding exact subscription for: ${e}`);
|
|
300
|
+
let s = this.directMap.get(e);
|
|
301
|
+
s || (s = /* @__PURE__ */ new Set(), this.directMap.set(e, s));
|
|
302
|
+
const i = {
|
|
303
|
+
callback: (o) => t(o)
|
|
304
|
+
// runtime "type-lie" cast
|
|
305
|
+
};
|
|
306
|
+
return s.add(i), () => {
|
|
307
|
+
this._log.debug(`Removing exact subscription for: ${e}`), s.delete(i), s.size === 0 && this.directMap.delete(e);
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Subscribes with either:
|
|
312
|
+
* - a wildcard string (contains `*`) that gets converted to a RegExp
|
|
313
|
+
* - a RegExp directly
|
|
314
|
+
*
|
|
315
|
+
* @template P - The payload shape you want to expect in the callback.
|
|
316
|
+
* @param {string | RegExp} patternOrRegExp - The pattern to match (`"input:*"` or `/^input:/`).
|
|
317
|
+
* @param {GameEventCallback<P>} callback - A callback expecting a `GameEvent<P>`.
|
|
318
|
+
* @returns {Unsubscribe} function that unsubscribes this pattern-based subscription.
|
|
319
|
+
*/
|
|
320
|
+
subscribePattern(e, t) {
|
|
321
|
+
let s;
|
|
322
|
+
if (typeof e == "string") {
|
|
323
|
+
const o = e.replace(/[-/\\^$+?.()|[\]{}]/g, "\\$&").replace(/\*/g, ".*");
|
|
324
|
+
s = new RegExp(`^${o}$`), this._log.debug(`Adding pattern subscription for string: ${e}, regex: ${s.toString()}`);
|
|
325
|
+
} else
|
|
326
|
+
s = e, this._log.debug(`Adding pattern subscription for regex: ${s.toString()}`);
|
|
327
|
+
const i = {
|
|
328
|
+
pattern: s,
|
|
329
|
+
callback: (o) => t(o)
|
|
330
|
+
};
|
|
331
|
+
return this.patternSubs.add(i), () => {
|
|
332
|
+
this._log.debug(`Removing pattern subscription: ${s.toString()}`), this.patternSubs.delete(i);
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* Publishes an event, invoking all matching callbacks.
|
|
337
|
+
*
|
|
338
|
+
* Callbacks are called asynchronously (microtask) via `Promise.resolve()`.
|
|
339
|
+
*
|
|
340
|
+
* @param {GameEvent<any>} event - The event object to publish.
|
|
341
|
+
*/
|
|
342
|
+
publish(e) {
|
|
343
|
+
this._log.trace(`Publishing event: ${e.type}`, e);
|
|
344
|
+
let t = 0;
|
|
345
|
+
const s = this.directMap.get(e.type);
|
|
346
|
+
if (s) {
|
|
347
|
+
t += s.size;
|
|
348
|
+
for (const i of s)
|
|
349
|
+
Promise.resolve().then(() => i.callback(e));
|
|
350
|
+
}
|
|
351
|
+
for (const i of this.patternSubs)
|
|
352
|
+
i.pattern && i.pattern.test(e.type) && (t++, Promise.resolve().then(() => i.callback(e)));
|
|
353
|
+
t === 0 ? this._log.debug(`No subscribers found for event: ${e.type}`) : this._log.trace(`Event ${e.type} dispatched to ${t} subscribers`);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
class N {
|
|
357
|
+
constructor(e, t, s) {
|
|
358
|
+
n(this, "_engine");
|
|
359
|
+
n(this, "_physics");
|
|
360
|
+
n(this, "_log");
|
|
361
|
+
this._engine = e, this._physics = t, this._log = (s == null ? void 0 : s.getLogger("SceneEngine")) || new l("SceneEngine");
|
|
362
|
+
}
|
|
363
|
+
async _buildDemoScene(e, t) {
|
|
364
|
+
this._log.debug("Building demo scene..."), this._log.trace("Creating camera...");
|
|
365
|
+
const s = new V("camera1", new b(0, 5, -10), e);
|
|
366
|
+
s.setTarget(b.Zero()), s.attachControl(t, !0), this._log.trace("Creating light...");
|
|
367
|
+
const i = new G("light", new b(0, 1, 0), e);
|
|
368
|
+
i.intensity = 0.7, this._log.trace("Creating sphere...");
|
|
369
|
+
const o = D.CreateSphere("sphere", { diameter: 2, segments: 32 }, e);
|
|
370
|
+
o.position.y = 4, this._log.trace("Creating ground...");
|
|
371
|
+
const r = D.CreateGround("ground", { width: 10, height: 10 }, e);
|
|
372
|
+
this._log.trace("Adding physics to sphere..."), new E(o, C.SPHERE, { mass: 1, restitution: 0.75 }, e), this._log.trace("Adding physics to ground..."), new E(r, C.BOX, { mass: 0 }, e), this._log.debug("Demo scene built successfully");
|
|
373
|
+
}
|
|
374
|
+
// TODO: This needs more logic to handle different scenes
|
|
375
|
+
async loadScene(e) {
|
|
376
|
+
this._log.info("Loading scene..."), this._log.time("sceneLoad"), this._log.debug("Creating new scene...");
|
|
377
|
+
const t = new L(this._engine);
|
|
378
|
+
return this._log.debug("Enabling physics with gravity (0, -9.8, 0)..."), t.enablePhysics(new b(0, -9.8, 0), this._physics), await this._buildDemoScene(t, e), this._log.timeEnd("sceneLoad"), this._log.info("Scene loaded successfully"), t;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
const J = [
|
|
382
|
+
"trigger",
|
|
383
|
+
"toggle",
|
|
384
|
+
"value"
|
|
385
|
+
];
|
|
386
|
+
class Y {
|
|
387
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
388
|
+
/**
|
|
389
|
+
* Create a new trigger binding
|
|
390
|
+
*
|
|
391
|
+
* @param action - Action to trigger
|
|
392
|
+
* @param deviceID - Device ID this binding is for
|
|
393
|
+
* @param reader - Input reader to read values from
|
|
394
|
+
* @param options - Binding configuration options
|
|
395
|
+
*/
|
|
396
|
+
constructor(e, t, s, i = {}) {
|
|
397
|
+
n(this, "type", "trigger");
|
|
398
|
+
n(this, "action");
|
|
399
|
+
n(this, "context");
|
|
400
|
+
n(this, "deviceID");
|
|
401
|
+
n(this, "reader");
|
|
402
|
+
// Trigger-specific options
|
|
403
|
+
n(this, "_edgeMode");
|
|
404
|
+
n(this, "_threshold");
|
|
405
|
+
// State tracking
|
|
406
|
+
n(this, "_lastDigitalState", !1);
|
|
407
|
+
this.action = e, this.deviceID = t, this.reader = s, this.context = i.context, this._edgeMode = i.edgeMode ?? "rising", this._threshold = i.threshold ?? 0.5;
|
|
408
|
+
}
|
|
409
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
410
|
+
// Public Getters
|
|
411
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
412
|
+
/**
|
|
413
|
+
* Get the current options for this trigger binding.
|
|
414
|
+
*
|
|
415
|
+
* @returns The current options for this trigger binding
|
|
416
|
+
*/
|
|
417
|
+
get options() {
|
|
418
|
+
return {
|
|
419
|
+
edgeMode: this._edgeMode,
|
|
420
|
+
threshold: this._threshold
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
424
|
+
// Public Methods
|
|
425
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
426
|
+
/**
|
|
427
|
+
* Process input state and emit an action if triggered
|
|
428
|
+
*
|
|
429
|
+
* @param state - Current input state
|
|
430
|
+
* @param eventBus - Event bus to emit action events to
|
|
431
|
+
* @returns True if an action was triggered, false otherwise
|
|
432
|
+
*/
|
|
433
|
+
process(e, t) {
|
|
434
|
+
const s = this.reader.getValue(e) ?? !1, i = typeof s == "boolean" ? s : s >= this._threshold;
|
|
435
|
+
let o = !1;
|
|
436
|
+
switch (this._edgeMode) {
|
|
437
|
+
case "rising":
|
|
438
|
+
o = i && !this._lastDigitalState;
|
|
439
|
+
break;
|
|
440
|
+
case "falling":
|
|
441
|
+
o = !i && this._lastDigitalState;
|
|
442
|
+
break;
|
|
443
|
+
case "both":
|
|
444
|
+
o = i !== this._lastDigitalState;
|
|
445
|
+
break;
|
|
446
|
+
}
|
|
447
|
+
if (this._lastDigitalState = i, o) {
|
|
448
|
+
let r;
|
|
449
|
+
if (this.action.type === "analog") {
|
|
450
|
+
let h = typeof s == "number" ? s : s ? 1 : 0;
|
|
451
|
+
if (this.action.minValue !== void 0 || this.action.maxValue !== void 0) {
|
|
452
|
+
const u = this.action.minValue ?? 0, g = this.action.maxValue ?? 1;
|
|
453
|
+
h = u + h * (g - u);
|
|
454
|
+
}
|
|
455
|
+
r = h;
|
|
456
|
+
} else
|
|
457
|
+
r = !0;
|
|
458
|
+
const c = {
|
|
459
|
+
type: `action:${this.action.name}`,
|
|
460
|
+
payload: {
|
|
461
|
+
value: r,
|
|
462
|
+
deviceId: this.deviceID,
|
|
463
|
+
context: this.context
|
|
464
|
+
}
|
|
465
|
+
};
|
|
466
|
+
t.publish(c);
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
/**
|
|
470
|
+
* Returns a JSON-serializable representation of this trigger binding
|
|
471
|
+
*
|
|
472
|
+
* @returns A simple object representation that can be converted to JSON
|
|
473
|
+
*/
|
|
474
|
+
toJSON() {
|
|
475
|
+
return {
|
|
476
|
+
type: this.type,
|
|
477
|
+
action: this.action.name,
|
|
478
|
+
input: {
|
|
479
|
+
deviceID: this.deviceID,
|
|
480
|
+
...this.reader.toJSON()
|
|
481
|
+
},
|
|
482
|
+
context: this.context,
|
|
483
|
+
options: this.options
|
|
484
|
+
};
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
class H {
|
|
488
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
489
|
+
/**
|
|
490
|
+
* Create a new toggle binding
|
|
491
|
+
*
|
|
492
|
+
* @param action - Action to toggle
|
|
493
|
+
* @param deviceID - Device ID this binding is for
|
|
494
|
+
* @param reader - Input reader to read values from
|
|
495
|
+
* @param options - Binding configuration options
|
|
496
|
+
*/
|
|
497
|
+
constructor(e, t, s, i = {}) {
|
|
498
|
+
n(this, "type", "toggle");
|
|
499
|
+
n(this, "action");
|
|
500
|
+
n(this, "context");
|
|
501
|
+
n(this, "deviceID");
|
|
502
|
+
n(this, "reader");
|
|
503
|
+
// Toggle-specific options
|
|
504
|
+
n(this, "_invert");
|
|
505
|
+
n(this, "_threshold");
|
|
506
|
+
n(this, "_initialState");
|
|
507
|
+
n(this, "_onValue");
|
|
508
|
+
n(this, "_offValue");
|
|
509
|
+
// State tracking
|
|
510
|
+
n(this, "_lastDigitalState", !1);
|
|
511
|
+
n(this, "_toggleState");
|
|
512
|
+
this.action = e, this.deviceID = t, this.reader = s, this.context = i.context, this._invert = i.invert ?? !1, this._threshold = i.threshold ?? 0.5, this._initialState = i.initialState ?? !1, this._toggleState = this._initialState, this._onValue = i.onValue ?? !0, this._offValue = i.offValue ?? !1;
|
|
513
|
+
}
|
|
514
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
515
|
+
// Public Getters
|
|
516
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
517
|
+
/**
|
|
518
|
+
* Get the current toggle state
|
|
519
|
+
*
|
|
520
|
+
* @returns Current toggle state (true = on, false = off)
|
|
521
|
+
*/
|
|
522
|
+
get state() {
|
|
523
|
+
return this._toggleState;
|
|
524
|
+
}
|
|
525
|
+
/**
|
|
526
|
+
* Set the toggle state directly (useful for programmatic control)
|
|
527
|
+
*
|
|
528
|
+
* @param value - New toggle state
|
|
529
|
+
*/
|
|
530
|
+
set state(e) {
|
|
531
|
+
this._toggleState = e;
|
|
532
|
+
}
|
|
533
|
+
/**
|
|
534
|
+
* Get the value emitted when toggle is in the "on" state
|
|
535
|
+
*
|
|
536
|
+
* @returns The value for the "on" state
|
|
537
|
+
*/
|
|
538
|
+
get onValue() {
|
|
539
|
+
return this._onValue;
|
|
540
|
+
}
|
|
541
|
+
/**
|
|
542
|
+
* Get the value emitted when toggle is in the "off" state
|
|
543
|
+
*
|
|
544
|
+
* @returns The value for the "off" state
|
|
545
|
+
*/
|
|
546
|
+
get offValue() {
|
|
547
|
+
return this._offValue;
|
|
548
|
+
}
|
|
549
|
+
/**
|
|
550
|
+
* Get the current value based on toggle state
|
|
551
|
+
*
|
|
552
|
+
* @returns The current value (boolean or number)
|
|
553
|
+
*/
|
|
554
|
+
get value() {
|
|
555
|
+
return this.action.type === "analog" ? this._toggleState ? this.action.maxValue ?? 1 : this.action.minValue ?? 0 : this._toggleState ? this._onValue : this._offValue;
|
|
556
|
+
}
|
|
557
|
+
/**
|
|
558
|
+
* Get the current options for this toggle binding.
|
|
559
|
+
*
|
|
560
|
+
* @returns The current options for this toggle binding
|
|
561
|
+
*/
|
|
562
|
+
get options() {
|
|
563
|
+
return {
|
|
564
|
+
invert: this._invert,
|
|
565
|
+
threshold: this._threshold,
|
|
566
|
+
initialState: this._initialState,
|
|
567
|
+
onValue: this._onValue,
|
|
568
|
+
offValue: this._offValue
|
|
569
|
+
};
|
|
570
|
+
}
|
|
571
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
572
|
+
// Public Methods
|
|
573
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
574
|
+
/**
|
|
575
|
+
* Process input state and emit a digital action if toggled
|
|
576
|
+
*
|
|
577
|
+
* @param state - Current input state
|
|
578
|
+
* @param eventBus - Event bus to emit action events to
|
|
579
|
+
*/
|
|
580
|
+
process(e, t) {
|
|
581
|
+
const s = this.reader.getValue(e) ?? !1, i = typeof s == "boolean" ? s : s >= this._threshold, o = this._invert ? !i && this._lastDigitalState : i && !this._lastDigitalState;
|
|
582
|
+
this._lastDigitalState = i, o && (this._toggleState = !this._toggleState, t.publish({
|
|
583
|
+
type: `action:${this.action.name}`,
|
|
584
|
+
payload: {
|
|
585
|
+
value: this.value,
|
|
586
|
+
deviceId: this.deviceID,
|
|
587
|
+
context: this.context
|
|
588
|
+
}
|
|
589
|
+
}));
|
|
590
|
+
}
|
|
591
|
+
/**
|
|
592
|
+
* Reset toggle to its initial state
|
|
593
|
+
*/
|
|
594
|
+
reset() {
|
|
595
|
+
this._toggleState = this._initialState;
|
|
596
|
+
}
|
|
597
|
+
/**
|
|
598
|
+
* Returns a JSON-serializable representation of this toggle binding
|
|
599
|
+
*
|
|
600
|
+
* @returns A simple object representation that can be converted to JSON
|
|
601
|
+
*/
|
|
602
|
+
toJSON() {
|
|
603
|
+
return {
|
|
604
|
+
type: this.type,
|
|
605
|
+
action: this.action.name,
|
|
606
|
+
input: {
|
|
607
|
+
deviceID: this.deviceID,
|
|
608
|
+
...this.reader.toJSON()
|
|
609
|
+
},
|
|
610
|
+
state: this._toggleState,
|
|
611
|
+
context: this.context,
|
|
612
|
+
options: this.options
|
|
613
|
+
};
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
class X {
|
|
617
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
618
|
+
/**
|
|
619
|
+
* Create a new value binding
|
|
620
|
+
*
|
|
621
|
+
* @param action - Action to emit values for
|
|
622
|
+
* @param deviceID - Device ID this binding is for
|
|
623
|
+
* @param reader - Input reader to read values from
|
|
624
|
+
* @param options - Binding configuration options
|
|
625
|
+
*/
|
|
626
|
+
constructor(e, t, s, i = {}) {
|
|
627
|
+
n(this, "type", "value");
|
|
628
|
+
n(this, "action");
|
|
629
|
+
n(this, "context");
|
|
630
|
+
n(this, "deviceID");
|
|
631
|
+
n(this, "reader");
|
|
632
|
+
// Value-specific options
|
|
633
|
+
n(this, "_scale");
|
|
634
|
+
n(this, "_offset");
|
|
635
|
+
n(this, "_invert");
|
|
636
|
+
n(this, "_emitOnChange");
|
|
637
|
+
n(this, "_deadzone");
|
|
638
|
+
n(this, "_onValue");
|
|
639
|
+
n(this, "_offValue");
|
|
640
|
+
n(this, "_min");
|
|
641
|
+
n(this, "_max");
|
|
642
|
+
// State tracking
|
|
643
|
+
n(this, "_lastValue");
|
|
644
|
+
this.action = e, this.deviceID = t, this.reader = s, this.context = i.context, this._scale = i.scale ?? 1, this._offset = i.offset ?? 0, this._invert = i.invert ?? !1, this._emitOnChange = i.emitOnChange ?? !0, this._deadzone = i.deadzone ?? 0, this._onValue = i.onValue ?? !0, this._offValue = i.offValue ?? !1;
|
|
645
|
+
const o = this.action.type === "analog" ? this.action.minValue ?? Number.NEGATIVE_INFINITY : Number.NEGATIVE_INFINITY;
|
|
646
|
+
this._min = i.min ?? o;
|
|
647
|
+
const r = this.action.type === "analog" ? this.action.maxValue ?? Number.POSITIVE_INFINITY : Number.POSITIVE_INFINITY;
|
|
648
|
+
this._max = i.max ?? r;
|
|
649
|
+
}
|
|
650
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
651
|
+
/**
|
|
652
|
+
* Get the current options for this value binding.
|
|
653
|
+
*
|
|
654
|
+
* @returns The current options for this value binding
|
|
655
|
+
*/
|
|
656
|
+
get options() {
|
|
657
|
+
return {
|
|
658
|
+
scale: this._scale,
|
|
659
|
+
offset: this._offset,
|
|
660
|
+
invert: this._invert,
|
|
661
|
+
emitOnChange: this._emitOnChange,
|
|
662
|
+
deadzone: this._deadzone,
|
|
663
|
+
onValue: this._onValue,
|
|
664
|
+
offValue: this._offValue,
|
|
665
|
+
min: this._min,
|
|
666
|
+
max: this._max
|
|
667
|
+
};
|
|
668
|
+
}
|
|
669
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
670
|
+
// Public Methods
|
|
671
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
672
|
+
/**
|
|
673
|
+
* Process input state and emit an action value
|
|
674
|
+
*
|
|
675
|
+
* @param state - Current input state
|
|
676
|
+
* @param eventBus - Event bus to emit action events to
|
|
677
|
+
* @returns True if a value was emitted, false otherwise
|
|
678
|
+
*/
|
|
679
|
+
process(e, t) {
|
|
680
|
+
const s = this.reader.getValue(e);
|
|
681
|
+
if (s === void 0)
|
|
682
|
+
return;
|
|
683
|
+
const i = typeof s == "boolean" ? s ? 1 : 0 : s;
|
|
684
|
+
if (i === void 0)
|
|
685
|
+
return;
|
|
686
|
+
let o = this._deadzone > 0 && Math.abs(i) < this._deadzone ? 0 : i;
|
|
687
|
+
o = (this._invert ? -o : o) * this._scale + this._offset, o = Math.max(this._min, Math.min(this._max, o)), !(this._emitOnChange && this._lastValue === o) && (this._lastValue = o, t.publish({
|
|
688
|
+
type: `action:${this.action.name}`,
|
|
689
|
+
payload: {
|
|
690
|
+
value: o,
|
|
691
|
+
deviceId: this.deviceID,
|
|
692
|
+
context: this.context
|
|
693
|
+
}
|
|
694
|
+
}));
|
|
695
|
+
}
|
|
696
|
+
/**
|
|
697
|
+
* Returns a JSON-serializable representation of this value binding
|
|
698
|
+
*
|
|
699
|
+
* @returns A simple object representation that can be converted to JSON
|
|
700
|
+
*/
|
|
701
|
+
toJSON() {
|
|
702
|
+
return {
|
|
703
|
+
type: this.type,
|
|
704
|
+
action: this.action.name,
|
|
705
|
+
input: {
|
|
706
|
+
deviceID: this.deviceID,
|
|
707
|
+
...this.reader.toJSON()
|
|
708
|
+
},
|
|
709
|
+
context: this.context,
|
|
710
|
+
options: this.options
|
|
711
|
+
};
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
class w {
|
|
715
|
+
/**
|
|
716
|
+
* Creates a new KeyboardValueReader
|
|
717
|
+
*
|
|
718
|
+
* @param keyCode - The key code to monitor (e.g., "KeyA", "Space")
|
|
719
|
+
* @param options - Configuration options
|
|
720
|
+
*/
|
|
721
|
+
constructor(e, t = {}) {
|
|
722
|
+
/**
|
|
723
|
+
* The type of keyboard input (always 'key')
|
|
724
|
+
*/
|
|
725
|
+
n(this, "sourceType", "key");
|
|
726
|
+
/**
|
|
727
|
+
* The key code to monitor (e.g., "KeyA", "Space")
|
|
728
|
+
*/
|
|
729
|
+
n(this, "sourceKey");
|
|
730
|
+
/**
|
|
731
|
+
* Whether to use delta state instead of current state
|
|
732
|
+
*/
|
|
733
|
+
n(this, "useDelta");
|
|
734
|
+
this.sourceKey = e, this.useDelta = t.useDelta || !1;
|
|
735
|
+
}
|
|
736
|
+
/**
|
|
737
|
+
* Gets the value of the key state
|
|
738
|
+
*
|
|
739
|
+
* @param state - The input state
|
|
740
|
+
* @returns The key state value or undefined if the state type is not keyboard
|
|
741
|
+
*/
|
|
742
|
+
getValue(e) {
|
|
743
|
+
if (e.type !== "keyboard")
|
|
744
|
+
return;
|
|
745
|
+
const t = e;
|
|
746
|
+
return this.useDelta ? t.delta[this.sourceKey] : t.keys[this.sourceKey];
|
|
747
|
+
}
|
|
748
|
+
/**
|
|
749
|
+
* Creates a KeyboardValueReader from a string representation
|
|
750
|
+
*
|
|
751
|
+
* @param sourceKey - The key code (e.g., "KeyA", "Space")
|
|
752
|
+
* @param options - Optional configuration
|
|
753
|
+
* @returns A new KeyboardValueReader instance
|
|
754
|
+
*/
|
|
755
|
+
static fromString(e, t = {}) {
|
|
756
|
+
return new w(e, t);
|
|
757
|
+
}
|
|
758
|
+
/**
|
|
759
|
+
* Returns a JSON-serializable representation of this keyboard value reader
|
|
760
|
+
*
|
|
761
|
+
* @returns A simple object representation that can be converted to JSON
|
|
762
|
+
*/
|
|
763
|
+
toJSON() {
|
|
764
|
+
return {
|
|
765
|
+
type: "keyboard",
|
|
766
|
+
sourceType: this.sourceType,
|
|
767
|
+
sourceKey: this.sourceKey,
|
|
768
|
+
options: {
|
|
769
|
+
useDelta: this.useDelta
|
|
770
|
+
}
|
|
771
|
+
};
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
class x {
|
|
775
|
+
/**
|
|
776
|
+
* Creates a new MouseValueReader
|
|
777
|
+
*
|
|
778
|
+
* @param sourceType - Type of mouse input to monitor (button, position, wheel)
|
|
779
|
+
* @param sourceKey - The specific key for this input type
|
|
780
|
+
*/
|
|
781
|
+
constructor(e, t) {
|
|
782
|
+
/**
|
|
783
|
+
* The type of mouse input
|
|
784
|
+
*/
|
|
785
|
+
n(this, "sourceType");
|
|
786
|
+
/**
|
|
787
|
+
* The specific key for this input
|
|
788
|
+
*/
|
|
789
|
+
n(this, "sourceKey");
|
|
790
|
+
this.sourceType = e, this.sourceKey = t;
|
|
791
|
+
}
|
|
792
|
+
/**
|
|
793
|
+
* Gets the value of the mouse input source
|
|
794
|
+
*
|
|
795
|
+
* @param state - The current input state
|
|
796
|
+
* @returns The value of the input source
|
|
797
|
+
*/
|
|
798
|
+
getValue(e) {
|
|
799
|
+
if (e.type === "mouse")
|
|
800
|
+
switch (this.sourceType) {
|
|
801
|
+
case "button": {
|
|
802
|
+
const t = e.buttons[this.sourceKey];
|
|
803
|
+
return t ? t.pressed : void 0;
|
|
804
|
+
}
|
|
805
|
+
case "position": {
|
|
806
|
+
const [t, s] = this.sourceKey.split(":");
|
|
807
|
+
return !t || !s || t !== "absolute" && t !== "relative" || s !== "x" && s !== "y" ? void 0 : e.position[t][s];
|
|
808
|
+
}
|
|
809
|
+
case "wheel": {
|
|
810
|
+
const t = this.sourceKey === "deltaX" || this.sourceKey === "deltaY" || this.sourceKey === "deltaZ";
|
|
811
|
+
return e.wheel && t ? e.wheel[this.sourceKey] : void 0;
|
|
812
|
+
}
|
|
813
|
+
default:
|
|
814
|
+
return;
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
/**
|
|
818
|
+
* Creates a MouseValueReader from a string representation
|
|
819
|
+
*
|
|
820
|
+
* @param sourceTypeString - String in format "sourceType:sourceKey" (e.g., "button:0", "position:absolute:x")
|
|
821
|
+
* @returns A new MouseValueReader instance
|
|
822
|
+
*/
|
|
823
|
+
static fromString(e) {
|
|
824
|
+
const [t, ...s] = e.split(":"), i = s.join(":");
|
|
825
|
+
if (!t || !i)
|
|
826
|
+
throw new Error(`Invalid mouse source format: ${e}`);
|
|
827
|
+
return new x(t, i);
|
|
828
|
+
}
|
|
829
|
+
/**
|
|
830
|
+
* Returns a JSON-serializable representation of this mouse value reader
|
|
831
|
+
*
|
|
832
|
+
* @returns A simple object representation that can be converted to JSON
|
|
833
|
+
*/
|
|
834
|
+
toJSON() {
|
|
835
|
+
return {
|
|
836
|
+
type: "mouse",
|
|
837
|
+
sourceType: this.sourceType,
|
|
838
|
+
sourceKey: this.sourceKey
|
|
839
|
+
};
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
class S {
|
|
843
|
+
/**
|
|
844
|
+
* Creates a new GamepadValueReader
|
|
845
|
+
*
|
|
846
|
+
* @param sourceType - Type of gamepad input to monitor (button or axis)
|
|
847
|
+
* @param sourceKey - The specific key for this input type
|
|
848
|
+
* @param options - Configuration options
|
|
849
|
+
*/
|
|
850
|
+
constructor(e, t, s = {}) {
|
|
851
|
+
/**
|
|
852
|
+
* The type of gamepad input
|
|
853
|
+
*/
|
|
854
|
+
n(this, "sourceType");
|
|
855
|
+
/**
|
|
856
|
+
* The specific key for this input
|
|
857
|
+
*/
|
|
858
|
+
n(this, "sourceKey");
|
|
859
|
+
/**
|
|
860
|
+
* Whether to use analog value for buttons
|
|
861
|
+
*/
|
|
862
|
+
n(this, "useAnalogValue");
|
|
863
|
+
/**
|
|
864
|
+
* Deadzone value for axes
|
|
865
|
+
*/
|
|
866
|
+
n(this, "deadzone");
|
|
867
|
+
/**
|
|
868
|
+
* Whether to invert axis values
|
|
869
|
+
*/
|
|
870
|
+
n(this, "invert");
|
|
871
|
+
this.sourceType = e, this.sourceKey = t, this.useAnalogValue = s.useAnalogValue || !1, this.deadzone = s.deadzone || 0.1, this.invert = s.invert || !1;
|
|
872
|
+
}
|
|
873
|
+
/**
|
|
874
|
+
* Gets the value of the input source based on the current state
|
|
875
|
+
*
|
|
876
|
+
* @param state - The current input state
|
|
877
|
+
* @returns The value of the input source
|
|
878
|
+
*/
|
|
879
|
+
getValue(e) {
|
|
880
|
+
if (e.type === "gamepad")
|
|
881
|
+
switch (this.sourceType) {
|
|
882
|
+
case "button": {
|
|
883
|
+
const t = e.buttons[this.sourceKey];
|
|
884
|
+
return t ? this.useAnalogValue ? t.value : t.pressed : void 0;
|
|
885
|
+
}
|
|
886
|
+
case "axis": {
|
|
887
|
+
let t = e.axes[this.sourceKey];
|
|
888
|
+
return t === void 0 ? void 0 : (Math.abs(t) < this.deadzone && (t = 0), this.invert ? -t : t);
|
|
889
|
+
}
|
|
890
|
+
default:
|
|
891
|
+
return;
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
/**
|
|
895
|
+
* Creates a GamepadValueReader from a string representation
|
|
896
|
+
*
|
|
897
|
+
* @param sourceTypeString - String in format "sourceType:sourceKey" (e.g., "button:0", "axis:1")
|
|
898
|
+
* @param options - Optional configuration options
|
|
899
|
+
* @returns A new GamepadValueReader instance
|
|
900
|
+
*/
|
|
901
|
+
static fromString(e, t = {}) {
|
|
902
|
+
const [s, i] = e.split(":");
|
|
903
|
+
if (!s || !i)
|
|
904
|
+
throw new Error(`Invalid gamepad source format: ${e}`);
|
|
905
|
+
return new S(s, i, t);
|
|
906
|
+
}
|
|
907
|
+
/**
|
|
908
|
+
* Returns a JSON-serializable representation of this gamepad value reader
|
|
909
|
+
*
|
|
910
|
+
* @returns A simple object representation that can be converted to JSON
|
|
911
|
+
*/
|
|
912
|
+
toJSON() {
|
|
913
|
+
return {
|
|
914
|
+
type: "gamepad",
|
|
915
|
+
sourceType: this.sourceType,
|
|
916
|
+
sourceKey: this.sourceKey,
|
|
917
|
+
options: {
|
|
918
|
+
useAnalogValue: this.useAnalogValue,
|
|
919
|
+
deadzone: this.deadzone,
|
|
920
|
+
invert: this.invert
|
|
921
|
+
}
|
|
922
|
+
};
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
class j {
|
|
926
|
+
/**
|
|
927
|
+
* Creates an instance of BindingManager.
|
|
928
|
+
*
|
|
929
|
+
* @param eventBus - The game event bus to publish events to
|
|
930
|
+
* @param logger - The logging utility to use
|
|
931
|
+
*/
|
|
932
|
+
constructor(e, t) {
|
|
933
|
+
/** Map of device IDs to their bindings */
|
|
934
|
+
n(this, "_bindings", /* @__PURE__ */ new Map());
|
|
935
|
+
/** Map of action names to their action definitions */
|
|
936
|
+
n(this, "_actions", /* @__PURE__ */ new Map());
|
|
937
|
+
/** Map of all registered contexts by name for O(1) lookup */
|
|
938
|
+
n(this, "_contexts", /* @__PURE__ */ new Map());
|
|
939
|
+
/**
|
|
940
|
+
* Set of all active contexts (both exclusive and non-exclusive)
|
|
941
|
+
*/
|
|
942
|
+
n(this, "_activeContexts", /* @__PURE__ */ new Set());
|
|
943
|
+
/** Event bus for handling game events */
|
|
944
|
+
n(this, "_eventBus");
|
|
945
|
+
/** Logger instance */
|
|
946
|
+
n(this, "_log");
|
|
947
|
+
this._eventBus = e, this._eventBus.subscribe("input:changed", (s) => {
|
|
948
|
+
s.payload && this.$handleInput(s.payload.device, s.payload.state);
|
|
949
|
+
}), this._log = (t == null ? void 0 : t.getLogger("BindingManager")) || new l("BindingManager"), this._log.debug("BindingManager initialized");
|
|
950
|
+
}
|
|
951
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
952
|
+
// Private Methods
|
|
953
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
954
|
+
/**
|
|
955
|
+
* Checks if a binding's context is currently active
|
|
956
|
+
*
|
|
957
|
+
* @param binding - The binding to check
|
|
958
|
+
* @returns True if binding's context is active or has no context
|
|
959
|
+
*/
|
|
960
|
+
_isBindingContextActive(e) {
|
|
961
|
+
return e.context ? this._activeContexts.has(e.context) : !0;
|
|
962
|
+
}
|
|
963
|
+
/**
|
|
964
|
+
* Get a context by name, creating it if it doesn't exist
|
|
965
|
+
*
|
|
966
|
+
* @param contextName - The name of the context to get or create
|
|
967
|
+
* @param exclusive - Whether the context is exclusive (only used if creating)
|
|
968
|
+
* @returns The context object
|
|
969
|
+
*/
|
|
970
|
+
_getOrCreateContext(e, t = !0) {
|
|
971
|
+
let s = this._contexts.get(e);
|
|
972
|
+
return s || (s = {
|
|
973
|
+
name: e,
|
|
974
|
+
exclusive: t
|
|
975
|
+
}, this._contexts.set(e, s), this._log.debug(`Auto-created context "${e}" (exclusive: ${t})`)), s;
|
|
976
|
+
}
|
|
977
|
+
/**
|
|
978
|
+
* Deactivate all exclusive contexts except the specified one
|
|
979
|
+
*
|
|
980
|
+
* @param exceptContextName - Name of context to not deactivate
|
|
981
|
+
* @returns Array of deactivated context names
|
|
982
|
+
*/
|
|
983
|
+
_deactivateExclusiveContexts(e) {
|
|
984
|
+
const t = [];
|
|
985
|
+
for (const s of this._activeContexts) {
|
|
986
|
+
if (s === e)
|
|
987
|
+
continue;
|
|
988
|
+
const i = this._contexts.get(s);
|
|
989
|
+
i != null && i.exclusive && (this._activeContexts.delete(s), t.push(s));
|
|
990
|
+
}
|
|
991
|
+
return t;
|
|
992
|
+
}
|
|
993
|
+
/**
|
|
994
|
+
* Create a binding from a binding definition
|
|
995
|
+
*
|
|
996
|
+
* @param definition - The binding definition to create a binding from
|
|
997
|
+
* @returns A new binding instance or null if the type is not supported
|
|
998
|
+
*/
|
|
999
|
+
_createBindingFromDefinition(e) {
|
|
1000
|
+
const t = this._actions.get(e.action);
|
|
1001
|
+
if (!t)
|
|
1002
|
+
return this._log.warn(`Cannot create binding: Action "${e.action}" not found.`), null;
|
|
1003
|
+
const { deviceID: s, ...i } = e.input;
|
|
1004
|
+
switch (e.type) {
|
|
1005
|
+
case "trigger":
|
|
1006
|
+
return new Y(t, s, this._createInputSourceFromDefinition(i), {
|
|
1007
|
+
...e.options || {},
|
|
1008
|
+
context: e.context
|
|
1009
|
+
});
|
|
1010
|
+
case "toggle":
|
|
1011
|
+
return new H(t, s, this._createInputSourceFromDefinition(i), {
|
|
1012
|
+
...e.options || {},
|
|
1013
|
+
context: e.context
|
|
1014
|
+
});
|
|
1015
|
+
case "value":
|
|
1016
|
+
return new X(t, s, this._createInputSourceFromDefinition(i), {
|
|
1017
|
+
...e.options || {},
|
|
1018
|
+
context: e.context
|
|
1019
|
+
});
|
|
1020
|
+
default:
|
|
1021
|
+
return this._log.error(`Binding type not implemented: ${e.type}`), null;
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
/**
|
|
1025
|
+
* Create a device value reader from a definition object
|
|
1026
|
+
*
|
|
1027
|
+
* @param definition - The device value reader definition
|
|
1028
|
+
* @returns A new device value reader instance
|
|
1029
|
+
* @throws Error if the reader type is not supported
|
|
1030
|
+
*/
|
|
1031
|
+
_createInputSourceFromDefinition(e) {
|
|
1032
|
+
switch (e.type) {
|
|
1033
|
+
case "keyboard":
|
|
1034
|
+
return new w(e.sourceKey, e.options);
|
|
1035
|
+
case "mouse": {
|
|
1036
|
+
const t = e.sourceType;
|
|
1037
|
+
if (!(t === "button" || t === "position" || t === "wheel"))
|
|
1038
|
+
throw new Error(`Invalid mouse source type: ${t}`);
|
|
1039
|
+
return new x(t, e.sourceKey);
|
|
1040
|
+
}
|
|
1041
|
+
case "gamepad": {
|
|
1042
|
+
const t = e.sourceType;
|
|
1043
|
+
if (!(t === "button" || t === "axis"))
|
|
1044
|
+
throw new Error(`Invalid gamepad source type: ${t}`);
|
|
1045
|
+
return new S(t, e.sourceKey, e.options);
|
|
1046
|
+
}
|
|
1047
|
+
default:
|
|
1048
|
+
throw new Error(`Unsupported input source type: ${e.type}`);
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
1052
|
+
// Internal API
|
|
1053
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
1054
|
+
/**
|
|
1055
|
+
* Handle input from a device and process all relevant bindings
|
|
1056
|
+
*
|
|
1057
|
+
* @param device - The input device
|
|
1058
|
+
* @param state - Current input state
|
|
1059
|
+
*/
|
|
1060
|
+
$handleInput(e, t) {
|
|
1061
|
+
const s = this._bindings.get(e.id);
|
|
1062
|
+
if (!(!s || s.length === 0) && !(this._activeContexts.size === 0 && s.some((i) => i.context)))
|
|
1063
|
+
for (const i of s)
|
|
1064
|
+
this._isBindingContextActive(i) && i.process(t, this._eventBus);
|
|
1065
|
+
}
|
|
1066
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
1067
|
+
// Action Management API
|
|
1068
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
1069
|
+
/**
|
|
1070
|
+
* Registers an action
|
|
1071
|
+
*
|
|
1072
|
+
* @param action - The action to register
|
|
1073
|
+
* @throws Error if action is already registered
|
|
1074
|
+
*/
|
|
1075
|
+
registerAction(e) {
|
|
1076
|
+
if (this._log.debug(`Registering action "${e.name}"`), this._actions.has(e.name)) {
|
|
1077
|
+
const t = `Action "${e.name}" already registered.`;
|
|
1078
|
+
throw this._log.error(t), new Error(t);
|
|
1079
|
+
}
|
|
1080
|
+
this._actions.set(e.name, e), this._log.debug(`Action "${e.name}" registered successfully`);
|
|
1081
|
+
}
|
|
1082
|
+
/**
|
|
1083
|
+
* Gets an action by name
|
|
1084
|
+
*
|
|
1085
|
+
* @param actionName - The name of the action to get
|
|
1086
|
+
* @returns The action or null if not found
|
|
1087
|
+
*/
|
|
1088
|
+
getAction(e) {
|
|
1089
|
+
this._log.trace(`Getting action "${e}"`);
|
|
1090
|
+
const t = this._actions.get(e) ?? null;
|
|
1091
|
+
return t || this._log.debug(`Action "${e}" not found`), t;
|
|
1092
|
+
}
|
|
1093
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
1094
|
+
// Context Management API
|
|
1095
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
1096
|
+
/**
|
|
1097
|
+
* Registers a context with specific options
|
|
1098
|
+
*
|
|
1099
|
+
* @param contextName - The name of the context to register
|
|
1100
|
+
* @param exclusive - Whether the context is exclusive (default: true)
|
|
1101
|
+
* @returns The registered context
|
|
1102
|
+
*/
|
|
1103
|
+
registerContext(e, t = !0) {
|
|
1104
|
+
const s = this._getOrCreateContext(e, t);
|
|
1105
|
+
return s.exclusive !== t && (s.exclusive = t, this._log.info(`Updated context "${e}" exclusivity: ${t}`)), s;
|
|
1106
|
+
}
|
|
1107
|
+
/**
|
|
1108
|
+
* Activates a context, enabling all bindings associated with it.
|
|
1109
|
+
* If the context is exclusive, all other exclusive contexts will be deactivated.
|
|
1110
|
+
*
|
|
1111
|
+
* @param contextName - The name of the context to activate
|
|
1112
|
+
*/
|
|
1113
|
+
activateContext(e) {
|
|
1114
|
+
const s = this._getOrCreateContext(e).exclusive;
|
|
1115
|
+
this._log.debug(`Activating context "${e}" (exclusive: ${s})`);
|
|
1116
|
+
const i = this._activeContexts.has(e);
|
|
1117
|
+
if (s) {
|
|
1118
|
+
const o = this._deactivateExclusiveContexts(e);
|
|
1119
|
+
o.length > 0 && this._log.info(`Deactivated exclusive contexts: ${o.join(", ")}`);
|
|
1120
|
+
}
|
|
1121
|
+
i || (this._activeContexts.add(e), this._log.info(`Context "${e}" activated${s ? " as exclusive" : ""}`));
|
|
1122
|
+
}
|
|
1123
|
+
/**
|
|
1124
|
+
* Deactivates a context, disabling all bindings associated with it
|
|
1125
|
+
*
|
|
1126
|
+
* @param contextName - The name of the context to deactivate
|
|
1127
|
+
*/
|
|
1128
|
+
deactivateContext(e) {
|
|
1129
|
+
this._log.debug(`Deactivating context "${e}"`), this._activeContexts.has(e) ? (this._activeContexts.delete(e), this._log.info(`Context "${e}" deactivated`)) : this._log.debug(`Context "${e}" was not active`);
|
|
1130
|
+
}
|
|
1131
|
+
/**
|
|
1132
|
+
* Returns a list of all active contexts
|
|
1133
|
+
*
|
|
1134
|
+
* @returns Array of active context names
|
|
1135
|
+
*/
|
|
1136
|
+
getActiveContexts() {
|
|
1137
|
+
return [...this._activeContexts];
|
|
1138
|
+
}
|
|
1139
|
+
/**
|
|
1140
|
+
* Returns whether a context is active
|
|
1141
|
+
*
|
|
1142
|
+
* @param contextName - The context to check
|
|
1143
|
+
* @returns True if the context is active
|
|
1144
|
+
*/
|
|
1145
|
+
isContextActive(e) {
|
|
1146
|
+
return this._activeContexts.has(e);
|
|
1147
|
+
}
|
|
1148
|
+
/**
|
|
1149
|
+
* Gets a context by name
|
|
1150
|
+
*
|
|
1151
|
+
* @param contextName - The context name to get
|
|
1152
|
+
* @returns The context or null if not found
|
|
1153
|
+
*/
|
|
1154
|
+
getContext(e) {
|
|
1155
|
+
return this._contexts.get(e) || null;
|
|
1156
|
+
}
|
|
1157
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
1158
|
+
// Binding Management API
|
|
1159
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
1160
|
+
/**
|
|
1161
|
+
* Register a binding for an input device
|
|
1162
|
+
*
|
|
1163
|
+
* @param binding - The binding to register
|
|
1164
|
+
* @throws Error if binding type is invalid
|
|
1165
|
+
*/
|
|
1166
|
+
$registerBinding(e) {
|
|
1167
|
+
var t;
|
|
1168
|
+
if (!J.includes(e.type))
|
|
1169
|
+
throw new Error(`Invalid binding type: ${e.type}`);
|
|
1170
|
+
e.context && !this._contexts.has(e.context) && this.registerContext(e.context), this._bindings.has(e.deviceID) || this._bindings.set(e.deviceID, []), (t = this._bindings.get(e.deviceID)) == null || t.push(e), this._log.debug(`Registered ${e.type} binding for "${e.action.name}" in context "${e.context || null}"`);
|
|
1171
|
+
}
|
|
1172
|
+
/**
|
|
1173
|
+
* Register a binding using a binding definition object
|
|
1174
|
+
*
|
|
1175
|
+
* @param definition - The binding definition to register
|
|
1176
|
+
*/
|
|
1177
|
+
registerBinding(e) {
|
|
1178
|
+
const t = this._createBindingFromDefinition(e);
|
|
1179
|
+
t ? this.$registerBinding(t) : this._log.error(`Failed to create binding for action "${e.action}" with type "${e.type}"`);
|
|
1180
|
+
}
|
|
1181
|
+
/**
|
|
1182
|
+
* Unregister all bindings for an action within a context
|
|
1183
|
+
*
|
|
1184
|
+
* @param actionName - The name of the action
|
|
1185
|
+
* @param context - The context to unregister from (defaults to null)
|
|
1186
|
+
*/
|
|
1187
|
+
unregisterBindings(e, t = null) {
|
|
1188
|
+
this._log.debug(`Unregistering all bindings for action "${e}" in context "${t}"`);
|
|
1189
|
+
let s = 0;
|
|
1190
|
+
for (const [i, o] of this._bindings.entries()) {
|
|
1191
|
+
const r = o.filter((c) => {
|
|
1192
|
+
const h = c.context || null, u = c.action.name !== e || h !== t;
|
|
1193
|
+
return u || s++, u;
|
|
1194
|
+
});
|
|
1195
|
+
r.length !== o.length && this._bindings.set(i, r);
|
|
1196
|
+
}
|
|
1197
|
+
this._log.info(`Removed ${s} bindings for action "${e}" in context "${t}"`);
|
|
1198
|
+
}
|
|
1199
|
+
/**
|
|
1200
|
+
* Gets all bindings for a specific action
|
|
1201
|
+
*
|
|
1202
|
+
* @param actionName - The name of the action
|
|
1203
|
+
* @param context - The context to get bindings from (optional)
|
|
1204
|
+
* @returns Array of bindings that match the criteria
|
|
1205
|
+
*/
|
|
1206
|
+
getBindingsForAction(e, t) {
|
|
1207
|
+
const s = [];
|
|
1208
|
+
for (const i of this._bindings.values())
|
|
1209
|
+
for (const o of i) {
|
|
1210
|
+
const r = o.context || null;
|
|
1211
|
+
o.action.name === e && (!t || r === t) && s.push(o);
|
|
1212
|
+
}
|
|
1213
|
+
return s;
|
|
1214
|
+
}
|
|
1215
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
1216
|
+
// Configuration Management API
|
|
1217
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
1218
|
+
/**
|
|
1219
|
+
* Exports the current configuration to a serializable object
|
|
1220
|
+
*
|
|
1221
|
+
* @returns A BindingConfiguration object representing the current state
|
|
1222
|
+
*/
|
|
1223
|
+
exportConfiguration() {
|
|
1224
|
+
this._log.debug("Exporting binding configuration");
|
|
1225
|
+
const e = [...this._actions.values()].map((i) => i.type === "analog" ? {
|
|
1226
|
+
name: i.name,
|
|
1227
|
+
type: i.type,
|
|
1228
|
+
minValue: i.minValue ?? 0,
|
|
1229
|
+
maxValue: i.maxValue ?? 1
|
|
1230
|
+
} : i), t = [];
|
|
1231
|
+
for (const i of this._bindings.values())
|
|
1232
|
+
for (const o of i)
|
|
1233
|
+
t.push(o.toJSON());
|
|
1234
|
+
const s = [...this._contexts.values()].map((i) => ({
|
|
1235
|
+
name: i.name,
|
|
1236
|
+
exclusive: i.exclusive,
|
|
1237
|
+
active: this._activeContexts.has(i.name)
|
|
1238
|
+
}));
|
|
1239
|
+
return this._log.info(`Configuration exported: ${e.length} actions, ${t.length} bindings, ${s.length} contexts`), {
|
|
1240
|
+
actions: e,
|
|
1241
|
+
bindings: t,
|
|
1242
|
+
contexts: s
|
|
1243
|
+
};
|
|
1244
|
+
}
|
|
1245
|
+
/**
|
|
1246
|
+
* Imports a configuration from an object
|
|
1247
|
+
*
|
|
1248
|
+
* @param config - The configuration to import
|
|
1249
|
+
*/
|
|
1250
|
+
importConfiguration(e) {
|
|
1251
|
+
this._log.debug("Importing binding configuration"), this._bindings.clear(), this._actions.clear(), this._contexts.clear(), this._activeContexts.clear();
|
|
1252
|
+
for (const t of e.actions)
|
|
1253
|
+
this.registerAction(t);
|
|
1254
|
+
for (const t of e.contexts)
|
|
1255
|
+
this.registerContext(t.name, t.exclusive), t.active && this.activateContext(t.name);
|
|
1256
|
+
for (const t of e.bindings)
|
|
1257
|
+
try {
|
|
1258
|
+
this.registerBinding(t);
|
|
1259
|
+
} catch (s) {
|
|
1260
|
+
s instanceof Error && this._log.error(`Failed to import binding for action "${t.action}": ${s.message}`);
|
|
1261
|
+
}
|
|
1262
|
+
this._log.info(`Configuration imported: ${e.actions.length} actions, ${e.bindings.length} bindings, ${e.contexts.length} contexts`);
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1265
|
+
function A() {
|
|
1266
|
+
return typeof window < "u" && typeof window.document < "u";
|
|
1267
|
+
}
|
|
1268
|
+
function $() {
|
|
1269
|
+
return A() && !!window.navigator.gpu;
|
|
1270
|
+
}
|
|
1271
|
+
class Z {
|
|
1272
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
1273
|
+
constructor(e, t, s, i, o) {
|
|
1274
|
+
n(this, "_engine");
|
|
1275
|
+
n(this, "_entityManager");
|
|
1276
|
+
n(this, "_inputManager");
|
|
1277
|
+
n(this, "_sceneEngine");
|
|
1278
|
+
n(this, "_currentScene", null);
|
|
1279
|
+
n(this, "_log");
|
|
1280
|
+
n(this, "started", !1);
|
|
1281
|
+
this._engine = e, this._sceneEngine = t, this._entityManager = s, this._inputManager = i, this._log = (o == null ? void 0 : o.getLogger("GameManager")) || new l("GameManager"), A() && window.addEventListener("resize", this._resizeHandler.bind(this));
|
|
1282
|
+
}
|
|
1283
|
+
_renderLoop() {
|
|
1284
|
+
const e = this._engine.getDeltaTime();
|
|
1285
|
+
this._entityManager.$frameUpdate(e), this._inputManager && this._inputManager.pollGamepads(), this._currentScene && this._currentScene.render();
|
|
1286
|
+
}
|
|
1287
|
+
_resizeHandler() {
|
|
1288
|
+
this.started && this._engine.resize();
|
|
1289
|
+
}
|
|
1290
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
1291
|
+
// Public API
|
|
1292
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
1293
|
+
async start(e) {
|
|
1294
|
+
this._currentScene = await this._sceneEngine.loadScene(e), this._engine.runRenderLoop(this._renderLoop.bind(this)), this.started = !0, this._log.info("SkewedAspect Game Engine started successfully");
|
|
1295
|
+
}
|
|
1296
|
+
async stop() {
|
|
1297
|
+
this.started = !1, this._engine.stopRenderLoop(), this._log.info("SkewedAspect Game Engine stopped successfully");
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
class ue {
|
|
1301
|
+
constructor() {
|
|
1302
|
+
n(this, "entity", null);
|
|
1303
|
+
}
|
|
1304
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
1305
|
+
// Internal API
|
|
1306
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
1307
|
+
$emit(e) {
|
|
1308
|
+
if (!this.entity)
|
|
1309
|
+
throw new Error("Entity is not set for this behavior.");
|
|
1310
|
+
e.senderID = this.entity.id, this.entity.eventBus.publish(e);
|
|
1311
|
+
}
|
|
1312
|
+
/**
|
|
1313
|
+
* Sets the entity for this behavior.
|
|
1314
|
+
* @param entity - The entity to set.
|
|
1315
|
+
*/
|
|
1316
|
+
$setEntity(e) {
|
|
1317
|
+
this.entity = e;
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1320
|
+
class q {
|
|
1321
|
+
/**
|
|
1322
|
+
* Creates an instance of GameEntityBase.
|
|
1323
|
+
* @param type - The type of the entity.
|
|
1324
|
+
* @param initialState - The initial state of the entity.
|
|
1325
|
+
* @param behaviors - An array of behaviors to attach to the entity.
|
|
1326
|
+
* @param eventBus
|
|
1327
|
+
*/
|
|
1328
|
+
constructor(e, t, s, i) {
|
|
1329
|
+
/** The unique identifier of the entity. */
|
|
1330
|
+
n(this, "id");
|
|
1331
|
+
/** The type of the entity. */
|
|
1332
|
+
n(this, "type");
|
|
1333
|
+
/** The state of the entity. */
|
|
1334
|
+
n(this, "state");
|
|
1335
|
+
/** A map of behaviors attached to the entity. */
|
|
1336
|
+
n(this, "behaviors", /* @__PURE__ */ new Map());
|
|
1337
|
+
/** The event bus for the entity. */
|
|
1338
|
+
n(this, "eventBus");
|
|
1339
|
+
/** The event subscriptions for the entity. */
|
|
1340
|
+
n(this, "subscriptions", /* @__PURE__ */ new Map());
|
|
1341
|
+
this.id = crypto.randomUUID(), this.type = e, this.state = s, this.eventBus = t;
|
|
1342
|
+
for (const o of i)
|
|
1343
|
+
this.attachBehavior(new o());
|
|
1344
|
+
}
|
|
1345
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
1346
|
+
// Internal Methods
|
|
1347
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
1348
|
+
/**
|
|
1349
|
+
* Processes a game event by passing it to the entity's behaviors.
|
|
1350
|
+
* @param event - The game event to process.
|
|
1351
|
+
* @returns A promise that resolves when the event has been processed.
|
|
1352
|
+
*/
|
|
1353
|
+
async $processEvent(e) {
|
|
1354
|
+
for (const t of this.behaviors.values())
|
|
1355
|
+
if (await t.processEvent(e, this.state))
|
|
1356
|
+
break;
|
|
1357
|
+
}
|
|
1358
|
+
/**
|
|
1359
|
+
* Updates the entity by calling the update method of its behaviors.
|
|
1360
|
+
* @param dt - The delta time since the last update.
|
|
1361
|
+
*/
|
|
1362
|
+
$update(e) {
|
|
1363
|
+
var t;
|
|
1364
|
+
for (const s of this.behaviors.values())
|
|
1365
|
+
(t = s.update) == null || t.call(s, e, this.state);
|
|
1366
|
+
}
|
|
1367
|
+
/**
|
|
1368
|
+
* Destroys the entity by calling the destroy method of its behaviors.
|
|
1369
|
+
* @returns A promise that resolves when the entity has been destroyed.
|
|
1370
|
+
*/
|
|
1371
|
+
async $destroy() {
|
|
1372
|
+
var e;
|
|
1373
|
+
for (const t of this.behaviors.values())
|
|
1374
|
+
(e = t.destroy) == null || e.call(t);
|
|
1375
|
+
for (const t of this.subscriptions.values())
|
|
1376
|
+
t.unsubscribe();
|
|
1377
|
+
this.behaviors.clear(), this.subscriptions.clear();
|
|
1378
|
+
}
|
|
1379
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
1380
|
+
// Public Methods
|
|
1381
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
1382
|
+
/**
|
|
1383
|
+
* Attaches a behavior to the entity.
|
|
1384
|
+
* @param behavior - The behavior to attach.
|
|
1385
|
+
* @throws Will throw an error if the behavior is already attached.
|
|
1386
|
+
*/
|
|
1387
|
+
attachBehavior(e) {
|
|
1388
|
+
if (this.behaviors.has(e.name))
|
|
1389
|
+
throw new Error(`Behavior ${e.name} is already attached to this entity.`);
|
|
1390
|
+
for (const t of e.eventSubscriptions) {
|
|
1391
|
+
const s = this.subscriptions.get(t);
|
|
1392
|
+
if (s)
|
|
1393
|
+
s.count++;
|
|
1394
|
+
else {
|
|
1395
|
+
const i = this.eventBus.subscribe(t, this.$processEvent.bind(this));
|
|
1396
|
+
this.subscriptions.set(t, { count: 1, unsubscribe: i });
|
|
1397
|
+
}
|
|
1398
|
+
}
|
|
1399
|
+
this.behaviors.set(e.name, e), e.$setEntity(this);
|
|
1400
|
+
}
|
|
1401
|
+
/**
|
|
1402
|
+
* Detaches a behavior from the entity.
|
|
1403
|
+
* @param behaviorName - The behavior to detach.
|
|
1404
|
+
*/
|
|
1405
|
+
detachBehavior(e) {
|
|
1406
|
+
const t = this.behaviors.get(e);
|
|
1407
|
+
if (t) {
|
|
1408
|
+
for (const s of t.eventSubscriptions) {
|
|
1409
|
+
const i = this.subscriptions.get(s);
|
|
1410
|
+
i && (i.count--, i.count <= 0 && (i.unsubscribe(), this.subscriptions.delete(s)));
|
|
1411
|
+
}
|
|
1412
|
+
this.behaviors.delete(t.name), t.$setEntity(null);
|
|
1413
|
+
}
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1416
|
+
class Q {
|
|
1417
|
+
/**
|
|
1418
|
+
* Creates an instance of EntityManager.
|
|
1419
|
+
* @param eventBus - The event bus for the entity manager.
|
|
1420
|
+
* @param logger - The logging utility to use
|
|
1421
|
+
* @param bindingManager - The binding manager for registering actions
|
|
1422
|
+
*/
|
|
1423
|
+
constructor(e, t, s) {
|
|
1424
|
+
/** The event bus for the entity manager. */
|
|
1425
|
+
n(this, "eventBus");
|
|
1426
|
+
/** A map of entities managed by the entity manager. */
|
|
1427
|
+
n(this, "entities", /* @__PURE__ */ new Map());
|
|
1428
|
+
/** A map of entity definitions registered with the entity manager. */
|
|
1429
|
+
n(this, "entityDefinitions", /* @__PURE__ */ new Map());
|
|
1430
|
+
/** Reference to the binding manager for registering actions */
|
|
1431
|
+
n(this, "bindingManager");
|
|
1432
|
+
/** Logger instance */
|
|
1433
|
+
n(this, "_log");
|
|
1434
|
+
this.eventBus = e, this.bindingManager = s, this._log = (t == null ? void 0 : t.getLogger("EntityManager")) || new l("EntityManager"), this._log.info("EntityManager initialized");
|
|
1435
|
+
}
|
|
1436
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
1437
|
+
// Private Methods
|
|
1438
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
1439
|
+
/**
|
|
1440
|
+
* Checks if two actions are compatible
|
|
1441
|
+
* @param existingAction - The action already registered
|
|
1442
|
+
* @param newAction - The action being registered
|
|
1443
|
+
* @returns true if the actions are compatible, false if they have conflicting options
|
|
1444
|
+
*/
|
|
1445
|
+
areActionsCompatible(e, t) {
|
|
1446
|
+
return !(e.type !== t.type || e.type === "analog" && t.type === "analog" && (t.minValue !== void 0 && e.minValue !== t.minValue || t.maxValue !== void 0 && e.maxValue !== t.maxValue));
|
|
1447
|
+
}
|
|
1448
|
+
/**
|
|
1449
|
+
* Registers actions defined in the entity definition with the binding manager
|
|
1450
|
+
* @param entityDef - The entity definition containing actions to register
|
|
1451
|
+
*/
|
|
1452
|
+
registerEntityActions(e) {
|
|
1453
|
+
if (e.actions)
|
|
1454
|
+
for (const t of e.actions)
|
|
1455
|
+
try {
|
|
1456
|
+
const s = this.bindingManager.getAction(t.name);
|
|
1457
|
+
s ? this.areActionsCompatible(s, t) ? this._log.trace(`Action "${t.name}" already registered with compatible options, skipping registration`) : this._log.warn(`Action "${t.name}" already registered with different options. Entity "${e.type}" requires: ${JSON.stringify(t)}, but found: ${JSON.stringify(s)}`) : (this._log.debug(`Registering action "${t.name}" from entity type "${e.type}"`), this.bindingManager.registerAction(t));
|
|
1458
|
+
} catch (s) {
|
|
1459
|
+
this._log.debug(`Failed to register action "${t.name}": ${s instanceof Error ? s.message : String(s)}`);
|
|
1460
|
+
}
|
|
1461
|
+
}
|
|
1462
|
+
$frameUpdate(e) {
|
|
1463
|
+
this._log.trace(`Updating ${this.entities.size} entities with dt=${e}`);
|
|
1464
|
+
for (const t of this.entities.values())
|
|
1465
|
+
t.$update(e);
|
|
1466
|
+
}
|
|
1467
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
1468
|
+
// Public API
|
|
1469
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
1470
|
+
/**
|
|
1471
|
+
* Registers a new entity definition.
|
|
1472
|
+
* @param entityDef - The definition of the entity.
|
|
1473
|
+
*/
|
|
1474
|
+
registerEntityDefinition(e) {
|
|
1475
|
+
this._log.debug(`Registering entity definition: ${e.type}`), this.registerEntityActions(e), this.entityDefinitions.set(e.type, e);
|
|
1476
|
+
}
|
|
1477
|
+
/**
|
|
1478
|
+
* Creates a new entity of the given type.
|
|
1479
|
+
* @param type - The type of the entity to create.
|
|
1480
|
+
* @param initialState - The initial state of the entity.
|
|
1481
|
+
* @returns The created entity.
|
|
1482
|
+
*/
|
|
1483
|
+
createEntity(e, t = {}) {
|
|
1484
|
+
var r;
|
|
1485
|
+
this._log.debug(`Creating entity of type: ${e}`);
|
|
1486
|
+
const s = this.entityDefinitions.get(e);
|
|
1487
|
+
if (!s) {
|
|
1488
|
+
const c = `Entity type ${e} is not registered.`;
|
|
1489
|
+
throw this._log.error(c), new Error(c);
|
|
1490
|
+
}
|
|
1491
|
+
this._log.trace(`Using entity definition with ${((r = s.behaviors) == null ? void 0 : r.length) || 0} behaviors`);
|
|
1492
|
+
const i = { ...s.defaultState, ...t }, o = new q(s.type, this.eventBus, i, s.behaviors);
|
|
1493
|
+
return this.entities.set(o.id, o), this._log.debug(`Entity created with ID: ${o.id}`), o;
|
|
1494
|
+
}
|
|
1495
|
+
/**
|
|
1496
|
+
* Destroys the entity with the given ID.
|
|
1497
|
+
* @param entityID - The ID of the entity to destroy.
|
|
1498
|
+
*/
|
|
1499
|
+
destroyEntity(e) {
|
|
1500
|
+
this._log.debug(`Destroying entity: ${e}`);
|
|
1501
|
+
const t = this.entities.get(e);
|
|
1502
|
+
t ? (t.$destroy(), this.entities.delete(e), this._log.debug(`Entity ${e} destroyed`)) : this._log.warn(`Attempted to destroy non-existent entity: ${e}`);
|
|
1503
|
+
}
|
|
1504
|
+
/**
|
|
1505
|
+
* Gets the entity with the given ID.
|
|
1506
|
+
* @param entityID - The ID of the entity to get.
|
|
1507
|
+
* @returns The entity with the given ID, or null if it does not exist.
|
|
1508
|
+
*/
|
|
1509
|
+
getEntity(e) {
|
|
1510
|
+
return this._log.trace(`Getting entity: ${e}`), this.entities.get(e) ?? null;
|
|
1511
|
+
}
|
|
1512
|
+
/**
|
|
1513
|
+
* Adds an existing entity to the entity manager.
|
|
1514
|
+
* @param entity - The entity to add.
|
|
1515
|
+
*/
|
|
1516
|
+
addEntity(e) {
|
|
1517
|
+
this._log.debug(`Adding existing entity: ${e.id} (type: ${e.type})`), this.entities.set(e.id, e);
|
|
1518
|
+
}
|
|
1519
|
+
/**
|
|
1520
|
+
* Removes the entity with the given ID, without destroying it.
|
|
1521
|
+
* @param entityID - The ID of the entity to remove.
|
|
1522
|
+
*/
|
|
1523
|
+
removeEntity(e) {
|
|
1524
|
+
this._log.debug(`Removing entity: ${e}`), this.entities.get(e) ? (this.entities.delete(e), this._log.debug(`Entity ${e} removed`)) : this._log.warn(`Attempted to remove non-existent entity: ${e}`);
|
|
1525
|
+
}
|
|
1526
|
+
}
|
|
1527
|
+
class ee {
|
|
1528
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
1529
|
+
/**
|
|
1530
|
+
* Create a new KeyboardResourceAccess
|
|
1531
|
+
*/
|
|
1532
|
+
constructor() {
|
|
1533
|
+
n(this, "_keyboardDevice");
|
|
1534
|
+
n(this, "_keysState", {});
|
|
1535
|
+
// Callbacks
|
|
1536
|
+
n(this, "_onDeviceConnected");
|
|
1537
|
+
n(this, "_onInputChanged");
|
|
1538
|
+
this._keyboardDevice = {
|
|
1539
|
+
id: "keyboard-0",
|
|
1540
|
+
name: "Keyboard",
|
|
1541
|
+
type: "keyboard",
|
|
1542
|
+
connected: !0
|
|
1543
|
+
}, this._setupKeyboardEvents(), setTimeout(() => this._notifyDeviceConnected(), 0);
|
|
1544
|
+
}
|
|
1545
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
1546
|
+
// Public API
|
|
1547
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
1548
|
+
/**
|
|
1549
|
+
* Register a callback for device connected events
|
|
1550
|
+
*
|
|
1551
|
+
* @param callback - The callback to register
|
|
1552
|
+
*/
|
|
1553
|
+
onDeviceConnected(e) {
|
|
1554
|
+
this._onDeviceConnected = e;
|
|
1555
|
+
}
|
|
1556
|
+
/**
|
|
1557
|
+
* Register a callback for input changed events
|
|
1558
|
+
*
|
|
1559
|
+
* @param callback - The callback to register
|
|
1560
|
+
*/
|
|
1561
|
+
onInputChanged(e) {
|
|
1562
|
+
this._onInputChanged = e;
|
|
1563
|
+
}
|
|
1564
|
+
/**
|
|
1565
|
+
* Get the current keyboard state
|
|
1566
|
+
*/
|
|
1567
|
+
getState() {
|
|
1568
|
+
return {
|
|
1569
|
+
type: "keyboard",
|
|
1570
|
+
keys: { ...this._keysState },
|
|
1571
|
+
delta: {}
|
|
1572
|
+
};
|
|
1573
|
+
}
|
|
1574
|
+
/**
|
|
1575
|
+
* Get the keyboard device
|
|
1576
|
+
*/
|
|
1577
|
+
getDevice() {
|
|
1578
|
+
return { ...this._keyboardDevice };
|
|
1579
|
+
}
|
|
1580
|
+
/**
|
|
1581
|
+
* Destroy the keyboard resource access and clean up event listeners
|
|
1582
|
+
*/
|
|
1583
|
+
destroy() {
|
|
1584
|
+
window.removeEventListener("keydown", this._handleKeyDown), window.removeEventListener("keyup", this._handleKeyUp);
|
|
1585
|
+
}
|
|
1586
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
1587
|
+
// Private Methods
|
|
1588
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
1589
|
+
/**
|
|
1590
|
+
* Set up keyboard event listeners
|
|
1591
|
+
*/
|
|
1592
|
+
_setupKeyboardEvents() {
|
|
1593
|
+
this._handleKeyDown = this._handleKeyDown.bind(this), this._handleKeyUp = this._handleKeyUp.bind(this), window.addEventListener("keydown", this._handleKeyDown), window.addEventListener("keyup", this._handleKeyUp);
|
|
1594
|
+
}
|
|
1595
|
+
/**
|
|
1596
|
+
* Handle keyboard key down events
|
|
1597
|
+
*/
|
|
1598
|
+
_handleKeyDown(e) {
|
|
1599
|
+
this._keysState[e.code] = !0;
|
|
1600
|
+
const t = {};
|
|
1601
|
+
t[e.code] = !0;
|
|
1602
|
+
const s = {
|
|
1603
|
+
type: "keyboard",
|
|
1604
|
+
keys: { ...this._keysState },
|
|
1605
|
+
delta: t,
|
|
1606
|
+
event: e
|
|
1607
|
+
};
|
|
1608
|
+
this._notifyInputChanged(s);
|
|
1609
|
+
}
|
|
1610
|
+
/**
|
|
1611
|
+
* Handle keyboard key up events
|
|
1612
|
+
*/
|
|
1613
|
+
_handleKeyUp(e) {
|
|
1614
|
+
this._keysState[e.code] = !1;
|
|
1615
|
+
const t = {};
|
|
1616
|
+
t[e.code] = !1;
|
|
1617
|
+
const s = {
|
|
1618
|
+
type: "keyboard",
|
|
1619
|
+
keys: { ...this._keysState },
|
|
1620
|
+
delta: t,
|
|
1621
|
+
event: e
|
|
1622
|
+
};
|
|
1623
|
+
this._notifyInputChanged(s);
|
|
1624
|
+
}
|
|
1625
|
+
/**
|
|
1626
|
+
* Notify subscribers of device connected event
|
|
1627
|
+
*/
|
|
1628
|
+
_notifyDeviceConnected() {
|
|
1629
|
+
this._onDeviceConnected && this._onDeviceConnected(this._keyboardDevice);
|
|
1630
|
+
}
|
|
1631
|
+
/**
|
|
1632
|
+
* Notify subscribers of input changed event
|
|
1633
|
+
*/
|
|
1634
|
+
_notifyInputChanged(e) {
|
|
1635
|
+
this._onInputChanged && this._onInputChanged(this._keyboardDevice, e);
|
|
1636
|
+
}
|
|
1637
|
+
}
|
|
1638
|
+
class te {
|
|
1639
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
1640
|
+
/**
|
|
1641
|
+
* Create a new MouseResourceAccess
|
|
1642
|
+
*
|
|
1643
|
+
* @param targetElement - The DOM element to attach mouse listeners to (defaults to document.body)
|
|
1644
|
+
*/
|
|
1645
|
+
constructor(e = document.body) {
|
|
1646
|
+
n(this, "_targetElement");
|
|
1647
|
+
n(this, "_mouseDevice");
|
|
1648
|
+
n(this, "_buttonState", {});
|
|
1649
|
+
n(this, "_axesState", {});
|
|
1650
|
+
n(this, "_position", {
|
|
1651
|
+
absolute: { x: 0, y: 0 },
|
|
1652
|
+
relative: { x: 0, y: 0 }
|
|
1653
|
+
});
|
|
1654
|
+
n(this, "_wheelState", {
|
|
1655
|
+
deltaX: 0,
|
|
1656
|
+
deltaY: 0,
|
|
1657
|
+
deltaZ: 0,
|
|
1658
|
+
deltaMode: 0
|
|
1659
|
+
});
|
|
1660
|
+
// Callbacks
|
|
1661
|
+
n(this, "_onDeviceConnected");
|
|
1662
|
+
n(this, "_onInputChanged");
|
|
1663
|
+
this._targetElement = e, this._mouseDevice = {
|
|
1664
|
+
id: "mouse-0",
|
|
1665
|
+
name: "Mouse",
|
|
1666
|
+
type: "mouse",
|
|
1667
|
+
connected: !0
|
|
1668
|
+
}, this._setupMouseEvents(), this._axesState["axis-x"] = 0, this._axesState["axis-y"] = 0, setTimeout(() => this._notifyDeviceConnected(), 0);
|
|
1669
|
+
}
|
|
1670
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
1671
|
+
// Public API
|
|
1672
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
1673
|
+
/**
|
|
1674
|
+
* Register a callback for device connected events
|
|
1675
|
+
*
|
|
1676
|
+
* @param callback - The callback to register
|
|
1677
|
+
*/
|
|
1678
|
+
onDeviceConnected(e) {
|
|
1679
|
+
this._onDeviceConnected = e;
|
|
1680
|
+
}
|
|
1681
|
+
/**
|
|
1682
|
+
* Register a callback for input changed events
|
|
1683
|
+
*
|
|
1684
|
+
* @param callback - The callback to register
|
|
1685
|
+
*/
|
|
1686
|
+
onInputChanged(e) {
|
|
1687
|
+
this._onInputChanged = e;
|
|
1688
|
+
}
|
|
1689
|
+
/**
|
|
1690
|
+
* Get the current mouse state
|
|
1691
|
+
*/
|
|
1692
|
+
getState() {
|
|
1693
|
+
return {
|
|
1694
|
+
type: "mouse",
|
|
1695
|
+
buttons: { ...this._buttonState },
|
|
1696
|
+
axes: { ...this._axesState },
|
|
1697
|
+
position: {
|
|
1698
|
+
absolute: { ...this._position.absolute },
|
|
1699
|
+
relative: { ...this._position.relative }
|
|
1700
|
+
},
|
|
1701
|
+
wheel: { ...this._wheelState }
|
|
1702
|
+
};
|
|
1703
|
+
}
|
|
1704
|
+
/**
|
|
1705
|
+
* Get the mouse device
|
|
1706
|
+
*/
|
|
1707
|
+
getDevice() {
|
|
1708
|
+
return { ...this._mouseDevice };
|
|
1709
|
+
}
|
|
1710
|
+
/**
|
|
1711
|
+
* Destroy the mouse resource access and clean up event listeners
|
|
1712
|
+
*/
|
|
1713
|
+
destroy() {
|
|
1714
|
+
this._targetElement.removeEventListener("mousedown", this._handleMouseDown), this._targetElement.removeEventListener("mouseup", this._handleMouseUp), this._targetElement.removeEventListener("mousemove", this._handleMouseMove), this._targetElement.removeEventListener("wheel", this._handleMouseWheel);
|
|
1715
|
+
}
|
|
1716
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
1717
|
+
// Private Methods
|
|
1718
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
1719
|
+
/**
|
|
1720
|
+
* Set up mouse event listeners
|
|
1721
|
+
*/
|
|
1722
|
+
_setupMouseEvents() {
|
|
1723
|
+
this._handleMouseDown = this._handleMouseDown.bind(this), this._handleMouseUp = this._handleMouseUp.bind(this), this._handleMouseMove = this._handleMouseMove.bind(this), this._handleMouseWheel = this._handleMouseWheel.bind(this), this._targetElement.addEventListener("mousedown", this._handleMouseDown), this._targetElement.addEventListener("mouseup", this._handleMouseUp), this._targetElement.addEventListener("mousemove", this._handleMouseMove), this._targetElement.addEventListener("wheel", this._handleMouseWheel);
|
|
1724
|
+
}
|
|
1725
|
+
/**
|
|
1726
|
+
* Handle mouse button down events
|
|
1727
|
+
*/
|
|
1728
|
+
_handleMouseDown(e) {
|
|
1729
|
+
const t = `button-${e.button}`, s = {
|
|
1730
|
+
pressed: !0
|
|
1731
|
+
};
|
|
1732
|
+
this._buttonState[t] = s, this._notifyInputChanged({
|
|
1733
|
+
type: "mouse",
|
|
1734
|
+
event: e,
|
|
1735
|
+
buttons: { ...this._buttonState },
|
|
1736
|
+
axes: { ...this._axesState },
|
|
1737
|
+
position: { ...this._position }
|
|
1738
|
+
});
|
|
1739
|
+
}
|
|
1740
|
+
/**
|
|
1741
|
+
* Handle mouse button up events
|
|
1742
|
+
*/
|
|
1743
|
+
_handleMouseUp(e) {
|
|
1744
|
+
const t = `button-${e.button}`, s = {
|
|
1745
|
+
pressed: !1
|
|
1746
|
+
};
|
|
1747
|
+
this._buttonState[t] = s, this._notifyInputChanged({
|
|
1748
|
+
type: "mouse",
|
|
1749
|
+
event: e,
|
|
1750
|
+
buttons: { ...this._buttonState },
|
|
1751
|
+
axes: { ...this._axesState },
|
|
1752
|
+
position: { ...this._position }
|
|
1753
|
+
});
|
|
1754
|
+
}
|
|
1755
|
+
/**
|
|
1756
|
+
* Handle mouse move events
|
|
1757
|
+
*/
|
|
1758
|
+
_handleMouseMove(e) {
|
|
1759
|
+
this._position = {
|
|
1760
|
+
absolute: {
|
|
1761
|
+
x: e.clientX,
|
|
1762
|
+
y: e.clientY
|
|
1763
|
+
},
|
|
1764
|
+
relative: {
|
|
1765
|
+
x: e.movementX,
|
|
1766
|
+
y: e.movementY
|
|
1767
|
+
}
|
|
1768
|
+
}, this._axesState["axis-x"] = e.clientX, this._axesState["axis-y"] = e.clientY, this._notifyInputChanged({
|
|
1769
|
+
type: "mouse",
|
|
1770
|
+
event: e,
|
|
1771
|
+
buttons: { ...this._buttonState },
|
|
1772
|
+
axes: { ...this._axesState },
|
|
1773
|
+
position: { ...this._position }
|
|
1774
|
+
});
|
|
1775
|
+
}
|
|
1776
|
+
/**
|
|
1777
|
+
* Handle mouse wheel events
|
|
1778
|
+
*/
|
|
1779
|
+
_handleMouseWheel(e) {
|
|
1780
|
+
this._wheelState = {
|
|
1781
|
+
deltaX: e.deltaX,
|
|
1782
|
+
deltaY: e.deltaY,
|
|
1783
|
+
deltaZ: e.deltaZ,
|
|
1784
|
+
deltaMode: e.deltaMode
|
|
1785
|
+
}, this._notifyInputChanged({
|
|
1786
|
+
type: "mouse",
|
|
1787
|
+
event: e,
|
|
1788
|
+
buttons: { ...this._buttonState },
|
|
1789
|
+
axes: { ...this._axesState },
|
|
1790
|
+
position: { ...this._position },
|
|
1791
|
+
wheel: { ...this._wheelState }
|
|
1792
|
+
});
|
|
1793
|
+
}
|
|
1794
|
+
/**
|
|
1795
|
+
* Notify subscribers of device connected event
|
|
1796
|
+
*/
|
|
1797
|
+
_notifyDeviceConnected() {
|
|
1798
|
+
this._onDeviceConnected && this._onDeviceConnected(this._mouseDevice);
|
|
1799
|
+
}
|
|
1800
|
+
/**
|
|
1801
|
+
* Notify subscribers of input changed event
|
|
1802
|
+
*/
|
|
1803
|
+
_notifyInputChanged(e) {
|
|
1804
|
+
this._onInputChanged && this._onInputChanged(this._mouseDevice, e);
|
|
1805
|
+
}
|
|
1806
|
+
}
|
|
1807
|
+
class se {
|
|
1808
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
1809
|
+
/**
|
|
1810
|
+
* Create a new GamepadResourceAccess
|
|
1811
|
+
*/
|
|
1812
|
+
constructor() {
|
|
1813
|
+
n(this, "_gamepadDevices", {});
|
|
1814
|
+
n(this, "_buttonStates", {});
|
|
1815
|
+
n(this, "_axesStates", {});
|
|
1816
|
+
// Callbacks
|
|
1817
|
+
n(this, "_onDeviceConnected");
|
|
1818
|
+
n(this, "_onDeviceDisconnected");
|
|
1819
|
+
n(this, "_onInputChanged");
|
|
1820
|
+
this._setupGamepadEvents();
|
|
1821
|
+
}
|
|
1822
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
1823
|
+
// Public API
|
|
1824
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
1825
|
+
/**
|
|
1826
|
+
* Register a callback for device connected events
|
|
1827
|
+
*
|
|
1828
|
+
* @param callback - The callback to register
|
|
1829
|
+
*/
|
|
1830
|
+
onDeviceConnected(e) {
|
|
1831
|
+
this._onDeviceConnected = e;
|
|
1832
|
+
}
|
|
1833
|
+
/**
|
|
1834
|
+
* Register a callback for device disconnected events
|
|
1835
|
+
*
|
|
1836
|
+
* @param callback - The callback to register
|
|
1837
|
+
*/
|
|
1838
|
+
onDeviceDisconnected(e) {
|
|
1839
|
+
this._onDeviceDisconnected = e;
|
|
1840
|
+
}
|
|
1841
|
+
/**
|
|
1842
|
+
* Register a callback for input changed events
|
|
1843
|
+
*
|
|
1844
|
+
* @param callback - The callback to register
|
|
1845
|
+
*/
|
|
1846
|
+
onInputChanged(e) {
|
|
1847
|
+
this._onInputChanged = e;
|
|
1848
|
+
}
|
|
1849
|
+
/**
|
|
1850
|
+
* Get all connected gamepad devices
|
|
1851
|
+
*/
|
|
1852
|
+
getDevices() {
|
|
1853
|
+
return Object.values(this._gamepadDevices).map((e) => ({ ...e }));
|
|
1854
|
+
}
|
|
1855
|
+
/**
|
|
1856
|
+
* Get all connected gamepad states
|
|
1857
|
+
*/
|
|
1858
|
+
getStates() {
|
|
1859
|
+
const e = {};
|
|
1860
|
+
return Object.keys(this._buttonStates).forEach((t) => {
|
|
1861
|
+
const s = Number(t), i = this._buttonStates[s] || {}, o = this._axesStates[s] || {};
|
|
1862
|
+
e[s] = {
|
|
1863
|
+
type: "gamepad",
|
|
1864
|
+
buttons: { ...i },
|
|
1865
|
+
axes: { ...o }
|
|
1866
|
+
};
|
|
1867
|
+
}), e;
|
|
1868
|
+
}
|
|
1869
|
+
/**
|
|
1870
|
+
* Get a specific gamepad device by index
|
|
1871
|
+
*
|
|
1872
|
+
* @param index - The index of the gamepad to get
|
|
1873
|
+
*/
|
|
1874
|
+
getDevice(e) {
|
|
1875
|
+
const t = this._gamepadDevices[e];
|
|
1876
|
+
return t ? { ...t } : null;
|
|
1877
|
+
}
|
|
1878
|
+
/**
|
|
1879
|
+
* Get a specific gamepad state by index
|
|
1880
|
+
*
|
|
1881
|
+
* @param index - The index of the gamepad state to get
|
|
1882
|
+
*/
|
|
1883
|
+
getState(e) {
|
|
1884
|
+
const t = this._buttonStates[e], s = this._axesStates[e];
|
|
1885
|
+
return !t && !s ? null : {
|
|
1886
|
+
type: "gamepad",
|
|
1887
|
+
buttons: t ? { ...t } : {},
|
|
1888
|
+
axes: s ? { ...s } : {}
|
|
1889
|
+
};
|
|
1890
|
+
}
|
|
1891
|
+
/**
|
|
1892
|
+
* Poll for gamepad state updates - call this in your game loop
|
|
1893
|
+
*/
|
|
1894
|
+
pollGamepads() {
|
|
1895
|
+
if (!navigator.getGamepads)
|
|
1896
|
+
return;
|
|
1897
|
+
const e = navigator.getGamepads();
|
|
1898
|
+
for (const t of e) {
|
|
1899
|
+
if (!t)
|
|
1900
|
+
continue;
|
|
1901
|
+
const s = t.index;
|
|
1902
|
+
if (!this._gamepadDevices[s]) {
|
|
1903
|
+
this._handleGamepadConnected(t);
|
|
1904
|
+
continue;
|
|
1905
|
+
}
|
|
1906
|
+
const i = this._gamepadDevices[s];
|
|
1907
|
+
if (!i)
|
|
1908
|
+
continue;
|
|
1909
|
+
const o = this._buttonStates[s] || {}, r = this._axesStates[s] || {}, c = {};
|
|
1910
|
+
let h = !1;
|
|
1911
|
+
t.buttons.forEach((d, _) => {
|
|
1912
|
+
const p = `button-${_}`, f = {
|
|
1913
|
+
pressed: d.pressed,
|
|
1914
|
+
touched: d.touched,
|
|
1915
|
+
value: d.value
|
|
1916
|
+
};
|
|
1917
|
+
c[p] = f;
|
|
1918
|
+
const y = o[p];
|
|
1919
|
+
(!y || y.pressed !== f.pressed || y.touched !== f.touched || y.value !== f.value) && (h = !0);
|
|
1920
|
+
});
|
|
1921
|
+
const u = {};
|
|
1922
|
+
let g = !1;
|
|
1923
|
+
if (t.axes.forEach((d, _) => {
|
|
1924
|
+
const p = `axis-${_}`;
|
|
1925
|
+
u[p] = d, r[p] !== d && (g = !0);
|
|
1926
|
+
}), this._buttonStates[s] = c, this._axesStates[s] = u, h || g) {
|
|
1927
|
+
const d = {
|
|
1928
|
+
type: "gamepad",
|
|
1929
|
+
buttons: { ...c },
|
|
1930
|
+
axes: { ...u }
|
|
1931
|
+
};
|
|
1932
|
+
this._notifyInputChanged(i, d);
|
|
1933
|
+
}
|
|
1934
|
+
}
|
|
1935
|
+
}
|
|
1936
|
+
/**
|
|
1937
|
+
* Destroy the gamepad resource access and clean up event listeners
|
|
1938
|
+
*/
|
|
1939
|
+
destroy() {
|
|
1940
|
+
window.removeEventListener("gamepadconnected", this._handleGamepadConnected), window.removeEventListener("gamepaddisconnected", this._handleGamepadDisconnected);
|
|
1941
|
+
}
|
|
1942
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
1943
|
+
// Private Methods
|
|
1944
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
1945
|
+
/**
|
|
1946
|
+
* Set up gamepad event listeners
|
|
1947
|
+
*/
|
|
1948
|
+
_setupGamepadEvents() {
|
|
1949
|
+
if (this._handleGamepadConnected = this._handleGamepadConnected.bind(this), this._handleGamepadDisconnected = this._handleGamepadDisconnected.bind(this), window.addEventListener("gamepadconnected", this._handleGamepadConnected), window.addEventListener("gamepaddisconnected", this._handleGamepadDisconnected), navigator.getGamepads) {
|
|
1950
|
+
const e = navigator.getGamepads();
|
|
1951
|
+
for (const t of e)
|
|
1952
|
+
t && this._handleGamepadConnected(t);
|
|
1953
|
+
}
|
|
1954
|
+
}
|
|
1955
|
+
/**
|
|
1956
|
+
* Handle gamepad connected event
|
|
1957
|
+
*/
|
|
1958
|
+
_handleGamepadConnected(e) {
|
|
1959
|
+
const t = e instanceof GamepadEvent ? e.gamepad : e, s = t.index, i = {
|
|
1960
|
+
id: `gamepad-${s}`,
|
|
1961
|
+
name: t.id,
|
|
1962
|
+
type: "gamepad",
|
|
1963
|
+
connected: !0,
|
|
1964
|
+
index: s,
|
|
1965
|
+
mapping: t.mapping,
|
|
1966
|
+
axes: Array.from(t.axes),
|
|
1967
|
+
buttons: Array.from(t.buttons)
|
|
1968
|
+
};
|
|
1969
|
+
this._gamepadDevices[s] = i;
|
|
1970
|
+
const o = {};
|
|
1971
|
+
Array.from(t.buttons).forEach((c, h) => {
|
|
1972
|
+
o[`button-${h}`] = {
|
|
1973
|
+
pressed: c.pressed,
|
|
1974
|
+
touched: c.touched,
|
|
1975
|
+
value: c.value
|
|
1976
|
+
};
|
|
1977
|
+
}), this._buttonStates[s] = o;
|
|
1978
|
+
const r = {};
|
|
1979
|
+
Array.from(t.axes).forEach((c, h) => {
|
|
1980
|
+
r[`axis-${h}`] = c;
|
|
1981
|
+
}), this._axesStates[s] = r, this._notifyDeviceConnected(i), this._notifyInputChanged(i, {
|
|
1982
|
+
type: "gamepad",
|
|
1983
|
+
buttons: { ...o },
|
|
1984
|
+
axes: { ...r },
|
|
1985
|
+
event: e instanceof GamepadEvent ? e : void 0
|
|
1986
|
+
});
|
|
1987
|
+
}
|
|
1988
|
+
/**
|
|
1989
|
+
* Handle gamepad disconnected event
|
|
1990
|
+
*/
|
|
1991
|
+
_handleGamepadDisconnected(e) {
|
|
1992
|
+
const s = e.gamepad.index, i = this._gamepadDevices[s];
|
|
1993
|
+
i && (i.connected = !1, this._notifyDeviceDisconnected(i), this._gamepadDevices[s] = void 0, this._buttonStates[s] = void 0, this._axesStates[s] = void 0);
|
|
1994
|
+
}
|
|
1995
|
+
/**
|
|
1996
|
+
* Notify subscribers of device connected event
|
|
1997
|
+
*/
|
|
1998
|
+
_notifyDeviceConnected(e) {
|
|
1999
|
+
this._onDeviceConnected && this._onDeviceConnected(e);
|
|
2000
|
+
}
|
|
2001
|
+
/**
|
|
2002
|
+
* Notify subscribers of device disconnected event
|
|
2003
|
+
*/
|
|
2004
|
+
_notifyDeviceDisconnected(e) {
|
|
2005
|
+
this._onDeviceDisconnected && this._onDeviceDisconnected(e);
|
|
2006
|
+
}
|
|
2007
|
+
/**
|
|
2008
|
+
* Notify subscribers of input changed event
|
|
2009
|
+
*/
|
|
2010
|
+
_notifyInputChanged(e, t) {
|
|
2011
|
+
this._onInputChanged && this._onInputChanged(e, t);
|
|
2012
|
+
}
|
|
2013
|
+
}
|
|
2014
|
+
class ie {
|
|
2015
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
2016
|
+
/**
|
|
2017
|
+
* Create a new UserInputManager
|
|
2018
|
+
*
|
|
2019
|
+
* @param eventBus - The game event bus to publish events to
|
|
2020
|
+
* @param canvas - The DOM element to attach input listeners to
|
|
2021
|
+
* @param logger - The logging utility to use
|
|
2022
|
+
*/
|
|
2023
|
+
constructor(e, t, s) {
|
|
2024
|
+
n(this, "_eventBus");
|
|
2025
|
+
n(this, "_keyboardRA");
|
|
2026
|
+
n(this, "_mouseRA");
|
|
2027
|
+
n(this, "_gamepadRA");
|
|
2028
|
+
/** Logger instance */
|
|
2029
|
+
n(this, "_log");
|
|
2030
|
+
this._eventBus = e, this._log = (s == null ? void 0 : s.getLogger("UserInputManager")) || new l("UserInputManager"), this._log.info("Initializing UserInputManager"), this._log.debug("Initializing input resource access classes"), this._keyboardRA = new ee(), this._mouseRA = new te(t), this._gamepadRA = new se(), this._log.debug("Registering input event callbacks"), this._keyboardRA.onDeviceConnected(this._publishDeviceConnected.bind(this)), this._keyboardRA.onInputChanged(this._publishInputChanged.bind(this)), this._mouseRA.onDeviceConnected(this._publishDeviceConnected.bind(this)), this._mouseRA.onInputChanged(this._publishInputChanged.bind(this)), this._gamepadRA.onDeviceConnected(this._publishDeviceConnected.bind(this)), this._gamepadRA.onDeviceDisconnected(this._publishDeviceDisconnected.bind(this)), this._gamepadRA.onInputChanged(this._publishInputChanged.bind(this)), this._log.info("UserInputManager initialized successfully");
|
|
2031
|
+
}
|
|
2032
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
2033
|
+
// Private methods
|
|
2034
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
2035
|
+
/**
|
|
2036
|
+
* Publish device connected event to the event bus
|
|
2037
|
+
*/
|
|
2038
|
+
_publishDeviceConnected(e) {
|
|
2039
|
+
this._log.debug(`Device connected: ${e.id} (${e.name})`);
|
|
2040
|
+
const t = {
|
|
2041
|
+
type: "input:device:connected",
|
|
2042
|
+
payload: { device: e }
|
|
2043
|
+
};
|
|
2044
|
+
this._eventBus.publish(t);
|
|
2045
|
+
}
|
|
2046
|
+
/**
|
|
2047
|
+
* Publish device disconnected event to the event bus
|
|
2048
|
+
*/
|
|
2049
|
+
_publishDeviceDisconnected(e) {
|
|
2050
|
+
this._log.debug(`Device disconnected: ${e.id} (${e.name})`);
|
|
2051
|
+
const t = {
|
|
2052
|
+
type: "input:device:disconnected",
|
|
2053
|
+
payload: { device: e }
|
|
2054
|
+
};
|
|
2055
|
+
this._eventBus.publish(t);
|
|
2056
|
+
}
|
|
2057
|
+
/**
|
|
2058
|
+
* Publish input changed event to the event bus, used by all device types
|
|
2059
|
+
*/
|
|
2060
|
+
_publishInputChanged(e, t) {
|
|
2061
|
+
this._log.trace(`Input changed: ${e.id}`, t);
|
|
2062
|
+
const s = {
|
|
2063
|
+
type: "input:changed",
|
|
2064
|
+
payload: {
|
|
2065
|
+
deviceId: e.id,
|
|
2066
|
+
device: e,
|
|
2067
|
+
state: t
|
|
2068
|
+
}
|
|
2069
|
+
};
|
|
2070
|
+
this._eventBus.publish(s);
|
|
2071
|
+
}
|
|
2072
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
2073
|
+
// Internal methods
|
|
2074
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
2075
|
+
/**
|
|
2076
|
+
* Destroy the input manager and clean up event listeners
|
|
2077
|
+
*/
|
|
2078
|
+
$destroy() {
|
|
2079
|
+
this._log.info("Destroying UserInputManager"), this._log.debug("Cleaning up input resource access instances"), this._keyboardRA.destroy(), this._mouseRA.destroy(), this._gamepadRA.destroy(), this._log.info("UserInputManager destroyed");
|
|
2080
|
+
}
|
|
2081
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
2082
|
+
// Public methods
|
|
2083
|
+
//------------------------------------------------------------------------------------------------------------------
|
|
2084
|
+
/**
|
|
2085
|
+
* Get all input devices
|
|
2086
|
+
*
|
|
2087
|
+
* @return An array of input devices
|
|
2088
|
+
*/
|
|
2089
|
+
listDevices() {
|
|
2090
|
+
this._log.debug("Getting all connected input devices");
|
|
2091
|
+
const e = [];
|
|
2092
|
+
return e.push(this._keyboardRA.getDevice()), e.push(this._mouseRA.getDevice()), e.push(...this._gamepadRA.getDevices()), this._log.debug(`Found ${e.length} connected devices`), e;
|
|
2093
|
+
}
|
|
2094
|
+
/**
|
|
2095
|
+
* Get a specific input device by ID
|
|
2096
|
+
*
|
|
2097
|
+
* @param deviceId - The ID of the device to get
|
|
2098
|
+
*
|
|
2099
|
+
* @return The input device, or null if not found
|
|
2100
|
+
*/
|
|
2101
|
+
getDevice(e) {
|
|
2102
|
+
if (this._log.debug(`Getting device: ${e}`), e.startsWith("keyboard-"))
|
|
2103
|
+
return this._keyboardRA.getDevice();
|
|
2104
|
+
if (e.startsWith("mouse-"))
|
|
2105
|
+
return this._mouseRA.getDevice();
|
|
2106
|
+
if (e.startsWith("gamepad-")) {
|
|
2107
|
+
const t = parseInt(e.split("-")[1], 10);
|
|
2108
|
+
return this._gamepadRA.getDevice(t);
|
|
2109
|
+
} else {
|
|
2110
|
+
const t = this.listDevices();
|
|
2111
|
+
for (const s of t)
|
|
2112
|
+
if (s.id === e)
|
|
2113
|
+
return s;
|
|
2114
|
+
}
|
|
2115
|
+
return this._log.warn(`Device not found: ${e}`), null;
|
|
2116
|
+
}
|
|
2117
|
+
/**
|
|
2118
|
+
* Poll for gamepad state updates - call this in your game loop
|
|
2119
|
+
*/
|
|
2120
|
+
pollGamepads() {
|
|
2121
|
+
this._gamepadRA.pollGamepads();
|
|
2122
|
+
}
|
|
2123
|
+
}
|
|
2124
|
+
async function M(a, e) {
|
|
2125
|
+
const t = new K(a, e);
|
|
2126
|
+
return await t.initAsync(), t;
|
|
2127
|
+
}
|
|
2128
|
+
function v(a, e) {
|
|
2129
|
+
return new U(a, e.antialias, e.options, e.adaptToDeviceRatio);
|
|
2130
|
+
}
|
|
2131
|
+
function ne(a) {
|
|
2132
|
+
return new B(a);
|
|
2133
|
+
}
|
|
2134
|
+
async function oe(a, e) {
|
|
2135
|
+
if (a === null)
|
|
2136
|
+
return console.debug("Using Null Engine"), ne(e);
|
|
2137
|
+
const t = e.forceEngine || "auto";
|
|
2138
|
+
if (t === "webgpu")
|
|
2139
|
+
if ($())
|
|
2140
|
+
try {
|
|
2141
|
+
return console.debug("Using forced WebGPU engine"), await M(a, e);
|
|
2142
|
+
} catch (s) {
|
|
2143
|
+
throw console.error("Forced WebGPU initialization failed:", s), new Error("Forced WebGPU failed to initialize. If WebGPU is required, check browser compatibility.");
|
|
2144
|
+
}
|
|
2145
|
+
else
|
|
2146
|
+
throw new Error("WebGPU was forced but is not available in this browser.");
|
|
2147
|
+
if (t === "webgl")
|
|
2148
|
+
return console.debug("Using forced WebGL engine"), v(a, e);
|
|
2149
|
+
if ($())
|
|
2150
|
+
try {
|
|
2151
|
+
return console.debug("Using WebGPU"), M(a, e).catch((s) => (console.warn("WebGPU initialization failed, falling back to WebGL:", s), v(a, e)));
|
|
2152
|
+
} catch (s) {
|
|
2153
|
+
console.warn("WebGPU initialization failed, falling back to WebGL:", s);
|
|
2154
|
+
}
|
|
2155
|
+
else
|
|
2156
|
+
console.warn("WebGPU not supported, falling back to WebGL.");
|
|
2157
|
+
return console.debug("Using WebGL"), v(a, e);
|
|
2158
|
+
}
|
|
2159
|
+
async function ae() {
|
|
2160
|
+
const a = await z();
|
|
2161
|
+
return new R(!0, a);
|
|
2162
|
+
}
|
|
2163
|
+
async function de(a, e = {}) {
|
|
2164
|
+
const t = new F(e.logLevel || "debug"), s = t.getLogger("SAGE");
|
|
2165
|
+
s.info(`Initializing SAGE Game Engine v${m}...`), s.debug("Creating rendering engine...");
|
|
2166
|
+
const i = await oe(a, e.renderOptions || {});
|
|
2167
|
+
s.debug("Creating physics engine...");
|
|
2168
|
+
const o = await ae();
|
|
2169
|
+
s.debug("Creating event bus...");
|
|
2170
|
+
const r = new O(t);
|
|
2171
|
+
s.debug("Creating scene engine...");
|
|
2172
|
+
const c = new N(i, o, t);
|
|
2173
|
+
s.debug("Creating managers...");
|
|
2174
|
+
const h = new ie(r, a, t), u = new j(r, t), g = new Q(r, t, u), d = new Z(i, c, g, h, t);
|
|
2175
|
+
if (s.info(`SAGE Game Engine v${m} initialized successfully.`), s.debug("Registering default input bindings..."), e.bindings)
|
|
2176
|
+
for (const _ of e.bindings)
|
|
2177
|
+
u.registerBinding(_);
|
|
2178
|
+
return new T(
|
|
2179
|
+
a,
|
|
2180
|
+
i,
|
|
2181
|
+
o,
|
|
2182
|
+
r,
|
|
2183
|
+
t,
|
|
2184
|
+
// Engines
|
|
2185
|
+
{
|
|
2186
|
+
sceneEngine: c
|
|
2187
|
+
},
|
|
2188
|
+
// Managers
|
|
2189
|
+
{
|
|
2190
|
+
bindingManager: u,
|
|
2191
|
+
entityManager: g,
|
|
2192
|
+
gameManager: d,
|
|
2193
|
+
inputManager: h
|
|
2194
|
+
}
|
|
2195
|
+
);
|
|
2196
|
+
}
|
|
2197
|
+
export {
|
|
2198
|
+
W as ConsoleBackend,
|
|
2199
|
+
q as GameEntity,
|
|
2200
|
+
ue as GameEntityBehavior,
|
|
2201
|
+
O as GameEventBus,
|
|
2202
|
+
F as LoggingUtility,
|
|
2203
|
+
P as NullBackend,
|
|
2204
|
+
T as SkewedAspectGameEngine,
|
|
2205
|
+
m as VERSION,
|
|
2206
|
+
de as createGameEngine
|
|
2207
|
+
};
|
|
2208
|
+
//# sourceMappingURL=sage.es.js.map
|