@zylem/game-lib 0.8.0 → 0.8.3

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.
Files changed (81) hide show
  1. package/dist/actions.d.ts +6 -6
  2. package/dist/api/game-lib.api.json +3105 -1223
  3. package/dist/api/game-lib.api.md +197 -96
  4. package/dist/api/game-lib.d.ts +448 -96
  5. package/dist/api/game-lib.typedoc.json +134986 -201814
  6. package/dist/{base-node-life-cycle-D2NuN1VB.d.ts → base-node-life-cycle-BK9xebT9.d.ts} +6 -0
  7. package/dist/bridge.d.ts +97 -0
  8. package/dist/bridge.js +1 -0
  9. package/dist/chunk-2VSVOPQN.js +19 -0
  10. package/dist/chunk-3I537JFL.js +1 -0
  11. package/dist/chunk-47DG7I7M.js +1 -0
  12. package/dist/chunk-7QF6YTEZ.js +1 -0
  13. package/dist/chunk-AIQ6IVT5.js +1 -0
  14. package/dist/chunk-C5NEGC3S.js +1 -0
  15. package/dist/chunk-CV75NSES.js +1 -0
  16. package/dist/chunk-EVYHPR3J.js +1 -0
  17. package/dist/chunk-GKHAJ3MW.js +1 -0
  18. package/dist/chunk-HNCTOJP7.js +1 -0
  19. package/dist/{chunk-M3XNO56P.js → chunk-JNFFTNMH.js} +1 -1
  20. package/dist/{chunk-XNDS4YFR.js → chunk-LGS5N5M4.js} +1 -1
  21. package/dist/chunk-LKFF6XJC.js +1 -0
  22. package/dist/{chunk-RXDTAWI2.js → chunk-PCFF4LLO.js} +1 -1
  23. package/dist/chunk-Q27ULMXY.js +24 -0
  24. package/dist/chunk-TV3AZKT4.js +9 -0
  25. package/dist/chunk-UG22Z42P.js +1 -0
  26. package/dist/chunk-W5SXED5T.js +1 -0
  27. package/dist/chunk-WVSYUWTT.js +1 -0
  28. package/dist/chunk-YAOXFYOD.js +1 -0
  29. package/dist/{collision-builder-jF-toPIl.d.ts → collision-builder-B_tm8OVy.d.ts} +13 -2
  30. package/dist/core.d.ts +15 -15
  31. package/dist/core.js +1 -1
  32. package/dist/debug-state-Dp9CQQZ4.d.ts +49 -0
  33. package/dist/debug.d.ts +27 -8
  34. package/dist/debug.js +1 -1
  35. package/dist/{default-controls-0-vCnli7.d.ts → default-controls-CAKJ5zlo.d.ts} +2 -2
  36. package/dist/{entity-DZbFQ1Wg.d.ts → entity-B9yZeBm4.d.ts} +7 -1
  37. package/dist/{entity-BdSI8CL3.d.ts → entity-BGEq1yfC.d.ts} +32 -4
  38. package/dist/{entity-type-map-Bd69SpAS.d.ts → entity-type-map-CW6RHQkx.d.ts} +5 -5
  39. package/dist/entity.d.ts +12 -10
  40. package/dist/entity.js +1 -1
  41. package/dist/events.d.ts +2 -65
  42. package/dist/events.js +1 -1
  43. package/dist/{game-C1FHVqXl.d.ts → game-NHkubIfI.d.ts} +89 -32
  44. package/dist/game-default-5TB3T5N7.js +1 -0
  45. package/dist/{game-interfaces-mchNYSZE.d.ts → game-interfaces-vF5sRi1n.d.ts} +7 -1
  46. package/dist/globals.d.ts +1 -1
  47. package/dist/graphics.d.ts +5 -5
  48. package/dist/input-ui.d.ts +4 -4
  49. package/dist/input-ui.js +1 -1
  50. package/dist/input.d.ts +47 -4
  51. package/dist/input.js +1 -1
  52. package/dist/{material-9vJ6SZaI.d.ts → material-DbSuZBbP.d.ts} +1 -1
  53. package/dist/public.d.ts +16 -14
  54. package/dist/public.js +1 -1
  55. package/dist/runtime.d.ts +3 -3
  56. package/dist/runtime.js +1 -1
  57. package/dist/schema/game-config.schema.json +18 -0
  58. package/dist/schema/input-config.schema.json +18 -0
  59. package/dist/schema.d.ts +12 -0
  60. package/dist/schema.js +1 -1
  61. package/dist/{vector-CiSHbivc.d.ts → vector-DN6zlPoE.d.ts} +28 -0
  62. package/dist/web-components.d.ts +12 -12
  63. package/dist/web-components.js +1 -1
  64. package/dist/{zylem-camera-C3bn6BTI.d.ts → zylem-camera-Du2UPW9O.d.ts} +13 -1
  65. package/package.json +8 -3
  66. package/dist/chunk-7FR5JSQ2.js +0 -1
  67. package/dist/chunk-COM6HJ2L.js +0 -1
  68. package/dist/chunk-E3UBSUPX.js +0 -1
  69. package/dist/chunk-FKIKN7RP.js +0 -24
  70. package/dist/chunk-G4HONLWC.js +0 -1
  71. package/dist/chunk-IKWE2W6L.js +0 -1
  72. package/dist/chunk-JEHFHUH4.js +0 -0
  73. package/dist/chunk-JNKPTL77.js +0 -1
  74. package/dist/chunk-JQKS42G4.js +0 -1
  75. package/dist/chunk-MSC2BEO5.js +0 -1
  76. package/dist/chunk-TAUWDQZF.js +0 -9
  77. package/dist/chunk-VS2D4B3W.js +0 -1
  78. package/dist/chunk-XNHVRFAK.js +0 -1
  79. package/dist/chunk-YHEWKN7N.js +0 -1
  80. package/dist/debug-state-BPT6jEog.d.ts +0 -17
  81. package/dist/game-default-QHROHW2O.js +0 -1
@@ -1,4 +1,5 @@
1
1
  import { abs } from 'three/tsl';
2
+ import { announceBridgeReady } from '@zylem/bridge';
2
3
  import { asin } from 'three/tsl';
3
4
  import { atan } from 'three/tsl';
4
5
  import { BehaviorDescriptor } from '@zylem/behaviors';
@@ -20,6 +21,14 @@ import { BoundaryRicochet3DEntity } from '@zylem/behaviors';
20
21
  import { BoundaryRicochetCoordinator } from '@zylem/behaviors';
21
22
  import { BoundaryRicochetEntity } from '@zylem/behaviors';
22
23
  import { BoundaryRicochetUpdate } from '@zylem/behaviors';
24
+ import { BRIDGE_READY_EVENT } from '@zylem/bridge';
25
+ import { BridgeChannel } from '@zylem/bridge';
26
+ import { BridgeDebugTool } from '@zylem/bridge';
27
+ import { BridgeHandler } from '@zylem/bridge';
28
+ import { GameLoadingPayload as BridgeLoadingPayload } from '@zylem/bridge';
29
+ import { BridgeMessages } from '@zylem/bridge';
30
+ import { BridgeMessageType } from '@zylem/bridge';
31
+ import { BridgeVec3 } from '@zylem/bridge';
23
32
  import { BufferGeometry } from 'three';
24
33
  import { BurstParticlePresetOptions } from '@zylem/behaviors';
25
34
  import { Camera } from 'three';
@@ -71,11 +80,14 @@ import { Destructible3DHandle } from '@zylem/behaviors';
71
80
  import { DestructibleMesh } from '@zylem/behaviors';
72
81
  import { DestructiblePrebakeWorkerAPI } from '@zylem/behaviors';
73
82
  import { dot } from 'three/tsl';
83
+ import { EditorToGameMessages } from '@zylem/bridge';
74
84
  import { emitRuntimeGoal } from '@zylem/behaviors/runtime-2d';
75
85
  import { emitRuntimeRicochet } from '@zylem/behaviors/runtime-2d';
76
86
  import { emitRuntimeTriggerRegionEnter } from '@zylem/behaviors/runtime-2d';
77
87
  import { EntityDefinition } from '@zylem/behaviors/core';
78
88
  import { EntityHandle } from '@zylem/behaviors/core';
89
+ import { EntitySummaryPayload } from '@zylem/bridge';
90
+ import { EntityThumbnailPayload } from '@zylem/bridge';
79
91
  import { exp } from 'three/tsl';
80
92
  import { FacingAngle2DEntity } from '@zylem/behaviors';
81
93
  import { fireCooldown } from '@zylem/behaviors';
@@ -102,11 +114,16 @@ import { FogExp2 } from 'three';
102
114
  import { fract } from 'three/tsl';
103
115
  import { FractureOptions } from '@zylem/behaviors';
104
116
  import { FractureOptionsInput } from '@zylem/behaviors';
117
+ import { GameConfigPayload } from '@zylem/bridge';
118
+ import { GameNoticePayload } from '@zylem/bridge';
119
+ import { GameToEditorMessages } from '@zylem/bridge';
120
+ import { GameVariablePayload } from '@zylem/bridge';
105
121
  import { getCooldown } from '@zylem/behaviors';
106
122
  import { getCooldownStore } from '@zylem/behaviors';
107
123
  import { getEntityFacingAngle2D } from '@zylem/behaviors';
108
124
  import { getPosition } from '@zylem/behaviors/core';
109
125
  import { getVelocity } from '@zylem/behaviors/core';
126
+ import { getZylemBridge } from '@zylem/bridge';
110
127
  import { Group } from 'three';
111
128
  import { hasAnyWorldBoundary2DHit } from '@zylem/behaviors';
112
129
  import { hasAnyWorldBoundary3DHit } from '@zylem/behaviors';
@@ -302,6 +319,7 @@ import { StageBodyKindValue } from '@zylem/behaviors/core';
302
319
  import { StageBoundaryDim } from '@zylem/behaviors/core';
303
320
  import { StageColliderConfig } from '@zylem/behaviors/core';
304
321
  import { StageColliderShape } from '@zylem/behaviors/core';
322
+ import { StageConfigPayload } from '@zylem/bridge';
305
323
  import { StageDebugRender } from '@zylem/behaviors/core';
306
324
  import { StageEvent } from '@zylem/behaviors/core';
307
325
  import { StageEventType } from '@zylem/behaviors/core';
@@ -310,6 +328,7 @@ import { StageRaycastHit } from '@zylem/behaviors/core';
310
328
  import { StageRenderSlot } from '@zylem/behaviors/core';
311
329
  import { StageRicochetDim } from '@zylem/behaviors/core';
312
330
  import { StageRicochetReflection } from '@zylem/behaviors/core';
331
+ import { StageSnapshotPayload } from '@zylem/bridge';
313
332
  import { StageTopDownPlane } from '@zylem/behaviors/core';
314
333
  import { Static } from '@sinclair/typebox';
315
334
  import { step } from 'three/tsl';
@@ -374,6 +393,7 @@ import { WorldBoundary3DPosition } from '@zylem/behaviors';
374
393
  import { WorldBoundary3DState } from '@zylem/behaviors';
375
394
  import { wrapAround3D } from '@zylem/behaviors/core';
376
395
  import { wrapAroundXY } from '@zylem/behaviors/core';
396
+ import { ZylemBridge } from '@zylem/bridge';
377
397
 
378
398
  export { abs }
379
399
 
@@ -439,6 +459,8 @@ declare type AnimationOptions = {
439
459
  fadeDuration?: number;
440
460
  };
441
461
 
462
+ export { announceBridgeReady }
463
+
442
464
  declare type AnyNode = NodeLike | Promise<NodeLike>;
443
465
 
444
466
  /**
@@ -518,6 +540,12 @@ declare interface BaseCollisionOptions {
518
540
  collisionType?: string;
519
541
  /** Allowed collision types for filtering */
520
542
  collisionFilter?: string[];
543
+ /** Gravity scale applied at body spawn (no live setter FFI; default 1). */
544
+ gravityScale?: number;
545
+ /** Lock rotation axes at body spawn: `true` for all, or per-axis tuple. */
546
+ lockRotations?: LockAxesInput;
547
+ /** Lock translation axes at body spawn: `true` for all, or per-axis tuple. */
548
+ lockTranslations?: LockAxesInput;
521
549
  }
522
550
 
523
551
  /**
@@ -674,6 +702,22 @@ declare interface BoxMeshOptions extends BaseMeshOptions {
674
702
 
675
703
  declare type BoxOptions = BaseNode | Partial<ZylemBoxOptions>;
676
704
 
705
+ export { BRIDGE_READY_EVENT }
706
+
707
+ export { BridgeChannel }
708
+
709
+ export { BridgeDebugTool }
710
+
711
+ export { BridgeHandler }
712
+
713
+ export { BridgeLoadingPayload }
714
+
715
+ export { BridgeMessages }
716
+
717
+ export { BridgeMessageType }
718
+
719
+ export { BridgeVec3 }
720
+
677
721
  /** Cuboid collider sized as `size.xyz` (full extents — half-extents are computed). */
678
722
  export declare function buildBoxCollider(size: Vec3Input, options?: RuntimeColliderOptions): StageColliderConfig;
679
723
 
@@ -801,6 +845,10 @@ export declare class CameraManager {
801
845
  private _activeCameras;
802
846
  /** Auto-created debug camera with orbit controls */
803
847
  private _debugCamera;
848
+ /** Gameplay primary camera saved while the debug camera is active. */
849
+ private _preDebugPrimary;
850
+ /** Scene anchor at world origin for debug orbit when nothing is selected. */
851
+ private _debugOrbitTargetAnchor;
804
852
  /** Reference to the shared renderer manager */
805
853
  private _rendererManager;
806
854
  /** Scene reference */
@@ -820,6 +868,10 @@ export declare class CameraManager {
820
868
  * Get the debug camera.
821
869
  */
822
870
  get debugCamera(): ZylemCamera | null;
871
+ /**
872
+ * Gameplay primary camera that was active before debug mode was enabled.
873
+ */
874
+ get preDebugPrimary(): ZylemCamera | null;
823
875
  /**
824
876
  * Get all registered cameras.
825
877
  */
@@ -863,6 +915,16 @@ export declare class CameraManager {
863
915
  * Also creates the debug camera.
864
916
  */
865
917
  setup(scene: Scene, rendererManager: RendererManager): Promise<void>;
918
+ /**
919
+ * Switch the primary viewport to the debug orbit camera, remembering the
920
+ * previous primary so gameplay cameras (e.g. first-person) can be restored.
921
+ */
922
+ activateDebugCamera(): boolean;
923
+ /**
924
+ * Restore the gameplay primary camera that was active before
925
+ * {@link activateDebugCamera}.
926
+ */
927
+ deactivateDebugCamera(): boolean;
866
928
  /**
867
929
  * Update all active cameras' controllers.
868
930
  */
@@ -884,8 +946,14 @@ export declare class CameraManager {
884
946
  dispose(): void;
885
947
  /**
886
948
  * Create the always-available debug camera with orbit controls.
949
+ * Registered for lookup/activation, but not added to the active list.
887
950
  */
888
951
  private createDebugCamera;
952
+ /**
953
+ * Seed the debug camera orbit pose on first activation, framing the origin
954
+ * or offsetting from the saved gameplay primary.
955
+ */
956
+ private seedDebugCameraPose;
889
957
  /**
890
958
  * Resolve a camera from a name or reference.
891
959
  */
@@ -1157,6 +1225,9 @@ export declare function clearGlobalSubscriptions(): void;
1157
1225
  declare class CollisionBuilder {
1158
1226
  static: boolean;
1159
1227
  sensor: boolean;
1228
+ gravityScale?: number;
1229
+ lockRotations?: LockAxesInput;
1230
+ lockTranslations?: LockAxesInput;
1160
1231
  build(options: Partial<CollisionOptions>): [SimulationBodyDefinition, SimulationColliderDefinition];
1161
1232
  withCollision(collisionOptions: Partial<CollisionOptions>): this;
1162
1233
  collider(options: CollisionOptions): SimulationColliderDefinition;
@@ -1214,6 +1285,12 @@ declare interface CollisionOptions {
1214
1285
  position?: Vec3Input;
1215
1286
  collisionType?: string;
1216
1287
  collisionFilter?: string[];
1288
+ /** Gravity scale applied at body spawn (no live setter FFI; default 1). */
1289
+ gravityScale?: number;
1290
+ /** Lock rotation axes at body spawn: `true` for all, or per-axis tuple. */
1291
+ lockRotations?: LockAxesInput;
1292
+ /** Lock translation axes at body spawn: `true` for all, or per-axis tuple. */
1293
+ lockTranslations?: LockAxesInput;
1217
1294
  }
1218
1295
 
1219
1296
  declare type CollisionPhase = 'enter' | 'stay';
@@ -1385,8 +1462,10 @@ export declare function createEntity<T extends GameEntity<any>, CreateOptions ex
1385
1462
  * const asteroidFactory = createEntityFactory(asteroidTemplate);
1386
1463
  * const asteroids = asteroidFactory.generate(10);
1387
1464
  *
1388
- * asteroids.forEach((asteroid, i) => {
1389
- * asteroid.setPosition(Math.random() * 20 - 10, Math.random() * 15 - 7.5, 0);
1465
+ * asteroids.forEach((asteroid) => {
1466
+ * asteroid.onSetup(({ me }) => {
1467
+ * me.setPosition(Math.random() * 20 - 10, Math.random() * 15 - 7.5, 0);
1468
+ * });
1390
1469
  * stage.add(asteroid);
1391
1470
  * });
1392
1471
  * ```
@@ -1621,6 +1700,9 @@ declare interface CylinderMeshOptions extends BaseMeshOptions {
1621
1700
 
1622
1701
  declare type CylinderOptions = BaseNode | Partial<ZylemCylinderOptions>;
1623
1702
 
1703
+ /** Resolves a UUID to the live entity owned by the active stage. */
1704
+ export declare type DebugEntityResolver = (uuid: string) => GameEntity<any> | null;
1705
+
1624
1706
  /**
1625
1707
  * Interface for entities that provide custom debug information
1626
1708
  */
@@ -1632,14 +1714,22 @@ declare interface DebugState {
1632
1714
  enabled: boolean;
1633
1715
  paused: boolean;
1634
1716
  tool: DebugTools;
1635
- selectedEntity: GameEntity<any> | null;
1636
- hoveredEntity: GameEntity<any> | null;
1717
+ /**
1718
+ * UUID of the selected entity, never the entity itself. Valtio deep-proxies
1719
+ * any object assigned into a proxy, so storing a live `GameEntity` here
1720
+ * would walk `group.parent` into the entire THREE scene graph and
1721
+ * `physicsWorldRef` into the Rapier world, freezing the tab and handing
1722
+ * Proxy wrappers to engine code that expects real instances.
1723
+ */
1724
+ selectedEntityId: string | null;
1725
+ /** UUID of the hovered entity. Same constraint as `selectedEntityId`. */
1726
+ hoveredEntityId: string | null;
1637
1727
  flags: Set<string>;
1638
1728
  }
1639
1729
 
1640
1730
  export declare const debugState: DebugState;
1641
1731
 
1642
- export declare type DebugTools = 'select' | 'translate' | 'rotate' | 'scale' | 'delete' | 'none';
1732
+ export declare type DebugTools = 'select' | 'translate' | 'rotate' | 'scale' | 'delete' | 'add' | 'none';
1643
1733
 
1644
1734
  /**
1645
1735
  * Builds a ready-to-use {@link GameInputConfig} that wires up themed
@@ -1763,6 +1853,8 @@ export declare function dispatchStageState(): void;
1763
1853
 
1764
1854
  export { dot }
1765
1855
 
1856
+ export { EditorToGameMessages }
1857
+
1766
1858
  export { emitRuntimeGoal }
1767
1859
 
1768
1860
  export { emitRuntimeRicochet }
@@ -1800,36 +1892,6 @@ declare abstract class EntityCollisionBuilder extends CollisionBuilder {
1800
1892
  abstract collider(options: GameEntityOptions): SimulationColliderDefinition;
1801
1893
  }
1802
1894
 
1803
- /** Payload for entity configuration sent to editor. */
1804
- export declare interface EntityConfigPayload {
1805
- uuid: string;
1806
- name: string;
1807
- type: string;
1808
- position: {
1809
- x: number;
1810
- y: number;
1811
- z: number;
1812
- };
1813
- rotation: {
1814
- x: number;
1815
- y: number;
1816
- z: number;
1817
- };
1818
- scale: {
1819
- x: number;
1820
- y: number;
1821
- z: number;
1822
- };
1823
- /** PNG data URL of a framed entity preview, when available. */
1824
- thumbnail?: string | null;
1825
- /** World-space AABB size used for thumbnail rulers. */
1826
- bounds?: {
1827
- width: number;
1828
- height: number;
1829
- depth: number;
1830
- };
1831
- }
1832
-
1833
1895
  declare interface EntityDebugInfo {
1834
1896
  buildInfo: () => Record<string, string>;
1835
1897
  }
@@ -1988,6 +2050,8 @@ declare interface EntitySpawner {
1988
2050
 
1989
2051
  export declare function entitySpawner(factory: (x: number, y: number) => Promise<any> | GameEntity<any>): EntitySpawner;
1990
2052
 
2053
+ export { EntitySummaryPayload }
2054
+
1991
2055
  /**
1992
2056
  * Generates and caches entity thumbnail previews for the editor entity list.
1993
2057
  * Renders a cloned Object3D into an offscreen RenderTarget on the shared WebGPU renderer.
@@ -1997,15 +2061,30 @@ export declare class EntityThumbnailCache {
1997
2061
  private inFlight;
1998
2062
  private renderer;
1999
2063
  private size;
2000
- constructor(size?: number);
2064
+ private maxEntries;
2065
+ private queue;
2066
+ private activeRenders;
2067
+ constructor(size?: number, maxEntries?: number);
2001
2068
  setRenderer(renderer: WebGPURenderer | null): void;
2002
2069
  get(uuid: string): EntityThumbnailCacheEntry | null;
2003
2070
  invalidate(uuid: string): void;
2004
2071
  clear(): void;
2072
+ /** Number of thumbnails currently cached. */
2073
+ get cacheSize(): number;
2074
+ /** Entities waiting for a render slot. */
2075
+ get pendingCount(): number;
2005
2076
  /**
2006
- * Return a cached thumbnail or generate one asynchronously.
2077
+ * Return a cached thumbnail, or schedule one. Renders are throttled to
2078
+ * {@link MAX_CONCURRENT_RENDERS} at a time behind a bounded queue, so a
2079
+ * burst of spawns cannot flood the renderer with pixel readbacks.
2007
2080
  */
2008
2081
  ensure(uuid: string, object: Object3D | null | undefined): Promise<EntityThumbnailCacheEntry | null>;
2082
+ /** Start queued renders until the concurrency budget is spent. */
2083
+ private drainQueue;
2084
+ /** Move an entry to the end of the Map so it is evicted last. */
2085
+ private touch;
2086
+ /** Evict least-recently-used entries down to the cap. */
2087
+ private evictOverflow;
2009
2088
  private render;
2010
2089
  }
2011
2090
 
@@ -2016,7 +2095,13 @@ export declare interface EntityThumbnailCacheEntry extends EntityThumbnailResult
2016
2095
  uuid: string;
2017
2096
  }
2018
2097
 
2098
+ export { EntityThumbnailPayload }
2099
+
2019
2100
  export declare interface EntityThumbnailResult {
2101
+ /**
2102
+ * Preview image URL. Blob URL when the payload worker is available
2103
+ * (encoded off-thread), PNG data URL on the synchronous fallback path.
2104
+ */
2020
2105
  dataUrl: string;
2021
2106
  bounds: Pick<ObjectBounds, 'width' | 'height' | 'depth'>;
2022
2107
  }
@@ -2188,6 +2273,8 @@ export declare class FirstPersonPerspective implements CameraPerspective {
2188
2273
  look(deltaYaw: number, deltaPitch: number): void;
2189
2274
  /** Set absolute yaw and pitch. Pitch is clamped to the configured limit. */
2190
2275
  setLook(yaw: number, pitch: number): void;
2276
+ /** Set eye height above the followed target (used when syncing from FirstPersonBehavior). */
2277
+ setEyeHeight(height: number): void;
2191
2278
  /** Current yaw in radians. */
2192
2279
  get yaw(): number;
2193
2280
  /** Current pitch in radians. */
@@ -2415,6 +2502,89 @@ export declare class Game<TGlobals extends BaseGlobals> implements IGame<TGlobal
2415
2502
  onLoading(callback: (event: GameLoadingEvent) => void): () => void;
2416
2503
  }
2417
2504
 
2505
+ export declare class GameBridge {
2506
+ private channel;
2507
+ private unsubscribes;
2508
+ private host;
2509
+ /**
2510
+ * Values the editor is already known to hold, either because it commanded
2511
+ * them or because we published them. Anything matching here is skipped, so
2512
+ * an editor command can never echo back as a game update.
2513
+ */
2514
+ private editorKnown;
2515
+ constructor();
2516
+ /**
2517
+ * Subscribe to editor commands and announce the game side as ready.
2518
+ * @param host Live-game context for entity/stage lookups
2519
+ * @param announceTarget DOM node the `zylem:bridge:ready` event bubbles from
2520
+ */
2521
+ connect(host: GameBridgeHost, announceTarget?: EventTarget): void;
2522
+ disconnect(): void;
2523
+ /**
2524
+ * Publish only the parts of `debugState` the editor does not already know.
2525
+ * Values that arrived as editor commands are recorded on `editorKnown`
2526
+ * first, so they are never echoed back.
2527
+ */
2528
+ private publishDebugStateChanges;
2529
+ /**
2530
+ * Whether anything is listening for entity list updates. Building entity
2531
+ * summaries is pure editor overhead, so publishers skip it when no editor
2532
+ * is attached.
2533
+ */
2534
+ wantsEntityUpdates(): boolean;
2535
+ /**
2536
+ * Whether anything is listening for thumbnails. Each thumbnail costs an
2537
+ * offscreen render plus a pixel readback on the renderer the game draws
2538
+ * with, so this gate matters far more than the others.
2539
+ */
2540
+ wantsThumbnails(): boolean;
2541
+ /**
2542
+ * Run a callback when an editor first subscribes to entity data, so the
2543
+ * game can backfill state produced while nothing was listening.
2544
+ *
2545
+ * @returns An unsubscribe function.
2546
+ */
2547
+ onEditorAttached(callback: () => void): () => void;
2548
+ publishConfig(config: GameConfigPayload): void;
2549
+ publishLoading(payload: BridgeLoadingPayload): void;
2550
+ publishStatus(status: {
2551
+ paused?: boolean;
2552
+ debug?: boolean;
2553
+ }): void;
2554
+ publishVariable(payload: GameVariablePayload): void;
2555
+ /** Surface a diagnostic message in the editor console. */
2556
+ publishNotice(level: GameNoticePayload['level'], message: string): void;
2557
+ publishStageSnapshot(snapshot: StageSnapshotPayload): void;
2558
+ /** RAF-coalesced entity add/update stream; merged by uuid per frame. */
2559
+ queueEntityUpsert(entities: EntitySummaryPayload[]): void;
2560
+ /** RAF-coalesced entity removal stream. */
2561
+ queueEntityRemoved(uuids: string[]): void;
2562
+ /** RAF-coalesced thumbnail stream; merged by uuid per frame. */
2563
+ queueThumbnails(thumbnails: EntityThumbnailPayload[]): void;
2564
+ }
2565
+
2566
+ /**
2567
+ * Game-side adapter for the shared editor ↔ game bridge.
2568
+ *
2569
+ * Owns the editor→game command subscriptions (debug/tool/playback, entity
2570
+ * select/focus/transform, stage variables) and exposes typed publish helpers
2571
+ * the running game uses to stream config, stage snapshots, entity upserts,
2572
+ * and thumbnails to the editor. High-frequency publishes go through the
2573
+ * channel's `queue`, which coalesces to at most one message per animation
2574
+ * frame.
2575
+ */
2576
+
2577
+ /**
2578
+ * Host context supplied by the running game so bridge commands can act on
2579
+ * live stage state without the bridge importing game internals.
2580
+ */
2581
+ export declare interface GameBridgeHost {
2582
+ /** Resolve a live entity by UUID from the current stage. */
2583
+ resolveEntity(uuid: string): GameEntity<any> | null;
2584
+ /** Write a stage variable (editor `stage:variable:set`). */
2585
+ setStageVariable?(key: string, value: unknown): void;
2586
+ }
2587
+
2418
2588
  /**
2419
2589
  * GameCanvas is a DOM delegate that owns:
2420
2590
  * - container lookup/creation and styling (including fullscreen centering)
@@ -2502,6 +2672,9 @@ export declare const GameConfigJsonSchema: _sinclair_typebox.TObject<{
2502
2672
  mapping: _sinclair_typebox.TOptional<_sinclair_typebox.TRecord<_sinclair_typebox.TString, _sinclair_typebox.TArray<_sinclair_typebox.TString>>>;
2503
2673
  pointerLock: _sinclair_typebox.TOptional<_sinclair_typebox.TBoolean>;
2504
2674
  sensitivity: _sinclair_typebox.TOptional<_sinclair_typebox.TNumber>;
2675
+ lookMode: _sinclair_typebox.TOptional<_sinclair_typebox.TUnion<[_sinclair_typebox.TLiteral<"delta">, _sinclair_typebox.TLiteral<"screenCenter">]>>;
2676
+ maxLookDegrees: _sinclair_typebox.TOptional<_sinclair_typebox.TNumber>;
2677
+ lookSensitivity: _sinclair_typebox.TOptional<_sinclair_typebox.TNumber>;
2505
2678
  }>>;
2506
2679
  touch: _sinclair_typebox.TOptional<_sinclair_typebox.TObject<{
2507
2680
  enabled: _sinclair_typebox.TOptional<_sinclair_typebox.TUnion<[_sinclair_typebox.TBoolean, _sinclair_typebox.TLiteral<"auto">]>>;
@@ -2622,6 +2795,8 @@ export declare type GameConfigLike = Partial<{
2622
2795
  canvas: HTMLCanvasElement;
2623
2796
  }>;
2624
2797
 
2798
+ export { GameConfigPayload }
2799
+
2625
2800
  export declare type GameDeviceConfig = Partial<{
2626
2801
  /** numeric value or key in AspectRatio */
2627
2802
  aspectRatio: AspectRatioValue | keyof typeof AspectRatio;
@@ -2709,6 +2884,14 @@ export declare class GameEntity<O extends GameEntityOptions> extends BaseNode<O>
2709
2884
  private trackAddedComponents;
2710
2885
  private trackedComponents;
2711
2886
  transformStore: TransformState;
2887
+ /** Hide render target until spawn placement is confirmed. */
2888
+ _spawnPlacementPending: boolean;
2889
+ /** Author passed `position` in create options. */
2890
+ _spawnPositionExplicit: boolean;
2891
+ /** setPosition / setPose assigned a spawn placement during this spawn. */
2892
+ _spawnPlacementReady: boolean;
2893
+ /** True while {@link StageEntityDelegate.spawnEntity} is running for this entity. */
2894
+ _spawnInProgress: boolean;
2712
2895
  moveX: (delta: number) => void;
2713
2896
  moveY: (delta: number) => void;
2714
2897
  moveZ: (delta: number) => void;
@@ -2741,6 +2924,9 @@ export declare class GameEntity<O extends GameEntityOptions> extends BaseNode<O>
2741
2924
  setRotation: (x: number, y: number, z: number) => void;
2742
2925
  getRotation: () => any;
2743
2926
  constructor();
2927
+ private wrapSpawnPlacementMutators;
2928
+ /** Make the entity visible, clearing spawn placement pending state. */
2929
+ reveal(): void;
2744
2930
  /**
2745
2931
  * Read the entity's pose from the wasm simulation. Returns `null` when
2746
2932
  * the entity has not been spawned into the runtime yet.
@@ -2896,6 +3082,17 @@ export declare type GameEntityOptions = {
2896
3082
  color?: Color;
2897
3083
  size?: Vec3Input;
2898
3084
  position?: Vec3Input;
3085
+ /**
3086
+ * Spawn visibility behavior:
3087
+ * - `undefined` (default): hidden while `spawnEntity` runs, revealed once
3088
+ * the spawn finalizes — even if no position was assigned.
3089
+ * - `true` (strict): stays hidden until the first `setPosition`/`setPose`,
3090
+ * for spawner patterns that place the entity frames after spawn.
3091
+ * - `false`: never hidden during spawn.
3092
+ */
3093
+ hideUntilPositioned?: boolean;
3094
+ /** Explicit render visibility. When false, entity stays hidden until {@link GameEntity.reveal}. */
3095
+ visible?: boolean;
2899
3096
  /** Render path: `environment` (bundle), `pack` (instanced), or `none` (default). */
2900
3097
  category?: RenderCategory_2;
2901
3098
  runtime?: RuntimeEntityOptions;
@@ -2926,7 +3123,6 @@ export declare type GameEvents = {
2926
3123
  'debug': {
2927
3124
  enabled: boolean;
2928
3125
  };
2929
- 'state:dispatch': StateDispatchPayload;
2930
3126
  };
2931
3127
 
2932
3128
  export declare interface GameInputConfig {
@@ -2953,6 +3149,9 @@ export declare const GameInputConfigSchema: _sinclair_typebox.TRecord<_sinclair_
2953
3149
  mapping: _sinclair_typebox.TOptional<_sinclair_typebox.TRecord<_sinclair_typebox.TString, _sinclair_typebox.TArray<_sinclair_typebox.TString>>>;
2954
3150
  pointerLock: _sinclair_typebox.TOptional<_sinclair_typebox.TBoolean>;
2955
3151
  sensitivity: _sinclair_typebox.TOptional<_sinclair_typebox.TNumber>;
3152
+ lookMode: _sinclair_typebox.TOptional<_sinclair_typebox.TUnion<[_sinclair_typebox.TLiteral<"delta">, _sinclair_typebox.TLiteral<"screenCenter">]>>;
3153
+ maxLookDegrees: _sinclair_typebox.TOptional<_sinclair_typebox.TNumber>;
3154
+ lookSensitivity: _sinclair_typebox.TOptional<_sinclair_typebox.TNumber>;
2956
3155
  }>>;
2957
3156
  touch: _sinclair_typebox.TOptional<_sinclair_typebox.TObject<{
2958
3157
  enabled: _sinclair_typebox.TOptional<_sinclair_typebox.TUnion<[_sinclair_typebox.TBoolean, _sinclair_typebox.TLiteral<"auto">]>>;
@@ -3041,6 +3240,9 @@ export declare const GameInputPlayerConfigSchema: _sinclair_typebox.TObject<{
3041
3240
  mapping: _sinclair_typebox.TOptional<_sinclair_typebox.TRecord<_sinclair_typebox.TString, _sinclair_typebox.TArray<_sinclair_typebox.TString>>>;
3042
3241
  pointerLock: _sinclair_typebox.TOptional<_sinclair_typebox.TBoolean>;
3043
3242
  sensitivity: _sinclair_typebox.TOptional<_sinclair_typebox.TNumber>;
3243
+ lookMode: _sinclair_typebox.TOptional<_sinclair_typebox.TUnion<[_sinclair_typebox.TLiteral<"delta">, _sinclair_typebox.TLiteral<"screenCenter">]>>;
3244
+ maxLookDegrees: _sinclair_typebox.TOptional<_sinclair_typebox.TNumber>;
3245
+ lookSensitivity: _sinclair_typebox.TOptional<_sinclair_typebox.TNumber>;
3044
3246
  }>>;
3045
3247
  touch: _sinclair_typebox.TOptional<_sinclair_typebox.TObject<{
3046
3248
  enabled: _sinclair_typebox.TOptional<_sinclair_typebox.TUnion<[_sinclair_typebox.TBoolean, _sinclair_typebox.TLiteral<"auto">]>>;
@@ -3138,6 +3340,10 @@ export declare interface GameLoadingPayload extends LoadingEvent {
3138
3340
 
3139
3341
  declare type GameOptions<TGlobals extends BaseGlobals> = Array<ZylemGameConfig<Stage, any, TGlobals> | GameConfigLike | Stage | GameEntityLifeCycle | BaseNode>;
3140
3342
 
3343
+ export { GameToEditorMessages }
3344
+
3345
+ export { GameVariablePayload }
3346
+
3141
3347
  export { getCooldown }
3142
3348
 
3143
3349
  export { getCooldownStore }
@@ -3158,6 +3364,11 @@ export declare function getGlobal<T = unknown>(path: string): T | undefined;
3158
3364
  */
3159
3365
  export declare function getGlobals<T = Record<string, unknown>>(): T;
3160
3366
 
3367
+ /** The live hovered entity, resolved on demand through the active stage. */
3368
+ export declare function getHoveredEntity(): GameEntity<any> | null;
3369
+
3370
+ export declare function getHoveredEntityId(): string | null;
3371
+
3161
3372
  export { getPosition }
3162
3373
 
3163
3374
  export declare function getPresetResolution(preset: RetroPresetKey, key?: string): RetroResolution | undefined;
@@ -3167,6 +3378,11 @@ export declare function getPresetResolution(preset: RetroPresetKey, key?: string
3167
3378
  */
3168
3379
  export declare function getRotation(entity: RotatableEntity): any;
3169
3380
 
3381
+ /** The live selected entity, resolved on demand through the active stage. */
3382
+ export declare function getSelectedEntity(): GameEntity<any> | null;
3383
+
3384
+ export declare function getSelectedEntityId(): string | null;
3385
+
3170
3386
  /**
3171
3387
  * Get a variable from an object by path.
3172
3388
  * @example getVariable(stage1, 'totalAngle') // 0.5
@@ -3176,6 +3392,8 @@ export declare function getVariable<T = unknown>(target: object, path: string):
3176
3392
 
3177
3393
  export { getVelocity }
3178
3394
 
3395
+ export { getZylemBridge }
3396
+
3179
3397
  /**
3180
3398
  * Listen for a single global key change inside an onUpdate pipeline.
3181
3399
  * Usage: onUpdate(globalChange('p1Score', (value) => { ... }))
@@ -3297,8 +3515,14 @@ export declare interface InputGamepad {
3297
3515
  };
3298
3516
  /** Screen-space cursor position (normalized 0..1). Only populated by MouseProvider. */
3299
3517
  pointer?: {
3518
+ /** Window-normalized X (0 = left, 1 = right). */
3300
3519
  x: number;
3520
+ /** Window-normalized Y (0 = top, 1 = bottom). */
3301
3521
  y: number;
3522
+ /** Canvas-center-relative X (−1 = left edge, +1 = right edge). */
3523
+ centerX: number;
3524
+ /** Canvas-center-relative Y (−1 = top edge, +1 = bottom edge). */
3525
+ centerY: number;
3302
3526
  };
3303
3527
  }
3304
3528
 
@@ -3590,6 +3814,9 @@ export declare type LoadingEvent = {
3590
3814
  current?: number;
3591
3815
  };
3592
3816
 
3817
+ /** Per-axis lock input: `true` locks all axes, or pass an [x, y, z] tuple. */
3818
+ declare type LockAxesInput = boolean | readonly [boolean, boolean, boolean];
3819
+
3593
3820
  declare class MaterialBuilder {
3594
3821
  static sharedMaterialMap: Map<MaterialCacheKey, SharedMaterialMapEntry>;
3595
3822
  /**
@@ -3688,6 +3915,12 @@ export declare interface MouseConfig {
3688
3915
  pointerLock?: boolean;
3689
3916
  /** Sensitivity multiplier for mouse movement (default 0.002). */
3690
3917
  sensitivity?: number;
3918
+ /** How mouse position drives look: delta (movement) or screenCenter (cursor offset from viewport center). */
3919
+ lookMode?: 'delta' | 'screenCenter';
3920
+ /** Max yaw/pitch in degrees when `lookMode` is `screenCenter` (default 45). */
3921
+ maxLookDegrees?: number;
3922
+ /** Screen-center follow rate toward cursor-mapped target (lower = snappier; default 2). */
3923
+ lookSensitivity?: number;
3691
3924
  }
3692
3925
 
3693
3926
  /** Mouse input configuration. */
@@ -3695,6 +3928,9 @@ export declare const MouseConfigSchema: _sinclair_typebox.TObject<{
3695
3928
  mapping: _sinclair_typebox.TOptional<_sinclair_typebox.TRecord<_sinclair_typebox.TString, _sinclair_typebox.TArray<_sinclair_typebox.TString>>>;
3696
3929
  pointerLock: _sinclair_typebox.TOptional<_sinclair_typebox.TBoolean>;
3697
3930
  sensitivity: _sinclair_typebox.TOptional<_sinclair_typebox.TNumber>;
3931
+ lookMode: _sinclair_typebox.TOptional<_sinclair_typebox.TUnion<[_sinclair_typebox.TLiteral<"delta">, _sinclair_typebox.TLiteral<"screenCenter">]>>;
3932
+ maxLookDegrees: _sinclair_typebox.TOptional<_sinclair_typebox.TNumber>;
3933
+ lookSensitivity: _sinclair_typebox.TOptional<_sinclair_typebox.TNumber>;
3698
3934
  }>;
3699
3935
 
3700
3936
  export declare type MouseMapping = Record<string, string[]>;
@@ -4230,6 +4466,15 @@ declare type RectOptions = BaseNode | Partial<ZylemRectOptions>;
4230
4466
 
4231
4467
  export { registerCooldown }
4232
4468
 
4469
+ /**
4470
+ * Register the active stage's entity resolver so `getSelectedEntity` /
4471
+ * `getHoveredEntity` can hand back live entities without the reactive store
4472
+ * ever holding one.
4473
+ *
4474
+ * @returns An unregister function.
4475
+ */
4476
+ export declare function registerDebugEntityResolver(resolver: DebugEntityResolver): () => void;
4477
+
4233
4478
  /**
4234
4479
  * Register the active stage's focus context. Returns an unregister function.
4235
4480
  */
@@ -4508,6 +4753,9 @@ export declare function resolveButtonAccent(theme: TouchTheme, slot: VirtualTouc
4508
4753
 
4509
4754
  export { ResolvedAim2D }
4510
4755
 
4756
+ /** Resolve a UUID through the active stage, or null when unavailable. */
4757
+ export declare function resolveDebugEntity(uuid: string | null): GameEntity<any> | null;
4758
+
4511
4759
  /** `StageTransitionConfig` with all defaults applied. */
4512
4760
  declare interface ResolvedStageTransition {
4513
4761
  duration: number;
@@ -4832,6 +5080,41 @@ declare interface SceneState {
4832
5080
  */
4833
5081
  export declare type ScreenAnchor = 'top-left' | 'top-center' | 'top-right' | 'center-left' | 'center' | 'center-right' | 'bottom-left' | 'bottom-center' | 'bottom-right';
4834
5082
 
5083
+ /**
5084
+ * Computes WASM look deltas that drive current yaw/pitch toward screen-center targets.
5085
+ *
5086
+ * `lookSensitivity` controls follow rate toward the cursor-mapped target (lower = snappier).
5087
+ * WASM applies its own `look_sensitivity` multiplier to these deltas.
5088
+ */
5089
+ export declare function screenCenterLookDeltas(nx: number, ny: number, currentYaw: number, currentPitch: number, options?: ScreenCenterLookOptions): {
5090
+ lookX: number;
5091
+ lookY: number;
5092
+ };
5093
+
5094
+ export declare interface ScreenCenterLookOptions {
5095
+ maxLookDegrees?: number;
5096
+ /** How fast cursor position pulls yaw/pitch toward target (lower = snappier). @default 2 */
5097
+ lookSensitivity?: number;
5098
+ }
5099
+
5100
+ /**
5101
+ * Maps canvas-center-relative pointer offset to absolute yaw/pitch targets.
5102
+ * Used with screen-center mouse look (free cursor, no pointer lock).
5103
+ */
5104
+ export declare interface ScreenCenterLookTargets {
5105
+ targetYaw: number;
5106
+ targetPitch: number;
5107
+ }
5108
+
5109
+ /**
5110
+ * Converts normalized pointer offset from viewport center to look angles.
5111
+ *
5112
+ * @param nx Horizontal offset from center (−1 = left edge, +1 = right edge).
5113
+ * @param ny Vertical offset from center (−1 = top edge, +1 = bottom edge).
5114
+ * @param maxDegrees Maximum yaw/pitch at viewport edges (default 45).
5115
+ */
5116
+ export declare function screenCenterLookTargets(nx: number, ny: number, maxDegrees?: number): ScreenCenterLookTargets;
5117
+
4835
5118
  export { ScreenVisibilityBehavior }
4836
5119
 
4837
5120
  export { ScreenVisibilityChangeContext }
@@ -4911,6 +5194,8 @@ export declare function setDebugTool(tool: DebugTools): void;
4911
5194
  */
4912
5195
  export declare function setGlobal(path: string, value: unknown): void;
4913
5196
 
5197
+ export declare function setHoveredEntityId(uuid: string | null): void;
5198
+
4914
5199
  export declare function setPaused(paused: boolean): void;
4915
5200
 
4916
5201
  export { setPosition }
@@ -4961,7 +5246,13 @@ export declare function setRotationY(entity: RotatableEntity, y: number): void;
4961
5246
  */
4962
5247
  export declare function setRotationZ(entity: RotatableEntity, z: number): void;
4963
5248
 
4964
- export declare function setSelectedEntity(entity: GameEntity<any> | null): void;
5249
+ /**
5250
+ * @deprecated Pass a UUID to {@link setSelectedEntityId} instead. Accepts a
5251
+ * live entity only to keep the previous public API working.
5252
+ */
5253
+ export declare function setSelectedEntity(entity: GameEntity<any> | string | null): void;
5254
+
5255
+ export declare function setSelectedEntityId(uuid: string | null): void;
4965
5256
 
4966
5257
  export declare function setStageFrameProfilerEnabled(value: boolean): void;
4967
5258
 
@@ -5023,6 +5314,9 @@ export { Shooter2DStateComponent }
5023
5314
 
5024
5315
  export { Shooter2DTarget }
5025
5316
 
5317
+ /** Shortest signed angle from `from` to `to` in radians. */
5318
+ export declare function shortestAngleDelta(from: number, to: number): number;
5319
+
5026
5320
  declare function shortHash(objString: string): string;
5027
5321
 
5028
5322
  export { Simulation }
@@ -5034,7 +5328,19 @@ export declare class SimulationBody implements PhysicsRenderPoseProvider {
5034
5328
  private readonly poseScratch;
5035
5329
  private readonly renderScratch;
5036
5330
  private readonly previousScratch;
5331
+ /**
5332
+ * First step at which the shared render buffers are coherent for this
5333
+ * body. After a spawn or an explicit pose write (teleport), the current
5334
+ * slot isn't written until the next fixed step and the previous-step
5335
+ * copy lags one more; interpolating across that gap would sweep the
5336
+ * mesh from a stale pose (often the origin). Until this step is
5337
+ * reached, render reads collapse to the live body pose.
5338
+ */
5339
+ private interpolationValidAfterStep;
5037
5340
  constructor(simulation: Simulation, handle: EntityHandle, clock: SimulationStepClock);
5341
+ /** Suppress interpolation until the render buffers catch up. */
5342
+ private markPoseDiscontinuity;
5343
+ private isInterpolationStale;
5038
5344
  translation(): PhysicsVector3;
5039
5345
  rotation(): PhysicsQuaternion;
5040
5346
  linvel(): PhysicsVector3;
@@ -5044,6 +5350,22 @@ export declare class SimulationBody implements PhysicsRenderPoseProvider {
5044
5350
  setLinvel(velocity: PhysicsVector3, _wakeUp?: boolean): void;
5045
5351
  setAngvel(velocity: PhysicsVector3, _wakeUp?: boolean): void;
5046
5352
  applyImpulse(impulse: PhysicsVector3, _wakeUp?: boolean): void;
5353
+ /**
5354
+ * Rotation locks are fixed at body-creation time in the wasm runtime
5355
+ * (`SimulationBodyDefinition.lockRotation`); there is no live setter FFI
5356
+ * yet. Accepted for Rapier compat — when locking, the angular velocity is
5357
+ * zeroed to approximate the intent. Prefer lock flags on the body
5358
+ * definition at spawn time.
5359
+ */
5360
+ lockRotations(locked: boolean, _wakeUp?: boolean): void;
5361
+ /**
5362
+ * Translation locks are fixed at body-creation time in the wasm runtime
5363
+ * (`SimulationBodyDefinition.lockTranslation`); there is no live setter
5364
+ * FFI yet. Accepted for Rapier compat — when locking, the linear velocity
5365
+ * is zeroed to approximate the intent. Prefer lock flags on the body
5366
+ * definition at spawn time.
5367
+ */
5368
+ lockTranslations(locked: boolean, _wakeUp?: boolean): void;
5047
5369
  /**
5048
5370
  * Damping is fixed at body-creation time in the wasm runtime; there is no
5049
5371
  * live setter FFI yet, so this is accepted (for `MoveableBodyLike`
@@ -5342,19 +5664,7 @@ export declare type StageConfigLike = Partial<{
5342
5664
  postProcessingEffects: ZylemPostEffect[];
5343
5665
  }>;
5344
5666
 
5345
- /** Payload for stage configuration sent to editor. */
5346
- export declare interface StageConfigPayload {
5347
- id: string;
5348
- backgroundColor: string;
5349
- backgroundImage: string | null;
5350
- gravity: {
5351
- x: number;
5352
- y: number;
5353
- z: number;
5354
- };
5355
- inputs: Record<string, string[]>;
5356
- variables: Record<string, unknown>;
5357
- }
5667
+ export { StageConfigPayload }
5358
5668
 
5359
5669
  /**
5360
5670
  * Self-managing debug delegate that subscribes to `debugState` internally.
@@ -5376,16 +5686,33 @@ declare class StageDebugDelegate {
5376
5686
  private cameraDebugDelegate;
5377
5687
  private debugStateUnsubscribe;
5378
5688
  private focusContextUnregister;
5689
+ private entityResolverUnregister;
5690
+ private lastDebugEnabled;
5691
+ private warnedMissingAddFactory;
5379
5692
  constructor(stage: ZylemStage, options?: StageDebugDelegateOptions);
5693
+ /**
5694
+ * Supply the factory the `add` tool uses to spawn entities at the clicked
5695
+ * point. Without one the tool warns instead of silently doing nothing.
5696
+ */
5697
+ setAddEntityFactory(factory: AddEntityFactory | null): void;
5698
+ /** Camera used for debug orbit / raycasts (active primary, usually `__debug__`). */
5699
+ private getDebugViewCamera;
5380
5700
  private resolveEntity;
5381
5701
  /** Copy all stage children into debugMap so raycast/select tools work after enabling debug mid-session. */
5382
5702
  private populateDebugMap;
5383
5703
  private ensureCameraDebugDelegate;
5704
+ /**
5705
+ * Activate/deactivate only when `debugState.enabled` actually flips.
5706
+ * Hover and selection write to `debugState` every frame, and `activate()`
5707
+ * rebuilds post-processing, so reacting to every notification would
5708
+ * rebuild the render pipeline once per frame.
5709
+ */
5384
5710
  private syncWithDebugState;
5385
- /** Initialize DOM listeners and camera debug delegate when debug is turned on. */
5711
+ /** Initialize DOM listeners and switch rendering to the debug orbit camera. */
5386
5712
  private activate;
5387
- /** Tear down visuals and camera debug delegate when debug is turned off. */
5713
+ /** Tear down visuals and restore the gameplay primary camera. */
5388
5714
  private deactivate;
5715
+ private rebindPostProcessing;
5389
5716
  private initDebugVisuals;
5390
5717
  private disposeDebugVisuals;
5391
5718
  update(): void;
@@ -5410,6 +5737,12 @@ export declare interface StageEntity extends Entity {
5410
5737
  instanceId: number;
5411
5738
  isInstanced?: boolean;
5412
5739
  isBundled?: boolean;
5740
+ /**
5741
+ * When true, `syncRenderPoses` leaves the entity's Three.js transform alone.
5742
+ * Used for camera-parented viewmodels so physics pose sync cannot overwrite
5743
+ * the local offset.
5744
+ */
5745
+ skipRenderPoseSync?: boolean;
5413
5746
  controlledRotation: boolean;
5414
5747
  name: string;
5415
5748
  markedForRemoval: boolean;
@@ -5772,6 +6105,8 @@ export declare const StageSchema: _sinclair_typebox.TObject<{
5772
6105
  assets: _sinclair_typebox.TOptional<_sinclair_typebox.TArray<_sinclair_typebox.TString>>;
5773
6106
  }>;
5774
6107
 
6108
+ export { StageSnapshotPayload }
6109
+
5775
6110
  declare type StageState = ZylemStageConfig & {
5776
6111
  entities: GameEntityInterface[];
5777
6112
  };
@@ -5839,27 +6174,6 @@ export declare const standardShader: {
5839
6174
  fragment: string;
5840
6175
  };
5841
6176
 
5842
- /** Payload for state dispatch events from game to editor. */
5843
- export declare interface StateDispatchPayload {
5844
- scope: 'game' | 'stage' | 'entity';
5845
- path: string;
5846
- value: unknown;
5847
- previousValue?: unknown;
5848
- config?: {
5849
- id: string;
5850
- aspectRatio: number;
5851
- fullscreen: boolean;
5852
- bodyBackground: string | undefined;
5853
- internalResolution: {
5854
- width: number;
5855
- height: number;
5856
- } | undefined;
5857
- debug: boolean;
5858
- } | null;
5859
- stageConfig?: StageConfigPayload | null;
5860
- entities?: EntityConfigPayload[] | null;
5861
- }
5862
-
5863
6177
  export { step }
5864
6178
 
5865
6179
  /**
@@ -6355,6 +6669,16 @@ export declare function useMouseLook(player: InputPlayer, options?: {
6355
6669
  sensitivity?: number;
6356
6670
  }): GameInputConfig;
6357
6671
 
6672
+ /**
6673
+ * Enables screen-center mouse look (free cursor, no pointer lock).
6674
+ * Cursor offset from the game canvas center maps to yaw/pitch up to `maxLookDegrees`.
6675
+ * @example stage.setInputConfiguration(useScreenCenterLook('p1'));
6676
+ */
6677
+ export declare function useScreenCenterLook(player: InputPlayer, options?: {
6678
+ maxLookDegrees?: number;
6679
+ lookSensitivity?: number;
6680
+ }): GameInputConfig;
6681
+
6358
6682
  /** Whether the entity opts into a managed render path (bundle or instanced). */
6359
6683
  export declare function usesManagedRenderPath(options?: RenderCategoryOptions | null): boolean;
6360
6684
 
@@ -6908,6 +7232,8 @@ export declare class ZylemBox extends GameEntity<ZylemBoxOptions> {
6908
7232
 
6909
7233
  declare type ZylemBoxOptions = GameEntityOptions;
6910
7234
 
7235
+ export { ZylemBridge }
7236
+
6911
7237
  /**
6912
7238
  * ZylemCamera is a lightweight camera wrapper that manages the Three.js camera,
6913
7239
  * the camera pose pipeline, and viewport configuration. Rendering is handled
@@ -6961,6 +7287,8 @@ declare class ZylemCamera {
6961
7287
  private _useOrbitalControls;
6962
7288
  /** When true, debug-mode orbital controls are not attached to this camera. */
6963
7289
  _skipDebugOrbit: boolean;
7290
+ /** Dedicated debug-view camera (e.g. `__debug__`); uses simplified orbit path. */
7291
+ isDebugViewCamera: boolean;
6964
7292
  /** Reference to the shared renderer manager (set during setup). */
6965
7293
  private _rendererManager;
6966
7294
  /** Elapsed time tracker for CameraContext. */
@@ -7047,6 +7375,16 @@ declare class ZylemCamera {
7047
7375
  * Dispose camera resources.
7048
7376
  */
7049
7377
  destroy(): void;
7378
+ /**
7379
+ * Set the default orbit target for debug mode (e.g. origin anchor).
7380
+ */
7381
+ setDefaultOrbitTarget(target: Object3D): void;
7382
+ /**
7383
+ * Seed the debug orbit camera to an initial pose.
7384
+ */
7385
+ seedDebugOrbitPose(position: Vector3, lookAt: Vector3): void;
7386
+ /** Whether the debug orbit camera has received its initial pose. */
7387
+ isDebugOrbitPoseSeeded(): boolean;
7050
7388
  /**
7051
7389
  * Attach a delegate to react to debug state changes.
7052
7390
  * Skipped when _skipDebugOrbit is true so the pipeline always runs.
@@ -7386,16 +7724,10 @@ declare class ZylemGame<TGlobals extends BaseGlobals> {
7386
7724
  private rendererObserver;
7387
7725
  private eventBusUnsubscribes;
7388
7726
  private thumbnailUnsubscribes;
7389
- /**
7390
- * Trailing-debounce timer for entity-driven state dispatches. Building a
7391
- * `state:dispatch` payload is O(entities) (and downstream editor stores
7392
- * re-proxy/reconcile every entity), so bursts of thumbnail/destroy events
7393
- * must coalesce into a single dispatch instead of one per entity.
7394
- */
7395
- private entityDispatchTimer;
7396
- private pendingEntityDispatchPath;
7397
- /** Debounce window (ms) for coalescing entity-driven state dispatches. */
7398
- private static readonly ENTITY_DISPATCH_DEBOUNCE_MS;
7727
+ /** Serialized last-published game config, for dirty-checking resize churn. */
7728
+ private lastPublishedGameConfig;
7729
+ /** Editor ↔ game bridge adapter (RAF-coalesced publishes, command intake). */
7730
+ private gameBridge;
7399
7731
  private readonly gameUpdateParams;
7400
7732
  private readonly stageSetupParams;
7401
7733
  private readonly stageUpdateParams;
@@ -7449,27 +7781,47 @@ declare class ZylemGame<TGlobals extends BaseGlobals> {
7449
7781
  */
7450
7782
  private buildStageConfigPayload;
7451
7783
  /**
7452
- * Build the entities payload for the current stage.
7784
+ * Build a bridge entity summary for one entity, or null for entities on
7785
+ * the managed render path (instanced packs/bundles), which can number in
7786
+ * the thousands and are not useful in the editor panel.
7787
+ */
7788
+ private buildEntitySummary;
7789
+ /**
7790
+ * Build the full entities payload for the current stage.
7453
7791
  */
7454
7792
  private buildEntitiesPayload;
7455
7793
  /**
7456
- * Generate entity thumbnails as entities become available, then refresh editor state.
7794
+ * Generate entity thumbnails as entities become available, streaming
7795
+ * upserts/thumbnails/removals to the editor through the bridge
7796
+ * (RAF-coalesced, merged by uuid).
7797
+ *
7798
+ * All of this work exists only to feed the editor, and thumbnails in
7799
+ * particular cost an offscreen render plus a pixel readback on the shared
7800
+ * renderer. Each publisher therefore checks the bridge for a live listener
7801
+ * first, and a late-connecting editor is backfilled from
7802
+ * {@link wireEditorBackfill}.
7457
7803
  */
7458
7804
  private wireEntityThumbnails;
7459
- private clearEntityThumbnailWiring;
7460
7805
  /**
7461
- * Coalesce entity-driven state dispatches (thumbnail resolutions, entity
7462
- * destruction) into one trailing `emitStateDispatch` per burst.
7806
+ * Republish the current stage when an editor subscribes after the game
7807
+ * started. Without this, entities added while nothing was listening would
7808
+ * never appear, since the per-entity publishers were skipped.
7463
7809
  */
7464
- private queueEntityStateDispatch;
7810
+ private wireEditorBackfill;
7811
+ private clearEntityThumbnailWiring;
7812
+ private buildGameConfigPayload;
7813
+ /** Publish the resolved display config over the bridge. */
7465
7814
  /**
7466
- * Emit a state:dispatch event to the zylemEventBus.
7467
- * Called after stage load and on global state changes.
7815
+ * Publish the display config, skipping unchanged payloads. The
7816
+ * `ResizeObserver` on `<zylem-game>` funnels into `setDisplayRuntime`, which
7817
+ * fires on every layout tick even when nothing about the config moved.
7468
7818
  */
7469
- private emitStateDispatch;
7819
+ private publishGameConfig;
7820
+ /** Publish a full stage snapshot (config + entity list) over the bridge. */
7821
+ private publishStageSnapshot;
7470
7822
  /**
7471
- * Subscribe to the game event bus for stage loading and state events.
7472
- * Emits events to zylemEventBus for cross-package communication.
7823
+ * Subscribe to the game event bus for stage loading and state events,
7824
+ * mirroring global-variable updates to the editor over the bridge.
7473
7825
  */
7474
7826
  private subscribeToEventBus;
7475
7827
  }