@waica/engine 0.11.0 → 0.13.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/dist/archetype.d.ts +18 -0
- package/dist/audio/audio-subsystem.d.ts +141 -0
- package/dist/audio/audio-subsystem.js +398 -0
- package/dist/audio/backend.d.ts +69 -0
- package/dist/audio/backend.js +1 -0
- package/dist/audio/spatial.d.ts +22 -0
- package/dist/audio/spatial.js +32 -0
- package/dist/audio/types.d.ts +42 -0
- package/dist/audio/types.js +1 -0
- package/dist/audio/web-audio-backend.d.ts +39 -0
- package/dist/audio/web-audio-backend.js +149 -0
- package/dist/component.d.ts +6 -1
- package/dist/game.d.ts +66 -1
- package/dist/game.js +157 -17
- package/dist/index.d.ts +6 -2
- package/dist/index.js +1 -0
- package/dist/runtime-bridge.d.ts +9 -2
- package/dist/runtime-bridge.js +11 -2
- package/dist/runtime-inspection.d.ts +21 -0
- package/dist/runtime-inspection.js +13 -0
- package/dist/scene.d.ts +5 -1
- package/dist/scene.js +9 -2
- package/dist/ui.d.ts +12 -1
- package/dist/ui.js +35 -3
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
export { Game } from './game.js';
|
|
2
|
-
export type { GameOptions, GameResolution, SpawnPrefabOptions, UpdateFn, ParamOverrides, } from './game.js';
|
|
2
|
+
export type { GameOptions, GameResolution, SceneCatalog, SpawnPrefabOptions, UpdateFn, ParamOverrides, } from './game.js';
|
|
3
|
+
export { AudioSubsystem } from './audio/audio-subsystem.js';
|
|
4
|
+
export type { AudioSubsystemOptions } from './audio/audio-subsystem.js';
|
|
5
|
+
export type { AudioBackend, AudioResource, BackendPlayHandle, BackendPlayOptions } from './audio/backend.js';
|
|
6
|
+
export type { AudioChannelState, AudioPlayOptions, LiveSoundInfo, SoundHandle } from './audio/types.js';
|
|
3
7
|
export { installDirectionalAnimation, installedDirectionalAnimation, isAnimationFacingProvider, resolveDirectionalClip, } from './animation/directional.js';
|
|
4
8
|
export type { AnimationFacingProvider, DirectionalAnimation, DirectionalFallback, ResolvedDirectionalClip, } from './animation/directional.js';
|
|
5
9
|
export { isYSortParticipant, ySortZ } from './render-sort.js';
|
|
@@ -25,7 +29,7 @@ export type { PointerCamera, PointerDeps, PointerPick, PointerResolution } from
|
|
|
25
29
|
export { RUNTIME_BRIDGE_CAPABILITIES, RUNTIME_BRIDGE_PROTOCOL_VERSION, RUNTIME_BRIDGE_SYMBOL, RuntimeBridgeOperationError, } from './runtime-bridge.js';
|
|
26
30
|
export type { RuntimeBridge, RuntimeBridgeActivation, RuntimeControlRequest, RuntimeControlResult, RuntimeMetadata, RuntimeMode, } from './runtime-bridge.js';
|
|
27
31
|
export { RUNTIME_PROJECTION_LIMITS } from './runtime-inspection.js';
|
|
28
|
-
export type { ProjectedValue, ProjectionIssue, ProjectionMarker, ProjectionMarkerKind, RuntimeComponentSnapshot, RuntimeEntitySnapshot, RuntimeSnapshot, RuntimeSnapshotFilters, RuntimeTransformSnapshot, } from './runtime-inspection.js';
|
|
32
|
+
export type { ProjectedValue, ProjectionIssue, ProjectionMarker, ProjectionMarkerKind, RuntimeComponentSnapshot, RuntimeEntitySnapshot, RuntimeSnapshot, RuntimeSnapshotAudio, RuntimeSnapshotFilters, RuntimeTransformSnapshot, } from './runtime-inspection.js';
|
|
29
33
|
export type { ArchetypeArt, ArchetypeManifest, BrowserArchetypeManifest, EntityTemplate, } from './archetype.js';
|
|
30
34
|
export { Stats } from './stats.js';
|
|
31
35
|
export type { StatValue } from './stats.js';
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { Game } from './game.js';
|
|
2
|
+
export { AudioSubsystem } from './audio/audio-subsystem.js';
|
|
2
3
|
export { installDirectionalAnimation, installedDirectionalAnimation, isAnimationFacingProvider, resolveDirectionalClip, } from './animation/directional.js';
|
|
3
4
|
export { isYSortParticipant, ySortZ } from './render-sort.js';
|
|
4
5
|
export { projectIsometric, screenInputToLogical, unprojectIsometric } from './projection.js';
|
package/dist/runtime-bridge.d.ts
CHANGED
|
@@ -11,7 +11,7 @@ export type RuntimeMode = 'paused' | 'real-time';
|
|
|
11
11
|
* doesn't distinguish an engine that silently no-ops an unknown operation
|
|
12
12
|
* from one that runs it.
|
|
13
13
|
*/
|
|
14
|
-
export declare const RUNTIME_BRIDGE_CAPABILITIES: readonly ['click'];
|
|
14
|
+
export declare const RUNTIME_BRIDGE_CAPABILITIES: readonly ['click', 'scene'];
|
|
15
15
|
export interface RuntimeMetadata {
|
|
16
16
|
bridgeVersion: typeof RUNTIME_BRIDGE_PROTOCOL_VERSION;
|
|
17
17
|
engineVersion: string;
|
|
@@ -33,6 +33,9 @@ export type RuntimeControlRequest = {
|
|
|
33
33
|
operation: 'click';
|
|
34
34
|
x: number;
|
|
35
35
|
y: number;
|
|
36
|
+
} | {
|
|
37
|
+
operation: 'scene';
|
|
38
|
+
scene: string;
|
|
36
39
|
};
|
|
37
40
|
export interface RuntimeControlResult extends RuntimeMetadata {
|
|
38
41
|
heldActions: string[];
|
|
@@ -40,8 +43,9 @@ export interface RuntimeControlResult extends RuntimeMetadata {
|
|
|
40
43
|
export declare class RuntimeBridgeOperationError extends Error {
|
|
41
44
|
readonly code: 'runtime-invalid-state' | 'runtime-operation-failed';
|
|
42
45
|
readonly availableActions?: string[] | undefined;
|
|
46
|
+
readonly availableScenes?: string[] | undefined;
|
|
43
47
|
readonly stage: 'control';
|
|
44
|
-
constructor(code: 'runtime-invalid-state' | 'runtime-operation-failed', message: string, availableActions?: string[] | undefined);
|
|
48
|
+
constructor(code: 'runtime-invalid-state' | 'runtime-operation-failed', message: string, availableActions?: string[] | undefined, availableScenes?: string[] | undefined);
|
|
45
49
|
}
|
|
46
50
|
/** Engine-owned capability registered only in an MCP-activated page. */
|
|
47
51
|
export interface RuntimeBridge {
|
|
@@ -66,6 +70,9 @@ export interface RuntimeBridgeHost {
|
|
|
66
70
|
heldActions(): string[];
|
|
67
71
|
inspect(metadata: RuntimeMetadata, filters?: RuntimeSnapshotFilters): RuntimeSnapshot;
|
|
68
72
|
click(x: number, y: number): void;
|
|
73
|
+
/** Resolves `name` through the registered catalog and loads it. */
|
|
74
|
+
loadScene(name: string): boolean;
|
|
75
|
+
availableScenes(): string[];
|
|
69
76
|
}
|
|
70
77
|
export declare class EngineRuntimeBridge implements RuntimeBridge {
|
|
71
78
|
readonly surface: HTMLCanvasElement;
|
package/dist/runtime-bridge.js
CHANGED
|
@@ -10,15 +10,17 @@ export const RUNTIME_BRIDGE_SYMBOL = Symbol.for('@waica/runtime-bridge/v1');
|
|
|
10
10
|
* doesn't distinguish an engine that silently no-ops an unknown operation
|
|
11
11
|
* from one that runs it.
|
|
12
12
|
*/
|
|
13
|
-
export const RUNTIME_BRIDGE_CAPABILITIES = ['click'];
|
|
13
|
+
export const RUNTIME_BRIDGE_CAPABILITIES = ['click', 'scene'];
|
|
14
14
|
export class RuntimeBridgeOperationError extends Error {
|
|
15
15
|
code;
|
|
16
16
|
availableActions;
|
|
17
|
+
availableScenes;
|
|
17
18
|
stage = 'control';
|
|
18
|
-
constructor(code, message, availableActions) {
|
|
19
|
+
constructor(code, message, availableActions, availableScenes) {
|
|
19
20
|
super(message);
|
|
20
21
|
this.code = code;
|
|
21
22
|
this.availableActions = availableActions;
|
|
23
|
+
this.availableScenes = availableScenes;
|
|
22
24
|
this.name = 'RuntimeBridgeOperationError';
|
|
23
25
|
}
|
|
24
26
|
}
|
|
@@ -106,6 +108,13 @@ export class EngineRuntimeBridge {
|
|
|
106
108
|
this.host.click(request.x, request.y);
|
|
107
109
|
break;
|
|
108
110
|
}
|
|
111
|
+
case 'scene': {
|
|
112
|
+
if (!this.host.loadScene(request.scene)) {
|
|
113
|
+
const available = this.host.availableScenes();
|
|
114
|
+
throw new RuntimeBridgeOperationError('runtime-operation-failed', `Unknown scene "${request.scene}". Available scenes: ${available.join(', ') || '(none)'}.`, undefined, available);
|
|
115
|
+
}
|
|
116
|
+
break;
|
|
117
|
+
}
|
|
109
118
|
default: {
|
|
110
119
|
const unsupported = request;
|
|
111
120
|
throw new RuntimeBridgeOperationError('runtime-operation-failed', `Unsupported runtime control operation "${unsupported.operation}".`);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { AudioChannelState, LiveSoundInfo } from './audio/types.js';
|
|
1
2
|
import type { Game } from './game.js';
|
|
2
3
|
import type { RuntimeMetadata } from './runtime-bridge.js';
|
|
3
4
|
import type { StatValue } from './stats.js';
|
|
@@ -48,10 +49,29 @@ export interface RuntimeEntitySnapshot {
|
|
|
48
49
|
transform: RuntimeTransformSnapshot;
|
|
49
50
|
components: RuntimeComponentSnapshot[];
|
|
50
51
|
}
|
|
52
|
+
/**
|
|
53
|
+
* The mixer's state (CA-15): `master` and every channel's volume/mute,
|
|
54
|
+
* sorted by name, plus `playing` — `game.audio.liveSounds()` verbatim,
|
|
55
|
+
* already sorted (by uri then channel). Despite the name, `playing` is not
|
|
56
|
+
* "every currently-audible sound": per `liveSounds()`'s own docstring it
|
|
57
|
+
* also carries a loop retained before the autoplay unlock, a sound still
|
|
58
|
+
* loading, and even one about to fail to load (gone a tick later). Emitted
|
|
59
|
+
* unconditionally, like every other snapshot section —
|
|
60
|
+
* `[DEVIATION 2026-09-08]` in the spec: no section of RuntimeSnapshot is
|
|
61
|
+
* filterable today, so audio does not invent the first one.
|
|
62
|
+
*/
|
|
63
|
+
export interface RuntimeSnapshotAudio {
|
|
64
|
+
master: number;
|
|
65
|
+
channels: Record<string, AudioChannelState>;
|
|
66
|
+
playing: LiveSoundInfo[];
|
|
67
|
+
}
|
|
51
68
|
export interface RuntimeSnapshot extends RuntimeMetadata {
|
|
52
69
|
stats: Record<string, StatValue>;
|
|
70
|
+
/** The live scene's name (its catalog key), or null with no scene loaded. */
|
|
71
|
+
scene: string | null;
|
|
53
72
|
entities: RuntimeEntitySnapshot[];
|
|
54
73
|
projectionIssues: ProjectionIssue[];
|
|
74
|
+
audio: RuntimeSnapshotAudio;
|
|
55
75
|
}
|
|
56
76
|
export declare const RUNTIME_PROJECTION_LIMITS: {
|
|
57
77
|
readonly depth: 5;
|
|
@@ -66,6 +86,7 @@ export declare class RuntimeInspector {
|
|
|
66
86
|
private nextId;
|
|
67
87
|
constructor(game: Game);
|
|
68
88
|
snapshot(metadata: RuntimeMetadata, filters?: RuntimeSnapshotFilters): RuntimeSnapshot;
|
|
89
|
+
private audioSnapshot;
|
|
69
90
|
private capSnapshot;
|
|
70
91
|
private idFor;
|
|
71
92
|
}
|
|
@@ -234,10 +234,23 @@ export class RuntimeInspector {
|
|
|
234
234
|
return this.capSnapshot({
|
|
235
235
|
...metadata,
|
|
236
236
|
stats: Object.fromEntries([...this.game.stats.entries()].sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))),
|
|
237
|
+
scene: this.game.sceneName,
|
|
237
238
|
entities,
|
|
238
239
|
projectionIssues,
|
|
240
|
+
audio: this.audioSnapshot(),
|
|
239
241
|
});
|
|
240
242
|
}
|
|
243
|
+
audioSnapshot() {
|
|
244
|
+
const channels = {};
|
|
245
|
+
for (const name of this.game.audio.channels().sort()) {
|
|
246
|
+
channels[name] = this.game.audio.channelState(name);
|
|
247
|
+
}
|
|
248
|
+
return {
|
|
249
|
+
master: this.game.audio.master,
|
|
250
|
+
channels,
|
|
251
|
+
playing: this.game.audio.liveSounds(),
|
|
252
|
+
};
|
|
253
|
+
}
|
|
241
254
|
capSnapshot(snapshot) {
|
|
242
255
|
if (utf8Bytes(JSON.stringify(snapshot)) <= RUNTIME_PROJECTION_LIMITS.snapshotBytes) {
|
|
243
256
|
return snapshot;
|
package/dist/scene.d.ts
CHANGED
|
@@ -88,5 +88,9 @@ export declare function resolveProps(props: Record<string, unknown> | undefined,
|
|
|
88
88
|
export declare function resolveEntityComponents(entity: SceneEntityJson, prefabs?: Record<string, PrefabJson>): SceneComponentJson[];
|
|
89
89
|
/** Instantiates a scene entity into the game. */
|
|
90
90
|
export declare function spawnFromJson(game: Game, json: SceneEntityJson, registry: SceneRegistry): Entity;
|
|
91
|
-
/**
|
|
91
|
+
/**
|
|
92
|
+
* Loads a full scene into the game, replacing whatever scene is already
|
|
93
|
+
* live (Game.unloadScene() first) — see ADR 0011. A Game shows one scene
|
|
94
|
+
* at a time.
|
|
95
|
+
*/
|
|
92
96
|
export declare function loadScene(game: Game, scene: SceneJson, registry: SceneRegistry): void;
|
package/dist/scene.js
CHANGED
|
@@ -79,8 +79,14 @@ export function spawnFromJson(game, json, registry) {
|
|
|
79
79
|
}
|
|
80
80
|
return entity;
|
|
81
81
|
}
|
|
82
|
-
/**
|
|
82
|
+
/**
|
|
83
|
+
* Loads a full scene into the game, replacing whatever scene is already
|
|
84
|
+
* live (Game.unloadScene() first) — see ADR 0011. A Game shows one scene
|
|
85
|
+
* at a time.
|
|
86
|
+
*/
|
|
83
87
|
export function loadScene(game, scene, registry) {
|
|
88
|
+
if (game.registry)
|
|
89
|
+
game.unloadScene();
|
|
84
90
|
game.registry = registry;
|
|
85
91
|
game.setSceneRender(scene.render);
|
|
86
92
|
for (const entityJson of scene.entities)
|
|
@@ -89,6 +95,7 @@ export function loadScene(game, scene, registry) {
|
|
|
89
95
|
game.setSceneCamera(scene.camera);
|
|
90
96
|
if (registry.ui)
|
|
91
97
|
game.ui.defineAll(registry.ui);
|
|
98
|
+
// Scene-scoped: Game.unloadScene() unmounts these along with the entities.
|
|
92
99
|
for (const name of scene.ui ?? [])
|
|
93
|
-
game.ui.show(name);
|
|
100
|
+
game.ui.show(name, { scope: 'scene' });
|
|
94
101
|
}
|
package/dist/ui.d.ts
CHANGED
|
@@ -27,7 +27,7 @@ export declare class GameUi {
|
|
|
27
27
|
defineAll(pieces: Record<string, string>): void;
|
|
28
28
|
/** Piece names available to show (defined via the registry or define()). */
|
|
29
29
|
names(): string[];
|
|
30
|
-
show(name: string): void;
|
|
30
|
+
show(name: string, options?: ShowOptions): void;
|
|
31
31
|
hide(name: string): void;
|
|
32
32
|
toggle(name: string): void;
|
|
33
33
|
isVisible(name: string): boolean;
|
|
@@ -41,7 +41,18 @@ export declare class GameUi {
|
|
|
41
41
|
setActive(active: boolean): void;
|
|
42
42
|
/** Unmounts every piece and removes the overlay (Game.dispose). */
|
|
43
43
|
dispose(): void;
|
|
44
|
+
/**
|
|
45
|
+
* Unmounts every scene-scoped piece: the ones `loadScene` showed from the
|
|
46
|
+
* outgoing scene's `ui` list, plus any shown with `{ scope: 'scene' }`.
|
|
47
|
+
* A piece the host showed with no scope is untouched. The definition
|
|
48
|
+
* catalog (sources) always survives — Game.unloadScene.
|
|
49
|
+
*/
|
|
50
|
+
unloadScene(): void;
|
|
44
51
|
private mount;
|
|
45
52
|
private mountOverlay;
|
|
46
53
|
private sync;
|
|
47
54
|
}
|
|
55
|
+
export interface ShowOptions {
|
|
56
|
+
/** 'scene': unmounted by Game.unloadScene() along with the rest of the scene. */
|
|
57
|
+
scope?: 'scene';
|
|
58
|
+
}
|
package/dist/ui.js
CHANGED
|
@@ -35,10 +35,19 @@ export class GameUi {
|
|
|
35
35
|
names() {
|
|
36
36
|
return [...this.sources.keys()];
|
|
37
37
|
}
|
|
38
|
-
show(name) {
|
|
38
|
+
show(name, options = {}) {
|
|
39
|
+
const mounted = this.pieces.has(name);
|
|
39
40
|
const piece = this.mount(name);
|
|
40
|
-
if (piece)
|
|
41
|
+
if (piece) {
|
|
41
42
|
piece.visible = true;
|
|
43
|
+
// Scope belongs to whoever mounts the piece, and a later show never
|
|
44
|
+
// changes it. Otherwise a scene whose `ui` list happens to name a piece
|
|
45
|
+
// the host already mounted would quietly take ownership of it and
|
|
46
|
+
// destroy it on the next unload — a session-scoped HUD dying with a
|
|
47
|
+
// map it merely shares a name with.
|
|
48
|
+
if (!mounted)
|
|
49
|
+
piece.scope = options.scope;
|
|
50
|
+
}
|
|
42
51
|
this.sync();
|
|
43
52
|
}
|
|
44
53
|
hide(name) {
|
|
@@ -81,6 +90,23 @@ export class GameUi {
|
|
|
81
90
|
this.overlay?.remove();
|
|
82
91
|
this.overlay = undefined;
|
|
83
92
|
}
|
|
93
|
+
/**
|
|
94
|
+
* Unmounts every scene-scoped piece: the ones `loadScene` showed from the
|
|
95
|
+
* outgoing scene's `ui` list, plus any shown with `{ scope: 'scene' }`.
|
|
96
|
+
* A piece the host showed with no scope is untouched. The definition
|
|
97
|
+
* catalog (sources) always survives — Game.unloadScene.
|
|
98
|
+
*/
|
|
99
|
+
unloadScene() {
|
|
100
|
+
for (const [name, piece] of this.pieces) {
|
|
101
|
+
if (piece.scope !== 'scene')
|
|
102
|
+
continue;
|
|
103
|
+
for (const off of piece.unsubs)
|
|
104
|
+
off();
|
|
105
|
+
piece.shell.remove();
|
|
106
|
+
this.pieces.delete(name);
|
|
107
|
+
}
|
|
108
|
+
this.sync();
|
|
109
|
+
}
|
|
84
110
|
mount(name) {
|
|
85
111
|
const existing = this.pieces.get(name);
|
|
86
112
|
if (existing)
|
|
@@ -100,7 +126,13 @@ export class GameUi {
|
|
|
100
126
|
root.style.display = 'contents';
|
|
101
127
|
root.innerHTML = html;
|
|
102
128
|
shadow.append(root);
|
|
103
|
-
const piece = {
|
|
129
|
+
const piece = {
|
|
130
|
+
shell,
|
|
131
|
+
root,
|
|
132
|
+
visible: false,
|
|
133
|
+
scope: undefined,
|
|
134
|
+
unsubs: bindStats(root, this.stats),
|
|
135
|
+
};
|
|
104
136
|
this.mountOverlay().append(shell);
|
|
105
137
|
this.pieces.set(name, piece);
|
|
106
138
|
return piece;
|