@waica/engine 0.25.0 → 0.26.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.
Files changed (104) hide show
  1. package/README.md +55 -1
  2. package/dist/anchored-pieces.d.ts +3 -2
  3. package/dist/anchored-pieces.js +12 -8
  4. package/dist/archetype.d.ts +2 -2
  5. package/dist/assets/asset-loader.d.ts +28 -5
  6. package/dist/assets/asset-loader.js +63 -7
  7. package/dist/assets/model-backend.d.ts +21 -0
  8. package/dist/assets/model-backend.js +21 -0
  9. package/dist/assets/model-cache.d.ts +47 -0
  10. package/dist/assets/model-cache.js +117 -0
  11. package/dist/audio/audio-subsystem.d.ts +6 -1
  12. package/dist/audio/audio-subsystem.js +8 -15
  13. package/dist/audio/placement.d.ts +41 -0
  14. package/dist/audio/placement.js +21 -0
  15. package/dist/audio/spatial.d.ts +20 -0
  16. package/dist/audio/spatial.js +26 -0
  17. package/dist/audio/types.d.ts +7 -1
  18. package/dist/camera-projection.d.ts +38 -0
  19. package/dist/camera-projection.js +44 -0
  20. package/dist/camera-rig.d.ts +48 -0
  21. package/dist/camera-rig.js +98 -0
  22. package/dist/camera.d.ts +20 -3
  23. package/dist/camera.js +3 -1
  24. package/dist/component.d.ts +38 -2
  25. package/dist/component.js +3 -0
  26. package/dist/components/animated-sprite.d.ts +2 -1
  27. package/dist/components/animated-sprite.js +1 -0
  28. package/dist/components/collider.d.ts +89 -0
  29. package/dist/components/collider.js +57 -0
  30. package/dist/components/dynamic-body.d.ts +2 -1
  31. package/dist/components/dynamic-body.js +1 -0
  32. package/dist/components/hitbox.d.ts +2 -1
  33. package/dist/components/hitbox.js +1 -0
  34. package/dist/components/light.d.ts +2 -1
  35. package/dist/components/light.js +1 -0
  36. package/dist/components/model.d.ts +63 -0
  37. package/dist/components/model.js +117 -0
  38. package/dist/components/particle-emitter.d.ts +2 -1
  39. package/dist/components/particle-emitter.js +1 -0
  40. package/dist/components/point-light.d.ts +75 -0
  41. package/dist/components/point-light.js +102 -0
  42. package/dist/components/rigid-body.d.ts +114 -0
  43. package/dist/components/rigid-body.js +83 -0
  44. package/dist/components/solid.d.ts +2 -1
  45. package/dist/components/solid.js +1 -0
  46. package/dist/components/sprite.d.ts +2 -1
  47. package/dist/components/sprite.js +1 -0
  48. package/dist/components/sun.d.ts +46 -0
  49. package/dist/components/sun.js +74 -0
  50. package/dist/components/tilemap.d.ts +2 -1
  51. package/dist/components/tilemap.js +1 -0
  52. package/dist/entity-transform.d.ts +18 -0
  53. package/dist/entity-transform.js +17 -0
  54. package/dist/game.d.ts +51 -15
  55. package/dist/game.js +83 -70
  56. package/dist/index.d.ts +29 -4
  57. package/dist/index.js +12 -1
  58. package/dist/lit-frame.js +10 -3
  59. package/dist/physics-3d/body-contacts.d.ts +10 -0
  60. package/dist/physics-3d/body-contacts.js +59 -0
  61. package/dist/physics-3d/body-sync.d.ts +52 -0
  62. package/dist/physics-3d/body-sync.js +153 -0
  63. package/dist/physics-3d/character-controller.d.ts +44 -0
  64. package/dist/physics-3d/character-controller.js +126 -0
  65. package/dist/physics-3d/physics-host.d.ts +86 -0
  66. package/dist/physics-3d/physics-host.js +174 -0
  67. package/dist/physics-3d/physics-world.d.ts +65 -0
  68. package/dist/physics-3d/physics-world.js +158 -0
  69. package/dist/physics-3d/rapier-module.d.ts +17 -0
  70. package/dist/physics-3d/rapier-module.js +19 -0
  71. package/dist/physics-3d/sensor-dispatch.d.ts +10 -0
  72. package/dist/physics-3d/sensor-dispatch.js +77 -0
  73. package/dist/physics-3d/world-queries.d.ts +39 -0
  74. package/dist/physics-3d/world-queries.js +63 -0
  75. package/dist/pointer-raycast.d.ts +17 -0
  76. package/dist/pointer-raycast.js +48 -0
  77. package/dist/pointer.d.ts +27 -6
  78. package/dist/pointer.js +43 -17
  79. package/dist/runtime-bridge.d.ts +1 -1
  80. package/dist/runtime-inspection.d.ts +9 -0
  81. package/dist/runtime-inspection.js +8 -2
  82. package/dist/runtime-lighting-snapshot.d.ts +27 -1
  83. package/dist/runtime-lighting-snapshot.js +31 -0
  84. package/dist/runtime-physics-snapshot.d.ts +41 -0
  85. package/dist/runtime-physics-snapshot.js +45 -0
  86. package/dist/runtime-view-snapshot.d.ts +25 -0
  87. package/dist/runtime-view-snapshot.js +30 -0
  88. package/dist/scene-camera-3d.d.ts +66 -0
  89. package/dist/scene-camera-3d.js +117 -0
  90. package/dist/scene-lights-3d.d.ts +17 -0
  91. package/dist/scene-lights-3d.js +27 -0
  92. package/dist/scene-simulation.d.ts +22 -0
  93. package/dist/scene-simulation.js +29 -0
  94. package/dist/scene-space.d.ts +50 -0
  95. package/dist/scene-space.js +103 -0
  96. package/dist/scene.d.ts +12 -2
  97. package/dist/scene.js +11 -4
  98. package/dist/simulation-phases.d.ts +9 -0
  99. package/dist/simulation-phases.js +19 -0
  100. package/dist/spatial-query-3d.d.ts +48 -0
  101. package/dist/spatial-query-3d.js +96 -0
  102. package/dist/spatial-query.d.ts +18 -4
  103. package/dist/spatial-query.js +57 -1
  104. package/package.json +2 -1
@@ -0,0 +1,114 @@
1
+ import { Component, type ComponentSpace } from '../component.js';
2
+ import type { Vec3Json } from '../scene-camera-3d.js';
3
+ export type RigidBodyType = 'dynamic' | 'kinematic';
4
+ export declare const RIGID_BODY_TYPES: readonly RigidBodyType[];
5
+ /** A 3D vector as the body's public API reads and writes it. */
6
+ export interface BodyVector {
7
+ x: number;
8
+ y: number;
9
+ z: number;
10
+ }
11
+ /**
12
+ * Makes the entity's `Collider` a body that moves (ADR 0028). A `dynamic` body
13
+ * is Rapier's: it falls under the scene's gravity, bounces, stacks and rests,
14
+ * and after every Simulation Step the engine writes the entity's position and
15
+ * rotation from it. A `kinematic` body is moved by code. It needs a `Collider`
16
+ * on the same entity (`validate_project` reports a body without one, and at
17
+ * runtime none is created). `mass` is the collider's mass; `velocity` is the
18
+ * initial linear velocity. A `kinematic` body is a character: every step the
19
+ * engine moves it through Rapier's character controller by its
20
+ * `desiredVelocity` on x and z and its own vertical velocity under gravity,
21
+ * stopping at walls, climbing steps up to `stepHeight` and slopes up to
22
+ * `maxSlope`, sticking to the ground within `snapDistance`, and pushing
23
+ * dynamic bodies it walks into. A param outside its range (`type` unknown,
24
+ * `mass` under the minimum, a `velocity` that is not three numbers) creates
25
+ * no body and warns once; `validate_project` reports it as
26
+ * `invalid-rigid-body-param`. A RigidBody in a 2D scene creates nothing.
27
+ */
28
+ export declare class RigidBody extends Component {
29
+ static componentName: string;
30
+ static space: ComponentSpace;
31
+ static params: {
32
+ type: {
33
+ label: string;
34
+ options: RigidBodyType[];
35
+ };
36
+ mass: {
37
+ label: string;
38
+ min: number;
39
+ step: number;
40
+ };
41
+ gravityScale: {
42
+ label: string;
43
+ step: number;
44
+ };
45
+ linearDamping: {
46
+ label: string;
47
+ min: number;
48
+ step: number;
49
+ };
50
+ angularDamping: {
51
+ label: string;
52
+ min: number;
53
+ step: number;
54
+ };
55
+ lockRotations: {
56
+ label: string;
57
+ };
58
+ velocity: {
59
+ label: string;
60
+ kind: "vector3";
61
+ };
62
+ stepHeight: {
63
+ label: string;
64
+ min: number;
65
+ step: number;
66
+ };
67
+ maxSlope: {
68
+ label: string;
69
+ min: number;
70
+ max: number;
71
+ step: number;
72
+ };
73
+ snapDistance: {
74
+ label: string;
75
+ min: number;
76
+ step: number;
77
+ };
78
+ };
79
+ static transient: string[];
80
+ type: RigidBodyType;
81
+ mass: number;
82
+ gravityScale: number;
83
+ linearDamping: number;
84
+ angularDamping: number;
85
+ lockRotations: boolean;
86
+ /** The initial linear velocity `[x, y, z]`, applied when the body is created. */
87
+ velocity: Vec3Json;
88
+ /** Kinematic only: the tallest step it climbs without jumping. */
89
+ stepHeight: number;
90
+ /** Kinematic only: the steepest slope it climbs, in degrees. */
91
+ maxSlope: number;
92
+ /** Kinematic only: how far below its feet it still sticks to the ground (walking down steps and slopes). */
93
+ snapDistance: number;
94
+ /**
95
+ * Kinematic only: where it walks, in units per second on the world x and z
96
+ * axes. A motor sets it; the physics step reads it every Simulation Step and
97
+ * moves the body through the character controller. It stays until set again.
98
+ */
99
+ desiredVelocity: {
100
+ x: number;
101
+ z: number;
102
+ };
103
+ /** The body's linear velocity now, in units per second; zero before it exists. Assigning sets it. */
104
+ get linearVelocity(): BodyVector;
105
+ set linearVelocity(value: BodyVector);
106
+ /** Launches a kinematic body upward at `speed` (units per second), only while it stands on something; whether it did. */
107
+ jump(speed: number): boolean;
108
+ /** Whether the body stands on something; only a kinematic body moved by the character controller can. */
109
+ get grounded(): boolean;
110
+ /** Pushes a dynamic body with an instantaneous impulse (mass · velocity change); a no-op on any other body. */
111
+ applyImpulse(impulse: BodyVector): void;
112
+ onReady(): void;
113
+ onDestroy(): void;
114
+ }
@@ -0,0 +1,83 @@
1
+ import { Component } from '../component.js';
2
+ export const RIGID_BODY_TYPES = ['dynamic', 'kinematic'];
3
+ const ZERO = { x: 0, y: 0, z: 0 };
4
+ /**
5
+ * Makes the entity's `Collider` a body that moves (ADR 0028). A `dynamic` body
6
+ * is Rapier's: it falls under the scene's gravity, bounces, stacks and rests,
7
+ * and after every Simulation Step the engine writes the entity's position and
8
+ * rotation from it. A `kinematic` body is moved by code. It needs a `Collider`
9
+ * on the same entity (`validate_project` reports a body without one, and at
10
+ * runtime none is created). `mass` is the collider's mass; `velocity` is the
11
+ * initial linear velocity. A `kinematic` body is a character: every step the
12
+ * engine moves it through Rapier's character controller by its
13
+ * `desiredVelocity` on x and z and its own vertical velocity under gravity,
14
+ * stopping at walls, climbing steps up to `stepHeight` and slopes up to
15
+ * `maxSlope`, sticking to the ground within `snapDistance`, and pushing
16
+ * dynamic bodies it walks into. A param outside its range (`type` unknown,
17
+ * `mass` under the minimum, a `velocity` that is not three numbers) creates
18
+ * no body and warns once; `validate_project` reports it as
19
+ * `invalid-rigid-body-param`. A RigidBody in a 2D scene creates nothing.
20
+ */
21
+ export class RigidBody extends Component {
22
+ static componentName = 'RigidBody';
23
+ static space = '3d';
24
+ static params = {
25
+ type: { label: 'Type', options: [...RIGID_BODY_TYPES] },
26
+ mass: { label: 'Mass', min: 0.001, step: 0.1 },
27
+ gravityScale: { label: 'Gravity scale', step: 0.1 },
28
+ linearDamping: { label: 'Linear damping', min: 0, step: 0.1 },
29
+ angularDamping: { label: 'Angular damping', min: 0, step: 0.1 },
30
+ lockRotations: { label: 'Lock rotations' },
31
+ velocity: { label: 'Initial velocity', kind: 'vector3' },
32
+ stepHeight: { label: 'Step height', min: 0, step: 0.05 },
33
+ maxSlope: { label: 'Max slope (degrees)', min: 0, max: 89, step: 1 },
34
+ snapDistance: { label: 'Snap to ground', min: 0, step: 0.05 },
35
+ };
36
+ // Live reads and writes of the body, not authorable defaults (they need a Game to answer).
37
+ static transient = ['linearVelocity', 'desiredVelocity'];
38
+ type = 'dynamic';
39
+ mass = 1;
40
+ gravityScale = 1;
41
+ linearDamping = 0;
42
+ angularDamping = 0;
43
+ lockRotations = false;
44
+ /** The initial linear velocity `[x, y, z]`, applied when the body is created. */
45
+ velocity = [0, 0, 0];
46
+ /** Kinematic only: the tallest step it climbs without jumping. */
47
+ stepHeight = 0.3;
48
+ /** Kinematic only: the steepest slope it climbs, in degrees. */
49
+ maxSlope = 45;
50
+ /** Kinematic only: how far below its feet it still sticks to the ground (walking down steps and slopes). */
51
+ snapDistance = 0.2;
52
+ /**
53
+ * Kinematic only: where it walks, in units per second on the world x and z
54
+ * axes. A motor sets it; the physics step reads it every Simulation Step and
55
+ * moves the body through the character controller. It stays until set again.
56
+ */
57
+ desiredVelocity = { x: 0, z: 0 };
58
+ /** The body's linear velocity now, in units per second; zero before it exists. Assigning sets it. */
59
+ get linearVelocity() {
60
+ return this.game.physics.velocityOf(this.entity) ?? { ...ZERO };
61
+ }
62
+ set linearVelocity(value) {
63
+ this.game.physics.setVelocityOf(this.entity, value);
64
+ }
65
+ /** Launches a kinematic body upward at `speed` (units per second), only while it stands on something; whether it did. */
66
+ jump(speed) {
67
+ return this.game.physics.jumpOf(this.entity, speed);
68
+ }
69
+ /** Whether the body stands on something; only a kinematic body moved by the character controller can. */
70
+ get grounded() {
71
+ return this.game.physics.groundedOf(this.entity);
72
+ }
73
+ /** Pushes a dynamic body with an instantaneous impulse (mass · velocity change); a no-op on any other body. */
74
+ applyImpulse(impulse) {
75
+ this.game.physics.applyImpulseTo(this.entity, impulse);
76
+ }
77
+ onReady() {
78
+ this.game.physics.attach(this.entity);
79
+ }
80
+ onDestroy() {
81
+ this.game.physics.detach(this.entity);
82
+ }
83
+ }
@@ -1,4 +1,4 @@
1
- import { Component } from '../component.js';
1
+ import { Component, type ComponentSpace } from '../component.js';
2
2
  import { type CollisionPoint, type CollisionShape } from '../collision-shape.js';
3
3
  /**
4
4
  * Static collider. Character motors (e.g. PlatformerMotor) collide against
@@ -9,6 +9,7 @@ import { type CollisionPoint, type CollisionShape } from '../collision-shape.js'
9
9
  */
10
10
  export declare class Solid extends Component {
11
11
  static componentName: string;
12
+ static space: ComponentSpace;
12
13
  static params: {
13
14
  offsetX: {
14
15
  label: string;
@@ -9,6 +9,7 @@ import { collisionBounds, resolveCollisionPoints, } from '../collision-shape.js'
9
9
  */
10
10
  export class Solid extends Component {
11
11
  static componentName = 'Solid';
12
+ static space = '2d';
12
13
  static params = {
13
14
  offsetX: { label: 'x offset' },
14
15
  offsetY: { label: 'y offset' },
@@ -1,4 +1,4 @@
1
- import { Component } from '../component.js';
1
+ import { Component, type ComponentSpace } from '../component.js';
2
2
  import type { YSortParticipant } from '../render-sort.js';
3
3
  export type SpriteShape = 'rectangle' | 'circle';
4
4
  /**
@@ -7,6 +7,7 @@ export type SpriteShape = 'rectangle' | 'circle';
7
7
  */
8
8
  export declare class Sprite extends Component implements YSortParticipant {
9
9
  static componentName: string;
10
+ static space: ComponentSpace;
10
11
  static params: {
11
12
  offsetX: {
12
13
  label: string;
@@ -11,6 +11,7 @@ const clampAnchor = (value) => Math.min(1, Math.max(0, value));
11
11
  */
12
12
  export class Sprite extends Component {
13
13
  static componentName = 'Sprite';
14
+ static space = '2d';
14
15
  static params = {
15
16
  offsetX: { label: 'x offset' },
16
17
  offsetY: { label: 'y offset' },
@@ -0,0 +1,46 @@
1
+ import * as THREE from 'three/webgpu';
2
+ import { Component, type ComponentSpace } from '../component.js';
3
+ import type { Vec3Json } from '../scene-camera-3d.js';
4
+ /**
5
+ * The 3D scene's sunlight (ADR 0027, `CONTEXT.md` Sun): a directional light
6
+ * that shines along `direction`, in world space — where its entity stands, or
7
+ * how it is rotated, does not matter. Color is `0xrrggbb`; no shadows. A Sun
8
+ * in a 2D scene creates nothing. The three light lives in the Game's scene,
9
+ * moves with its props, and leaves with the entity.
10
+ */
11
+ export declare class Sun extends Component {
12
+ static componentName: string;
13
+ static space: ComponentSpace;
14
+ static params: {
15
+ direction: {
16
+ label: string;
17
+ kind: "vector3";
18
+ };
19
+ color: {
20
+ label: string;
21
+ kind: "color";
22
+ };
23
+ intensity: {
24
+ label: string;
25
+ min: number;
26
+ step: number;
27
+ };
28
+ };
29
+ static transient: string[];
30
+ /** The three light, null in a 2D scene or before the component is ready. */
31
+ light: THREE.DirectionalLight | null;
32
+ private _direction;
33
+ private _color;
34
+ private _intensity;
35
+ /** Where the light travels, as a world-space vector `[x, y, z]`; any length but zero. */
36
+ get direction(): Vec3Json;
37
+ set direction(value: Vec3Json);
38
+ get color(): number;
39
+ set color(value: number);
40
+ get intensity(): number;
41
+ set intensity(value: number);
42
+ onReady(): void;
43
+ onDestroy(): void;
44
+ /** Puts the light on the opposite side of the origin from where it travels, targeting the origin. */
45
+ private aim;
46
+ }
@@ -0,0 +1,74 @@
1
+ import * as THREE from 'three/webgpu';
2
+ import { Component } from '../component.js';
3
+ const DEFAULT_DIRECTION = [-1, -2, -1];
4
+ /** Scene JSON is untyped at runtime: only an array of three finite numbers, not all zero, aims a light. */
5
+ const finiteDirection = (value) => Array.isArray(value) && value.length === 3 && value.every(Number.isFinite) && value.some((component) => component !== 0);
6
+ /**
7
+ * The 3D scene's sunlight (ADR 0027, `CONTEXT.md` Sun): a directional light
8
+ * that shines along `direction`, in world space — where its entity stands, or
9
+ * how it is rotated, does not matter. Color is `0xrrggbb`; no shadows. A Sun
10
+ * in a 2D scene creates nothing. The three light lives in the Game's scene,
11
+ * moves with its props, and leaves with the entity.
12
+ */
13
+ export class Sun extends Component {
14
+ static componentName = 'Sun';
15
+ static space = '3d';
16
+ static params = {
17
+ direction: { label: 'Direction', kind: 'vector3' },
18
+ color: { label: 'Color', kind: 'color' },
19
+ intensity: { label: 'Intensity', min: 0, step: 0.1 },
20
+ };
21
+ static transient = ['light'];
22
+ /** The three light, null in a 2D scene or before the component is ready. */
23
+ light = null;
24
+ _direction = [...DEFAULT_DIRECTION];
25
+ _color = 0xffffff;
26
+ _intensity = 1;
27
+ /** Where the light travels, as a world-space vector `[x, y, z]`; any length but zero. */
28
+ get direction() {
29
+ return [...this._direction];
30
+ }
31
+ set direction(value) {
32
+ // A zero or unreadable direction keeps the last good one: there is nothing to aim along.
33
+ if (!finiteDirection(value))
34
+ return;
35
+ this._direction = [...value];
36
+ this.aim();
37
+ }
38
+ get color() {
39
+ return this._color;
40
+ }
41
+ set color(value) {
42
+ this._color = value;
43
+ this.light?.color.setHex(value);
44
+ }
45
+ get intensity() {
46
+ return this._intensity;
47
+ }
48
+ set intensity(value) {
49
+ this._intensity = value;
50
+ if (this.light)
51
+ this.light.intensity = value;
52
+ }
53
+ onReady() {
54
+ if (this.game.space !== '3d')
55
+ return;
56
+ const light = new THREE.DirectionalLight(this._color, this._intensity);
57
+ this.light = light;
58
+ // The target joins the scene too, or three never updates its world matrix.
59
+ this.game.scene.add(light, light.target);
60
+ this.aim();
61
+ }
62
+ onDestroy() {
63
+ this.light?.removeFromParent();
64
+ this.light?.target.removeFromParent();
65
+ }
66
+ /** Puts the light on the opposite side of the origin from where it travels, targeting the origin. */
67
+ aim() {
68
+ if (!this.light)
69
+ return;
70
+ const [x, y, z] = this._direction;
71
+ this.light.position.set(-x, -y, -z).normalize();
72
+ this.light.target.position.set(0, 0, 0);
73
+ }
74
+ }
@@ -1,10 +1,11 @@
1
- import { Component } from '../component.js';
1
+ import { Component, type ComponentSpace } from '../component.js';
2
2
  import { SOLID_SOURCE_SYMBOL, type SolidSource } from '../scene-solids.js';
3
3
  import { type TilemapCell, type TilemapCellBounds } from '../tilemap-grid.js';
4
4
  import { Solid } from './solid.js';
5
5
  /** One authorable cell map rendered as a single merged geometry. */
6
6
  export declare class Tilemap extends Component implements SolidSource {
7
7
  static componentName: string;
8
+ static space: ComponentSpace;
8
9
  static params: {
9
10
  color: {
10
11
  label: string;
@@ -11,6 +11,7 @@ import { TRANSPARENT_TEXEL_ALPHA } from '../render-layers.js';
11
11
  /** One authorable cell map rendered as a single merged geometry. */
12
12
  export class Tilemap extends Component {
13
13
  static componentName = 'Tilemap';
14
+ static space = '2d';
14
15
  static params = {
15
16
  color: { label: 'color' },
16
17
  cols: { label: 'tileset columns', min: 1, step: 1 },
@@ -0,0 +1,18 @@
1
+ import type { Entity } from './entity.js';
2
+ /** A scene entity's position: `[x, y]` (z is 0) or `[x, y, z]`. */
3
+ export type PositionJson = [number, number] | [number, number, number];
4
+ /** The transform a scene or prefab entity declares, applied when it spawns. */
5
+ export interface TransformJson {
6
+ /** `[x, y]` (z = 0) or `[x, y, z]`; in a 3D scene z is a world axis. */
7
+ position?: PositionJson;
8
+ /** Euler angles in degrees, order XYZ, applied to the entity's node (3D scenes). */
9
+ rotation?: [number, number, number];
10
+ /** Per-axis scale of the entity's node. */
11
+ scale?: [number, number, number];
12
+ }
13
+ /**
14
+ * Applies a declared transform to a freshly spawned entity. A two-number
15
+ * position behaves exactly as it always did (z = 0); `rotation` is converted
16
+ * from degrees and `scale` replaces the node's unit scale.
17
+ */
18
+ export declare function applyTransformJson(entity: Entity, json: TransformJson): void;
@@ -0,0 +1,17 @@
1
+ import * as THREE from 'three/webgpu';
2
+ /**
3
+ * Applies a declared transform to a freshly spawned entity. A two-number
4
+ * position behaves exactly as it always did (z = 0); `rotation` is converted
5
+ * from degrees and `scale` replaces the node's unit scale.
6
+ */
7
+ export function applyTransformJson(entity, json) {
8
+ const { position, rotation, scale } = json;
9
+ if (position)
10
+ entity.position.set(position[0], position[1], position[2] ?? 0);
11
+ if (rotation) {
12
+ const [x, y, z] = rotation;
13
+ entity.node.rotation.set(THREE.MathUtils.degToRad(x), THREE.MathUtils.degToRad(y), THREE.MathUtils.degToRad(z), 'XYZ');
14
+ }
15
+ if (scale)
16
+ entity.node.scale.set(scale[0], scale[1], scale[2]);
17
+ }
package/dist/game.d.ts CHANGED
@@ -1,19 +1,27 @@
1
1
  import * as THREE from 'three/webgpu';
2
2
  import { AssetLoader } from './assets/asset-loader.js';
3
+ import type { ModelBackend } from './assets/model-backend.js';
3
4
  import type { TextureBackend } from './assets/texture-backend.js';
4
5
  import type { AudioBackend } from './audio/backend.js';
5
6
  import { AudioSubsystem } from './audio/audio-subsystem.js';
6
- import { type SceneCameraJson } from './camera.js';
7
+ import type { SceneCameraJson } from './camera.js';
8
+ import { type GameCamera, type WorldPoint } from './camera-projection.js';
9
+ import { CameraRig } from './camera-rig.js';
7
10
  import { CameraEffects } from './camera-effects.js';
8
11
  import type { Component } from './component.js';
9
12
  import { Entity } from './entity.js';
13
+ import type { PositionJson } from './entity-transform.js';
10
14
  import { Emitter } from './events.js';
11
15
  import { GameTime } from './game-time.js';
12
16
  import { Input, type InputBindings } from './input.js';
17
+ import { PhysicsHost } from './physics-3d/physics-host.js';
18
+ import type { PhysicsBackend } from './physics-3d/rapier-module.js';
13
19
  import { Pointer } from './pointer.js';
14
20
  import { GamePost } from './post-effects.js';
15
21
  import { type RenderBackend } from './render-readiness.js';
16
22
  import { type SceneJson, type SceneRegistry, type SceneRenderJson } from './scene.js';
23
+ import { type SceneSimulationJson } from './scene-simulation.js';
24
+ import { type SceneSpace } from './scene-space.js';
17
25
  import { GameLighting } from './scene-lighting.js';
18
26
  import { type SpatialQuery } from './spatial-query.js';
19
27
  import { Stats, type StatValue } from './stats.js';
@@ -33,7 +41,7 @@ export interface GameOptions {
33
41
  /** Fixed resolution (from the project's game.json); absent = fill the canvas. */
34
42
  resolution?: GameResolution;
35
43
  /** Control overrides (action → key and `Gamepad:` codes) on top of the defaults. */
36
- bindings?: InputBindings;
44
+ bindings?: Readonly<InputBindings>;
37
45
  /** Radial stick dead zone of `game.input`, in [0, 1); absent or invalid means 0.2 (ADR 0023). */
38
46
  gamepadDeadZone?: number;
39
47
  /** Initial stat values (points, lives…) from the project's stats.json. */
@@ -53,11 +61,20 @@ export interface GameOptions {
53
61
  * the real backend either way; `game.assets` always exists.
54
62
  */
55
63
  textures?: TextureBackend;
64
+ /** Replaces the real `GLTFLoader` implementation behind `game.assets.model` (the same seam, for glTF). */
65
+ models?: ModelBackend;
66
+ /**
67
+ * Where Rapier comes from for a 3D scene (ADR 0013's seam, applied to
68
+ * physics): resolves to the initialized module. Defaults to the real
69
+ * dynamic import, which only a 3D scene triggers; a project's own tests
70
+ * pass the module they initialized once, or a rejecting stub.
71
+ */
72
+ physics?: PhysicsBackend;
56
73
  }
57
74
  export type UpdateFn = (dt: number) => void;
58
75
  export interface SpawnPrefabOptions {
59
76
  name?: string;
60
- position?: [number, number];
77
+ position?: PositionJson;
61
78
  }
62
79
  /** The Project's scenes by name (a file's stem), plus the registry shared by all of them. */
63
80
  export interface SceneCatalog {
@@ -72,7 +89,6 @@ export type ParamOverrides = Record<string, Record<string, Record<string, number
72
89
  */
73
90
  export declare class Game {
74
91
  readonly scene: THREE.Scene<THREE.Object3DEventMap>;
75
- readonly camera: THREE.OrthographicCamera;
76
92
  readonly input: Input;
77
93
  readonly pointer: Pointer;
78
94
  readonly query: SpatialQuery;
@@ -100,6 +116,8 @@ export declare class Game {
100
116
  readonly lighting: GameLighting;
101
117
  /** The live scene's Post Effects (vignette, color grade); they die with their scene. */
102
118
  readonly post: GamePost;
119
+ /** The live 3D scene's Rapier world and bodies (ADR 0028); package-internal, read by the physics components and the Runtime Snapshot. */
120
+ readonly physics: PhysicsHost;
103
121
  /** Registry retained by loadScene for runtime prefab spawning. */
104
122
  registry: SceneRegistry | null;
105
123
  paramOverrides: ParamOverrides;
@@ -131,8 +149,17 @@ export declare class Game {
131
149
  private readonly baseViewHeight;
132
150
  private viewHeight;
133
151
  private sceneCamera;
152
+ /** The orthographic camera of 2D scenes and the perspective one of 3D scenes (ADR 0027); read by the Runtime Snapshot, not part of the game-facing API. */
153
+ readonly cameraRig: CameraRig;
154
+ private sceneSpace;
155
+ /** A 3D scene's stereo pan, reading the live camera at every call (built once, not per frame). */
156
+ private readonly screenPan;
157
+ /** What following an entity reads from the Game, built once rather than per Simulation Step. */
158
+ private readonly followHost;
134
159
  private renderSort;
135
160
  private sceneProjection;
161
+ /** The Ambient Light of a 3D scene, fed by `lighting.ambient` (ADR 0027). */
162
+ private readonly ambientLight3d;
136
163
  /** Timestamp of the last animation frame; null until the loop's first frame seeds it. */
137
164
  private lastTime;
138
165
  /** Seconds of elapsed time not yet worth a whole Simulation Step (ADR 0014). */
@@ -153,7 +180,9 @@ export declare class Game {
153
180
  constructor(options: GameOptions);
154
181
  /**
155
182
  * Resolves once the renderer can draw; rejects, naming both Render
156
- * Backends, when neither initializes. Every call returns the same promise.
183
+ * Backends, when neither initializes. While the live scene is 3D it also
184
+ * waits for the physics module, and rejects naming its package if that
185
+ * fails to load (ADR 0028).
157
186
  * Frames before it simulate but draw nothing, so a host awaits it before
158
187
  * its first frame (ADR 0025).
159
188
  */
@@ -211,6 +240,8 @@ export declare class Game {
211
240
  * draw order stays layer-banded with spawn-order ties.
212
241
  */
213
242
  setSceneRender(json?: SceneRenderJson): void;
243
+ /** Adopts a scene's simulation block (gravity); called by loadScene beside setSceneRender. */
244
+ setSceneSimulation(json?: SceneSimulationJson): void;
214
245
  /**
215
246
  * Adopts a scene's camera block: jumps to its framing and, while
216
247
  * simulating, follows/clamps per its settings. Called by loadScene.
@@ -222,7 +253,21 @@ export declare class Game {
222
253
  removeEntity(entity: Entity): void;
223
254
  /** The scene's render projection; null keeps logical and render space identical. */
224
255
  get projection(): 'isometric' | null;
225
- /** Visible world height (2D camera zoom). */
256
+ /** The live scene's space (`render.space`); `'2d'` with no scene (ADR 0027). */
257
+ get space(): SceneSpace;
258
+ /** The camera the live scene draws with: orthographic in 2D scenes, perspective in 3D ones. */
259
+ get camera(): GameCamera;
260
+ /**
261
+ * Where a logical point falls on the canvas, in CSS px from its top-left
262
+ * corner inside the letterbox; null when a perspective camera cannot see it
263
+ * (behind its near plane). The one projection Anchored Pieces, spatial
264
+ * audio and the editor share (CA-6).
265
+ */
266
+ worldToScreen(point: WorldPoint): {
267
+ x: number;
268
+ y: number;
269
+ } | null;
270
+ /** Visible world height (2D camera zoom); a perspective camera does not use it. */
226
271
  get view(): number;
227
272
  setViewHeight(value: number): void;
228
273
  /** Shuts the game down completely (loop, input, GPU). */
@@ -279,14 +324,5 @@ export declare class Game {
279
324
  private componentUpdateSchedule;
280
325
  private updateSceneCamera;
281
326
  private renderPoint;
282
- /**
283
- * The audio listener's position (CA-8) in logical coordinates. The camera
284
- * itself only ever holds render-space coordinates (see `updateSceneCamera`,
285
- * `setSceneCamera`), so under `projection: 'isometric'` this is the exact
286
- * inverse of `renderPoint` — without it, distance-based attenuation would
287
- * measure render-space distance instead of real game distance.
288
- */
289
- private audioListenerPosition;
290
- private dispatchCollisions;
291
327
  private resize;
292
328
  }