@gg-web-engine/core 0.0.57 → 0.0.59

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 (97) hide show
  1. package/dist/2d/components/physics/i-physics-world-2d.component.d.ts +1 -1
  2. package/dist/2d/components/physics/i-rigid-body-2d.component.d.ts +1 -1
  3. package/dist/2d/components/physics/i-trigger-2d.component.d.ts +3 -3
  4. package/dist/2d/components/rendering/i-camera-2d.component.d.ts +5 -0
  5. package/dist/2d/components/rendering/i-display-object-2d.component.d.ts +1 -1
  6. package/dist/2d/components/rendering/i-renderer-2d.component.d.ts +1 -1
  7. package/dist/2d/components/rendering/i-visual-scene-2d.component.d.ts +2 -3
  8. package/dist/2d/entities/controllers/entity-2d-positioning.animator.d.ts +2 -2
  9. package/dist/2d/entities/entity-2d.d.ts +7 -7
  10. package/dist/2d/entities/i-renderable-2d.entity.d.ts +4 -4
  11. package/dist/2d/entities/renderer-2d.entity.d.ts +2 -1
  12. package/dist/2d/entities/trigger-2d.entity.d.ts +8 -4
  13. package/dist/2d/factories.d.ts +7 -7
  14. package/dist/2d/gg-2d-world.d.ts +38 -6
  15. package/dist/2d/gg-2d-world.js +27 -21
  16. package/dist/2d/index.d.ts +3 -0
  17. package/dist/2d/index.js +3 -0
  18. package/dist/2d/level-loader.d.ts +101 -0
  19. package/dist/2d/level-loader.js +88 -0
  20. package/dist/2d/loader.d.ts +11 -0
  21. package/dist/2d/loader.js +10 -0
  22. package/dist/3d/components/physics/i-physics-world-3d.component.d.ts +5 -5
  23. package/dist/3d/components/physics/i-raycast-vehicle.component.d.ts +1 -1
  24. package/dist/3d/components/physics/i-rigid-body-3d.component.d.ts +1 -1
  25. package/dist/3d/components/physics/i-trigger-3d.component.d.ts +3 -3
  26. package/dist/3d/components/rendering/{i-camera.component.d.ts → i-camera-3d.component.d.ts} +1 -1
  27. package/dist/3d/components/rendering/i-camera-3d.component.js +1 -0
  28. package/dist/3d/components/rendering/i-display-object-3d.component.d.ts +1 -1
  29. package/dist/3d/components/rendering/i-renderer-3d.component.d.ts +1 -2
  30. package/dist/3d/components/rendering/i-visual-scene-3d.component.d.ts +3 -4
  31. package/dist/3d/entities/camera-3d.entity.d.ts +24 -0
  32. package/dist/3d/entities/camera-3d.entity.js +35 -0
  33. package/dist/3d/entities/controllers/animators/camera-3d.animator.d.ts +3 -3
  34. package/dist/3d/entities/controllers/animators/entity-3d-positioning.animator.d.ts +2 -2
  35. package/dist/3d/entities/entity-3d.d.ts +6 -6
  36. package/dist/3d/entities/gg-car/gg-car.entity.d.ts +5 -5
  37. package/dist/3d/entities/i-renderable-3d.entity.d.ts +4 -4
  38. package/dist/3d/entities/map-graph-3d.entity.d.ts +6 -6
  39. package/dist/3d/entities/raycast-vehicle-3d.entity.d.ts +3 -3
  40. package/dist/3d/entities/renderer-3d.entity.d.ts +1 -6
  41. package/dist/3d/entities/renderer-3d.entity.js +0 -15
  42. package/dist/3d/entities/surface-following.entity.d.ts +3 -3
  43. package/dist/3d/entities/surface-following.entity.js +3 -0
  44. package/dist/3d/entities/trigger-3d.entity.d.ts +8 -4
  45. package/dist/3d/factories.d.ts +14 -14
  46. package/dist/3d/gg-3d-world.d.ts +37 -9
  47. package/dist/3d/gg-3d-world.js +23 -19
  48. package/dist/3d/index.d.ts +3 -1
  49. package/dist/3d/index.js +3 -1
  50. package/dist/3d/level-loader.d.ts +300 -0
  51. package/dist/3d/level-loader.js +267 -0
  52. package/dist/3d/loader.d.ts +55 -14
  53. package/dist/3d/loader.js +37 -8
  54. package/dist/3d/loaders.d.ts +4 -4
  55. package/dist/base/blueprint/blueprint-node.d.ts +75 -0
  56. package/dist/base/blueprint/blueprint-node.js +51 -0
  57. package/dist/base/blueprint/blueprint.d.ts +123 -0
  58. package/dist/base/blueprint/blueprint.js +86 -0
  59. package/dist/base/blueprint/nodes/remove-entity.node.d.ts +26 -0
  60. package/dist/base/blueprint/nodes/remove-entity.node.js +29 -0
  61. package/dist/base/components/i-world-component.d.ts +5 -5
  62. package/dist/base/components/physics/i-body.component.d.ts +5 -5
  63. package/dist/base/components/physics/i-physics-world.component.d.ts +13 -5
  64. package/dist/base/components/physics/i-rigid-body.component.d.ts +2 -2
  65. package/dist/base/components/physics/i-trigger.component.d.ts +4 -4
  66. package/dist/base/components/rendering/i-display-object.component.d.ts +6 -6
  67. package/dist/base/components/rendering/i-renderer.component.d.ts +5 -4
  68. package/dist/base/components/rendering/i-visual-scene.component.d.ts +4 -2
  69. package/dist/base/entities/controllers/animation-mixer.d.ts +3 -3
  70. package/dist/base/entities/group.entity.d.ts +17 -0
  71. package/dist/base/entities/group.entity.js +19 -0
  72. package/dist/base/entities/i-entity.d.ts +19 -8
  73. package/dist/base/entities/i-entity.js +28 -0
  74. package/dist/base/entities/i-renderable.entity.d.ts +2 -2
  75. package/dist/base/entities/i-renderer.entity.d.ts +11 -5
  76. package/dist/base/entities/i-renderer.entity.js +15 -0
  77. package/dist/base/gg-world.d.ts +42 -5
  78. package/dist/base/gg-world.js +45 -10
  79. package/dist/base/index.d.ts +6 -0
  80. package/dist/base/index.js +6 -0
  81. package/dist/base/inputs/mouse.input.js +1 -0
  82. package/dist/base/level-loader.d.ts +217 -0
  83. package/dist/base/level-loader.js +258 -0
  84. package/dist/base/math/numbers.d.ts +2 -1
  85. package/dist/base/math/numbers.js +7 -1
  86. package/dist/base/math/point2.d.ts +17 -1
  87. package/dist/base/math/point2.js +35 -0
  88. package/dist/base/math/point3.d.ts +2 -0
  89. package/dist/base/math/point3.js +15 -1
  90. package/dist/base/models/points.d.ts +20 -16
  91. package/dist/base/models/raycasting.d.ts +52 -0
  92. package/dist/base/models/raycasting.js +1 -0
  93. package/dist/version.d.ts +1 -1
  94. package/dist/version.js +1 -1
  95. package/package.json +1 -1
  96. package/tsconfig.json +3 -1
  97. /package/dist/{3d/components/rendering/i-camera.component.js → 2d/components/rendering/i-camera-2d.component.js} +0 -0
package/dist/3d/loader.js CHANGED
@@ -8,7 +8,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  });
9
9
  };
10
10
  import { Entity3d } from './entities/entity-3d';
11
- import { Pnt3, Qtrn } from '../base';
11
+ import { GroupEntity, Pnt3, Qtrn } from '../base';
12
+ import { Gg3dLevelLoader } from './level-loader';
12
13
  export var CachingStrategy;
13
14
  (function (CachingStrategy) {
14
15
  CachingStrategy[CachingStrategy["Nothing"] = 0] = "Nothing";
@@ -28,11 +29,38 @@ const cloneLoadResourcesResult = (loadResult) => ({
28
29
  body: body && body.clone(),
29
30
  })),
30
31
  });
31
- export class Gg3dLoader {
32
+ /**
33
+ * Flatten a `LoadResultWithProps` (and its recursively-nested `props`) into one flat list of every
34
+ * `Entity3d` it produced, for the built-in `"Glb"` level entity class to parent under a single
35
+ * `GroupEntity`.
36
+ */
37
+ function flattenGlbEntities(result) {
38
+ var _a;
39
+ return [...result.entities, ...((_a = result.props) !== null && _a !== void 0 ? _a : []).flatMap(prop => flattenGlbEntities(prop))];
40
+ }
41
+ /**
42
+ * Full 3D loader exposed as `Gg3dWorld.loader`: GLB+meta asset loading (`loadGgGlb` and friends)
43
+ * layered on top of `Gg3dLevelLoader`, so `registerClass`/`loadLevel`/`loadLevelFromUrl`/
44
+ * `getEntityByName` are all available directly on `world.loader`. Also registers a `"Glb"` level
45
+ * entity class (see `Glb3DSettings`) so a level JSON can place a GLB model declaratively, the same
46
+ * way it places primitives/triggers/cameras.
47
+ * @template TypeDoc - The type document repository
48
+ */
49
+ export class Gg3dLoader extends Gg3dLevelLoader {
32
50
  constructor(world) {
33
- this.world = world;
51
+ super(world);
34
52
  this.filesCache = new Map();
35
53
  this.loadResultCache = new Map();
54
+ this.registerClass('Glb', (w, settings) => __awaiter(this, void 0, void 0, function* () {
55
+ if (!settings.path) {
56
+ throw new Error('Path is required for Glb class');
57
+ }
58
+ const { path, position, rotation, cachingStrategy, loadProps, propsPath } = settings;
59
+ const result = yield this.loadGgGlb(path, Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, (position !== undefined ? { position } : {})), (rotation !== undefined ? { rotation } : {})), (cachingStrategy !== undefined ? { cachingStrategy } : {})), (loadProps !== undefined ? { loadProps } : {})), (propsPath !== undefined ? { propsPath } : {})));
60
+ const group = new GroupEntity();
61
+ group.addChildren(...flattenGlbEntities(result));
62
+ return group;
63
+ }));
36
64
  }
37
65
  loadGgGlbFiles(path_1) {
38
66
  return __awaiter(this, arguments, void 0, function* (path, useCache = false) {
@@ -57,6 +85,7 @@ export class Gg3dLoader {
57
85
  }
58
86
  loadGgGlbResources(path_1) {
59
87
  return __awaiter(this, arguments, void 0, function* (path, cachingStrategy = CachingStrategy.Nothing) {
88
+ var _a, _b;
60
89
  if (cachingStrategy == CachingStrategy.Entities && this.loadResultCache.has(path)) {
61
90
  const cached = this.loadResultCache.get(path);
62
91
  const cachedResult = cached instanceof Promise ? yield cached : cached;
@@ -67,21 +96,21 @@ export class Gg3dLoader {
67
96
  throw new Error('GLB not found');
68
97
  }
69
98
  const [object, bodies] = yield Promise.all([
70
- this.world.visualScene.loader.loadFromGgGlb(glb, meta),
71
- this.world.physicsWorld.loader.loadFromGgGlb(glb, meta),
99
+ (_a = this.world.visualScene) === null || _a === void 0 ? void 0 : _a.loader.loadFromGgGlb(glb, meta),
100
+ (_b = this.world.physicsWorld) === null || _b === void 0 ? void 0 : _b.loader.loadFromGgGlb(glb, meta),
72
101
  ]);
73
102
  const result = { resources: [], meta };
74
103
  if (!object) {
75
104
  return result;
76
105
  }
77
- if (bodies.length == 0) {
106
+ if ((bodies === null || bodies === void 0 ? void 0 : bodies.length) == 0) {
78
107
  result.resources.push({ object3D: object, body: null });
79
108
  }
80
- else if (bodies.length == 1) {
109
+ else if ((bodies === null || bodies === void 0 ? void 0 : bodies.length) == 1) {
81
110
  result.resources.push({ object3D: object, body: bodies[0] });
82
111
  }
83
112
  else {
84
- for (const body of bodies) {
113
+ for (const body of bodies || []) {
85
114
  result.resources.push({ object3D: object.popChild(body.name), body });
86
115
  }
87
116
  if (!object.isEmpty()) {
@@ -1,11 +1,11 @@
1
1
  import { GgMeta } from './models/gg-meta';
2
2
  import { IPhysicsWorld3dComponent } from './components/physics/i-physics-world-3d.component';
3
3
  import { PhysicsTypeDocRepo3D, VisualTypeDocRepo3D } from './gg-3d-world';
4
- export declare abstract class IPhysicsBody3dComponentLoader<TypeDoc extends PhysicsTypeDocRepo3D = PhysicsTypeDocRepo3D> {
4
+ export declare abstract class IPhysicsBody3dComponentLoader<PTypeDoc extends PhysicsTypeDocRepo3D = PhysicsTypeDocRepo3D> {
5
5
  protected readonly world: IPhysicsWorld3dComponent;
6
6
  protected constructor(world: IPhysicsWorld3dComponent);
7
- loadFromGgGlb(glbFile: ArrayBuffer, meta: GgMeta): Promise<TypeDoc['rigidBody'][]>;
7
+ loadFromGgGlb(glbFile: ArrayBuffer, meta: GgMeta): Promise<PTypeDoc['rigidBody'][]>;
8
8
  }
9
- export interface IDisplayObject3dComponentLoader<TypeDoc extends VisualTypeDocRepo3D = VisualTypeDocRepo3D> {
10
- loadFromGgGlb(glbFile: ArrayBuffer, meta: GgMeta): Promise<TypeDoc['displayObject'] | null>;
9
+ export interface IDisplayObject3dComponentLoader<VTypeDoc extends VisualTypeDocRepo3D = VisualTypeDocRepo3D> {
10
+ loadFromGgGlb(glbFile: ArrayBuffer, meta: GgMeta): Promise<VTypeDoc['displayObject'] | null>;
11
11
  }
@@ -0,0 +1,75 @@
1
+ import { Observable } from 'rxjs';
2
+ import { GgWorld, GgWorldTypeDocRepo } from '../gg-world';
3
+ /**
4
+ * Whether a {@link BlueprintNode} pin carries an execution pulse ("do this now", no meaningful
5
+ * payload - e.g. Unreal's white exec pins) or a data value ("here's a value", not itself a
6
+ * trigger - e.g. Unreal's colored data pins). Purely descriptive for now (introspection/future
7
+ * editor tooling) - {@link BlueprintNode.trigger} treats every input pin the same way at runtime,
8
+ * since a node is free to both react to and read a value from the same pin (see
9
+ * {@link RemoveEntityBlueprintNode}, whose single `"entity"` pin is a data pin that also triggers
10
+ * the node when fed a value - the same way an Unreal event node's payload pins double as the
11
+ * thing that fires the node).
12
+ */
13
+ export type BlueprintPinKind = 'exec' | 'data';
14
+ /**
15
+ * Declares one named pin a {@link BlueprintNode} exposes - either an input (fed a value via
16
+ * {@link BlueprintNode.trigger}) or an output (fired via {@link BlueprintNode.output}).
17
+ */
18
+ export interface BlueprintPinDefinition {
19
+ name: string;
20
+ kind: BlueprintPinKind;
21
+ }
22
+ /**
23
+ * One node in a {@link Blueprint} graph - the engine's analogue of a single Unreal Blueprint graph
24
+ * node: a small unit of behavior with named input pins that trigger it, named output pins it can
25
+ * fire in response, and a `settings` bag of static (non-pin) configuration baked in from its
26
+ * {@link BlueprintNodeJson} (e.g. `RemoveEntity`'s `dispose` flag). Register a concrete subclass's
27
+ * factory against a type alias via `LevelLoader.registerBlueprintNode` so `Blueprint` can
28
+ * instantiate it from JSON, the same way `LevelLoader.registerClass` works for entity classes.
29
+ * @template D - The position type
30
+ * @template R - The rotation type
31
+ * @template TypeDoc - The type document repository
32
+ */
33
+ export declare abstract class BlueprintNode<D = any, R = any, TypeDoc extends GgWorldTypeDocRepo<D, R> = GgWorldTypeDocRepo<D, R>> {
34
+ protected readonly world: GgWorld<D, R, TypeDoc>;
35
+ protected readonly settings: Record<string, any>;
36
+ /**
37
+ * This node's input pins - see {@link trigger} for how they're fed.
38
+ */
39
+ abstract readonly inputs: readonly BlueprintPinDefinition[];
40
+ /**
41
+ * This node's output pins - see {@link output} for how to observe them.
42
+ */
43
+ abstract readonly outputs: readonly BlueprintPinDefinition[];
44
+ private readonly outputSubjects;
45
+ constructor(world: GgWorld<D, R, TypeDoc>, settings: Record<string, any>);
46
+ private subjectFor;
47
+ /**
48
+ * Observe one of this node's output pins firing - what a {@link BlueprintLinkJson} subscribes
49
+ * to in order to feed a downstream node's input, or what an embedding `Blueprint`'s own
50
+ * `outputs` entry bubbles up to whatever triggered the blueprint.
51
+ * @param name - The output pin's name, per {@link outputs}
52
+ */
53
+ output(name: string): Observable<unknown>;
54
+ /**
55
+ * Fire one of this node's output pins - call from within {@link trigger} once the node has done
56
+ * whatever that output represents. A no-op if nothing is subscribed.
57
+ * @param name - The output pin's name, per {@link outputs}
58
+ * @param value - Payload for a data output pin; omit for a bare exec pulse
59
+ */
60
+ protected emit(name: string, value?: unknown): void;
61
+ /**
62
+ * Feed a value/pulse into one of this node's input pins (per {@link inputs}), running whatever
63
+ * behavior that pin represents. Called by `Blueprint` both for external triggers (via its own
64
+ * `inputs` aliasing) and for links from another node's output pin.
65
+ * @param inputName - The input pin's name, per {@link inputs}
66
+ * @param value - Payload carried in on a data pin, if any
67
+ */
68
+ abstract trigger(inputName: string, value?: unknown): void;
69
+ /**
70
+ * Release anything this node set up outside of its output subjects (timers, subscriptions,
71
+ * etc). Default no-op - override when a node needs it. `Blueprint.dispose` calls this on every
72
+ * node in its graph.
73
+ */
74
+ dispose(): void;
75
+ }
@@ -0,0 +1,51 @@
1
+ import { Subject } from 'rxjs';
2
+ /**
3
+ * One node in a {@link Blueprint} graph - the engine's analogue of a single Unreal Blueprint graph
4
+ * node: a small unit of behavior with named input pins that trigger it, named output pins it can
5
+ * fire in response, and a `settings` bag of static (non-pin) configuration baked in from its
6
+ * {@link BlueprintNodeJson} (e.g. `RemoveEntity`'s `dispose` flag). Register a concrete subclass's
7
+ * factory against a type alias via `LevelLoader.registerBlueprintNode` so `Blueprint` can
8
+ * instantiate it from JSON, the same way `LevelLoader.registerClass` works for entity classes.
9
+ * @template D - The position type
10
+ * @template R - The rotation type
11
+ * @template TypeDoc - The type document repository
12
+ */
13
+ export class BlueprintNode {
14
+ constructor(world, settings) {
15
+ this.world = world;
16
+ this.settings = settings;
17
+ this.outputSubjects = new Map();
18
+ }
19
+ subjectFor(name) {
20
+ let subject = this.outputSubjects.get(name);
21
+ if (!subject) {
22
+ subject = new Subject();
23
+ this.outputSubjects.set(name, subject);
24
+ }
25
+ return subject;
26
+ }
27
+ /**
28
+ * Observe one of this node's output pins firing - what a {@link BlueprintLinkJson} subscribes
29
+ * to in order to feed a downstream node's input, or what an embedding `Blueprint`'s own
30
+ * `outputs` entry bubbles up to whatever triggered the blueprint.
31
+ * @param name - The output pin's name, per {@link outputs}
32
+ */
33
+ output(name) {
34
+ return this.subjectFor(name).asObservable();
35
+ }
36
+ /**
37
+ * Fire one of this node's output pins - call from within {@link trigger} once the node has done
38
+ * whatever that output represents. A no-op if nothing is subscribed.
39
+ * @param name - The output pin's name, per {@link outputs}
40
+ * @param value - Payload for a data output pin; omit for a bare exec pulse
41
+ */
42
+ emit(name, value) {
43
+ this.subjectFor(name).next(value);
44
+ }
45
+ /**
46
+ * Release anything this node set up outside of its output subjects (timers, subscriptions,
47
+ * etc). Default no-op - override when a node needs it. `Blueprint.dispose` calls this on every
48
+ * node in its graph.
49
+ */
50
+ dispose() { }
51
+ }
@@ -0,0 +1,123 @@
1
+ import { Observable } from 'rxjs';
2
+ import { GgWorld, GgWorldTypeDocRepo } from '../gg-world';
3
+ import { BlueprintNode } from './blueprint-node';
4
+ /**
5
+ * A function that builds a {@link BlueprintNode} instance from its baked-in settings. Registered
6
+ * against a node type alias via `LevelLoader.registerBlueprintNode`, the same way
7
+ * {@link EntityGenerator} is registered against an entity class alias via `registerClass`.
8
+ * @template D - The position type
9
+ * @template R - The rotation type
10
+ * @template TypeDoc - The type document repository
11
+ */
12
+ export type BlueprintNodeFactory<D, R, TypeDoc extends GgWorldTypeDocRepo<D, R>> = (world: GgWorld<D, R, TypeDoc>, settings: Record<string, any>) => BlueprintNode<D, R, TypeDoc>;
13
+ /**
14
+ * A reference to one named pin on one node within a {@link BlueprintJson} graph - either end of a
15
+ * {@link BlueprintLinkJson}, or what a graph's own `inputs`/`outputs` entry aliases.
16
+ */
17
+ export interface BlueprintPinRef {
18
+ /**
19
+ * `id` of the node within the same `BlueprintJson.nodes` array
20
+ */
21
+ node: string;
22
+ /**
23
+ * Pin name on that node, per its `BlueprintNode.inputs`/`outputs`
24
+ */
25
+ pin: string;
26
+ }
27
+ /**
28
+ * JSON description of a single node instance within a {@link BlueprintJson} graph.
29
+ */
30
+ export interface BlueprintNodeJson {
31
+ /**
32
+ * Identifier for this node instance, unique within the same graph - referenced by
33
+ * `BlueprintLinkJson`/`BlueprintJson.inputs`/`BlueprintJson.outputs`.
34
+ */
35
+ id: string;
36
+ /**
37
+ * Node type alias, matching a type registered via `LevelLoader.registerBlueprintNode` (e.g. the
38
+ * built-in `"RemoveEntity"`).
39
+ */
40
+ type: string;
41
+ /**
42
+ * Static settings baked into the node (e.g. `RemoveEntity`'s `dispose` flag) - not wired at
43
+ * runtime, unlike a pin.
44
+ */
45
+ settings?: Record<string, any>;
46
+ }
47
+ /**
48
+ * JSON description of one wire connecting one node's output pin to another node's input pin
49
+ * within the same {@link BlueprintJson} graph. Whenever `from` fires, `to` is triggered with
50
+ * whatever value (if any) `from` fired with.
51
+ */
52
+ export interface BlueprintLinkJson {
53
+ from: BlueprintPinRef;
54
+ to: BlueprintPinRef;
55
+ }
56
+ /**
57
+ * A blueprint graph, serializable as a single JSON document - the engine's analogue of an Unreal
58
+ * Blueprint event graph. `nodes` are node instances (see {@link BlueprintNodeJson}), `links` wire
59
+ * one node's output pin to another's input pin, and `inputs`/`outputs` expose named entry/exit
60
+ * points at the graph's own boundary - each aliasing one node's pin - so embedding code (or, in
61
+ * the future, another blueprint nesting this one) doesn't need to know internal node ids. A level
62
+ * JSON references a `BlueprintJson` by name via its top-level `blueprints` map and an entity's
63
+ * `events` mapping - see `gg-engine-level-json`.
64
+ */
65
+ export interface BlueprintJson {
66
+ /**
67
+ * Node instances in this graph
68
+ */
69
+ nodes: BlueprintNodeJson[];
70
+ /**
71
+ * Wires wiring one node's output pin to another node's input pin
72
+ */
73
+ links?: BlueprintLinkJson[];
74
+ /**
75
+ * Named entry points into this graph, each aliasing one node's input pin - e.g.
76
+ * `{ "in": { "node": "n1", "pin": "entity" } }` lets external code trigger `"n1"`'s `"entity"`
77
+ * pin by calling `blueprint.trigger("in", value)` without knowing the internal node id. A
78
+ * blueprint bound to a level JSON entity event is always triggered through the entry named
79
+ * `"in"` - see `gg-engine-level-json`.
80
+ */
81
+ inputs?: Record<string, BlueprintPinRef>;
82
+ /**
83
+ * Named exit points out of this graph, each aliasing one node's output pin - for a future
84
+ * blueprint nested inside a larger graph to bubble one of its own nodes' outputs back out.
85
+ */
86
+ outputs?: Record<string, BlueprintPinRef>;
87
+ }
88
+ /**
89
+ * Runtime instance of a {@link BlueprintJson} graph: builds one {@link BlueprintNode} per
90
+ * `nodes` entry (via the node type registry passed in), wires every `links` entry as a live
91
+ * subscription from the source node's output pin to the target node's input pin, and exposes the
92
+ * graph's own `inputs`/`outputs` boundary. Each binding of a blueprint (e.g. one level JSON entity
93
+ * event) gets its own `Blueprint` instance - and therefore its own node instances - even when
94
+ * multiple bindings reference the same `BlueprintJson` by name, so per-node state (a future timer
95
+ * node's countdown, etc) is never accidentally shared between unrelated bindings.
96
+ * @template D - The position type
97
+ * @template R - The rotation type
98
+ * @template TypeDoc - The type document repository
99
+ */
100
+ export declare class Blueprint<D = any, R = any, TypeDoc extends GgWorldTypeDocRepo<D, R> = GgWorldTypeDocRepo<D, R>> {
101
+ private readonly json;
102
+ private readonly nodes;
103
+ private readonly linkSubscriptions;
104
+ constructor(world: GgWorld<D, R, TypeDoc>, json: BlueprintJson, registry: ReadonlyMap<string, BlueprintNodeFactory<D, R, TypeDoc>>);
105
+ /**
106
+ * Feed a value/pulse into one of this graph's named entry points (per `BlueprintJson.inputs`).
107
+ * A no-op (with a console warning) if no such input, or the node it aliases failed to build.
108
+ * @param inputName - Entry point name, per `BlueprintJson.inputs`
109
+ * @param value - Payload to hand to the aliased node's input pin, if any
110
+ */
111
+ trigger(inputName: string, value?: unknown): void;
112
+ /**
113
+ * Observe one of this graph's named exit points (per `BlueprintJson.outputs`) firing.
114
+ * @param outputName - Exit point name, per `BlueprintJson.outputs`
115
+ * @throws if no such output is declared, or the node it aliases failed to build
116
+ */
117
+ output(outputName: string): Observable<unknown>;
118
+ /**
119
+ * Unwire every link and dispose every node in this graph. Idempotent-ish - safe to call once
120
+ * per `Blueprint` instance, same lifetime contract as `IEntity.dispose`.
121
+ */
122
+ dispose(): void;
123
+ }
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Runtime instance of a {@link BlueprintJson} graph: builds one {@link BlueprintNode} per
3
+ * `nodes` entry (via the node type registry passed in), wires every `links` entry as a live
4
+ * subscription from the source node's output pin to the target node's input pin, and exposes the
5
+ * graph's own `inputs`/`outputs` boundary. Each binding of a blueprint (e.g. one level JSON entity
6
+ * event) gets its own `Blueprint` instance - and therefore its own node instances - even when
7
+ * multiple bindings reference the same `BlueprintJson` by name, so per-node state (a future timer
8
+ * node's countdown, etc) is never accidentally shared between unrelated bindings.
9
+ * @template D - The position type
10
+ * @template R - The rotation type
11
+ * @template TypeDoc - The type document repository
12
+ */
13
+ export class Blueprint {
14
+ constructor(world, json, registry) {
15
+ var _a, _b;
16
+ this.json = json;
17
+ this.nodes = new Map();
18
+ this.linkSubscriptions = [];
19
+ for (const nodeJson of json.nodes) {
20
+ const factory = registry.get(nodeJson.type);
21
+ if (!factory) {
22
+ console.warn(`No blueprint node type registered for "${nodeJson.type}" - skipping node "${nodeJson.id}"`);
23
+ continue;
24
+ }
25
+ this.nodes.set(nodeJson.id, factory(world, (_a = nodeJson.settings) !== null && _a !== void 0 ? _a : {}));
26
+ }
27
+ for (const link of (_b = json.links) !== null && _b !== void 0 ? _b : []) {
28
+ const source = this.nodes.get(link.from.node);
29
+ const target = this.nodes.get(link.to.node);
30
+ if (!source || !target) {
31
+ console.warn(`Blueprint link references an unknown node ("${link.from.node}" -> "${link.to.node}") - skipping`);
32
+ continue;
33
+ }
34
+ this.linkSubscriptions.push(source.output(link.from.pin).subscribe(value => target.trigger(link.to.pin, value)));
35
+ }
36
+ }
37
+ /**
38
+ * Feed a value/pulse into one of this graph's named entry points (per `BlueprintJson.inputs`).
39
+ * A no-op (with a console warning) if no such input, or the node it aliases failed to build.
40
+ * @param inputName - Entry point name, per `BlueprintJson.inputs`
41
+ * @param value - Payload to hand to the aliased node's input pin, if any
42
+ */
43
+ trigger(inputName, value) {
44
+ var _a;
45
+ const ref = (_a = this.json.inputs) === null || _a === void 0 ? void 0 : _a[inputName];
46
+ if (!ref) {
47
+ console.warn(`Blueprint has no declared input named "${inputName}" - ignoring trigger`);
48
+ return;
49
+ }
50
+ const node = this.nodes.get(ref.node);
51
+ if (!node) {
52
+ return;
53
+ }
54
+ node.trigger(ref.pin, value);
55
+ }
56
+ /**
57
+ * Observe one of this graph's named exit points (per `BlueprintJson.outputs`) firing.
58
+ * @param outputName - Exit point name, per `BlueprintJson.outputs`
59
+ * @throws if no such output is declared, or the node it aliases failed to build
60
+ */
61
+ output(outputName) {
62
+ var _a;
63
+ const ref = (_a = this.json.outputs) === null || _a === void 0 ? void 0 : _a[outputName];
64
+ if (!ref) {
65
+ throw new Error(`Blueprint has no declared output named "${outputName}"`);
66
+ }
67
+ const node = this.nodes.get(ref.node);
68
+ if (!node) {
69
+ throw new Error(`Blueprint output "${outputName}" references unknown node "${ref.node}"`);
70
+ }
71
+ return node.output(ref.pin);
72
+ }
73
+ /**
74
+ * Unwire every link and dispose every node in this graph. Idempotent-ish - safe to call once
75
+ * per `Blueprint` instance, same lifetime contract as `IEntity.dispose`.
76
+ */
77
+ dispose() {
78
+ for (const sub of this.linkSubscriptions) {
79
+ sub.unsubscribe();
80
+ }
81
+ this.linkSubscriptions.splice(0, this.linkSubscriptions.length);
82
+ for (const node of this.nodes.values()) {
83
+ node.dispose();
84
+ }
85
+ }
86
+ }
@@ -0,0 +1,26 @@
1
+ import { GgWorldTypeDocRepo } from '../../gg-world';
2
+ import { BlueprintNode, BlueprintPinDefinition } from '../blueprint-node';
3
+ /**
4
+ * Settings for the built-in `"RemoveEntity"` blueprint node - baked in from its
5
+ * {@link BlueprintNodeJson.settings}, not wired at runtime.
6
+ */
7
+ export interface RemoveEntityNodeSettings {
8
+ /**
9
+ * Whether to dispose the entity (release its components/children) as well as remove it from
10
+ * the world, same as the `dispose` argument of `GgWorld.removeEntity`. Defaults to `false`.
11
+ */
12
+ dispose?: boolean;
13
+ }
14
+ /**
15
+ * Built-in blueprint node: removes an entity from the world - the blueprint analogue of calling
16
+ * `world.removeEntity(entity, dispose)` directly. Has one input pin, `"entity"` (a data pin that
17
+ * also acts as this node's trigger - feeding it a value runs the node, same as an Unreal event
18
+ * node's payload pin doubling as its exec pulse) and no output pins. Whether the removal also
19
+ * disposes the entity is controlled by the static `dispose` setting, not a pin - see
20
+ * {@link RemoveEntityNodeSettings}.
21
+ */
22
+ export declare class RemoveEntityBlueprintNode<D = any, R = any, TypeDoc extends GgWorldTypeDocRepo<D, R> = GgWorldTypeDocRepo<D, R>> extends BlueprintNode<D, R, TypeDoc> {
23
+ readonly inputs: readonly BlueprintPinDefinition[];
24
+ readonly outputs: readonly BlueprintPinDefinition[];
25
+ trigger(inputName: string, value?: unknown): void;
26
+ }
@@ -0,0 +1,29 @@
1
+ import { IEntity } from '../../entities/i-entity';
2
+ import { BlueprintNode } from '../blueprint-node';
3
+ /**
4
+ * Built-in blueprint node: removes an entity from the world - the blueprint analogue of calling
5
+ * `world.removeEntity(entity, dispose)` directly. Has one input pin, `"entity"` (a data pin that
6
+ * also acts as this node's trigger - feeding it a value runs the node, same as an Unreal event
7
+ * node's payload pin doubling as its exec pulse) and no output pins. Whether the removal also
8
+ * disposes the entity is controlled by the static `dispose` setting, not a pin - see
9
+ * {@link RemoveEntityNodeSettings}.
10
+ */
11
+ export class RemoveEntityBlueprintNode extends BlueprintNode {
12
+ constructor() {
13
+ super(...arguments);
14
+ this.inputs = [{ name: 'entity', kind: 'data' }];
15
+ this.outputs = [];
16
+ }
17
+ trigger(inputName, value) {
18
+ var _a;
19
+ if (inputName !== 'entity') {
20
+ return;
21
+ }
22
+ if (!(value instanceof IEntity)) {
23
+ console.warn('RemoveEntity blueprint node triggered without a valid entity reference - ignoring');
24
+ return;
25
+ }
26
+ const settings = this.settings;
27
+ this.world.removeEntity(value, (_a = settings.dispose) !== null && _a !== void 0 ? _a : false);
28
+ }
29
+ }
@@ -1,8 +1,8 @@
1
1
  import { IEntity } from '../entities/i-entity';
2
- import { GgWorld, PhysicsTypeDocRepo, VisualTypeDocRepo } from '../gg-world';
3
- export interface IWorldComponent<D, R, VTypeDoc extends VisualTypeDocRepo<D, R> = VisualTypeDocRepo<D, R>, PTypeDoc extends PhysicsTypeDocRepo<D, R> = PhysicsTypeDocRepo<D, R>> {
2
+ import { GgWorld, GgWorldTypeDocRepo } from '../gg-world';
3
+ import { IComponent } from './i-component';
4
+ export interface IWorldComponent<D, R, TypeDoc extends GgWorldTypeDocRepo<D, R> = GgWorldTypeDocRepo<D, R>> extends IComponent {
4
5
  entity: IEntity | null;
5
- addToWorld(world: GgWorld<D, R, VTypeDoc, PTypeDoc>): void;
6
- removeFromWorld(world: GgWorld<D, R, VTypeDoc, PTypeDoc>, dispose?: boolean): void;
7
- dispose(): void;
6
+ addToWorld(world: GgWorld<D, R, TypeDoc>): void;
7
+ removeFromWorld(world: GgWorld<D, R, TypeDoc>, dispose?: boolean): void;
8
8
  }
@@ -1,8 +1,8 @@
1
1
  import { IEntity } from '../../entities/i-entity';
2
2
  import { IWorldComponent } from '../i-world-component';
3
- import { GgWorld, PhysicsTypeDocRepo, VisualTypeDocRepo } from '../../gg-world';
3
+ import { GgWorld, GgWorldTypeDocPPatch, PhysicsTypeDocRepo } from '../../gg-world';
4
4
  import { CollisionGroup, DebugBodySettings } from '../../models/body-options';
5
- export interface IBodyComponent<D, R, TypeDoc extends PhysicsTypeDocRepo<D, R> = PhysicsTypeDocRepo<D, R>> extends IWorldComponent<D, R, VisualTypeDocRepo<D, R>, TypeDoc> {
5
+ export interface IBodyComponent<D, R, PTypeDoc extends PhysicsTypeDocRepo<D, R> = PhysicsTypeDocRepo<D, R>> extends IWorldComponent<D, R, GgWorldTypeDocPPatch<D, R, PTypeDoc>> {
6
6
  entity: IEntity | null;
7
7
  position: D;
8
8
  rotation: R;
@@ -13,7 +13,7 @@ export interface IBodyComponent<D, R, TypeDoc extends PhysicsTypeDocRepo<D, R> =
13
13
  set interactWithCollisionGroups(value: ReadonlyArray<CollisionGroup> | 'all');
14
14
  /** body info for physics debugger view */
15
15
  readonly debugBodySettings: DebugBodySettings<any>;
16
- clone(): IBodyComponent<D, R, TypeDoc>;
17
- addToWorld(world: GgWorld<D, R, VisualTypeDocRepo<D, R>, TypeDoc>): void;
18
- removeFromWorld(world: GgWorld<D, R, VisualTypeDocRepo<D, R>, TypeDoc>): void;
16
+ clone(): IBodyComponent<D, R, PTypeDoc>;
17
+ addToWorld(world: GgWorld<D, R, GgWorldTypeDocPPatch<D, R, PTypeDoc>>): void;
18
+ removeFromWorld(world: GgWorld<D, R, GgWorldTypeDocPPatch<D, R, PTypeDoc>>): void;
19
19
  }
@@ -2,6 +2,7 @@ import { PhysicsTypeDocRepo } from '../../gg-world';
2
2
  import { IComponent } from '../i-component';
3
3
  import { CollisionGroup } from '../../models/body-options';
4
4
  import { Subject } from 'rxjs';
5
+ import { RaycastOptions, RaycastResult } from '../../models/raycasting';
5
6
  /**
6
7
  * Interface representing a physics world component.
7
8
  *
@@ -9,11 +10,11 @@ import { Subject } from 'rxjs';
9
10
  * @template R - Type representing the physics engine's rigid body.
10
11
  * @template TypeDoc - Physics typings repository.
11
12
  */
12
- export interface IPhysicsWorldComponent<D, R, TypeDoc extends PhysicsTypeDocRepo<D, R> = PhysicsTypeDocRepo<D, R>> extends IComponent {
13
+ export interface IPhysicsWorldComponent<D, R, PTypeDoc extends PhysicsTypeDocRepo<D, R> = PhysicsTypeDocRepo<D, R>> extends IComponent {
13
14
  /**
14
15
  * Factory function for creating physics-related objects.
15
16
  */
16
- readonly factory: TypeDoc['factory'];
17
+ readonly factory: PTypeDoc['factory'];
17
18
  /**
18
19
  * The gravity vector affecting the physics world.
19
20
  */
@@ -21,15 +22,15 @@ export interface IPhysicsWorldComponent<D, R, TypeDoc extends PhysicsTypeDocRepo
21
22
  /**
22
23
  * Event emitter that emits newly added physics components.
23
24
  */
24
- readonly added$: Subject<TypeDoc['rigidBody'] | TypeDoc['trigger'] | any>;
25
+ readonly added$: Subject<PTypeDoc['rigidBody'] | PTypeDoc['trigger'] | any>;
25
26
  /**
26
27
  * Event emitter that emits just removed physics components.
27
28
  */
28
- readonly removed$: Subject<TypeDoc['rigidBody'] | TypeDoc['trigger'] | any>;
29
+ readonly removed$: Subject<PTypeDoc['rigidBody'] | PTypeDoc['trigger'] | any>;
29
30
  /**
30
31
  * List of currently added physics components in the world.
31
32
  */
32
- readonly children: (TypeDoc['rigidBody'] | TypeDoc['trigger'] | any)[];
33
+ readonly children: (PTypeDoc['rigidBody'] | PTypeDoc['trigger'] | any)[];
33
34
  /**
34
35
  * The main collision group. All physics bodies have this collision group set by default.
35
36
  */
@@ -58,4 +59,11 @@ export interface IPhysicsWorldComponent<D, R, TypeDoc extends PhysicsTypeDocRepo
58
59
  * @param group - The collision group to be removed.
59
60
  */
60
61
  deregisterCollisionGroup(group: CollisionGroup): void;
62
+ /**
63
+ * Performs a raycast in the physics world.
64
+ *
65
+ * @param options - The options for the raycast.
66
+ * @returns The result of the raycast.
67
+ */
68
+ raycast(options: RaycastOptions<D>): RaycastResult<D, PTypeDoc['rigidBody'] | PTypeDoc['trigger']>;
61
69
  }
@@ -1,9 +1,9 @@
1
1
  import { IBodyComponent } from './i-body.component';
2
2
  import { PhysicsTypeDocRepo } from '../../gg-world';
3
- export interface IRigidBodyComponent<D, R, TypeDoc extends PhysicsTypeDocRepo<D, R> = PhysicsTypeDocRepo<D, R>> extends IBodyComponent<D, R, TypeDoc> {
3
+ export interface IRigidBodyComponent<D, R, PTypeDoc extends PhysicsTypeDocRepo<D, R> = PhysicsTypeDocRepo<D, R>> extends IBodyComponent<D, R, PTypeDoc> {
4
4
  linearVelocity: D;
5
5
  angularVelocity: R | D;
6
- clone(): IRigidBodyComponent<D, R, TypeDoc>;
6
+ clone(): IRigidBodyComponent<D, R, PTypeDoc>;
7
7
  /** clear velocities etc. */
8
8
  resetMotion(): void;
9
9
  }
@@ -2,9 +2,9 @@ import { Observable } from 'rxjs';
2
2
  import { IRigidBodyComponent } from './i-rigid-body.component';
3
3
  import { IBodyComponent } from './i-body.component';
4
4
  import { PhysicsTypeDocRepo } from '../../gg-world';
5
- export interface ITriggerComponent<D, R, TypDoc extends PhysicsTypeDocRepo<D, R> = PhysicsTypeDocRepo<D, R>> extends IBodyComponent<D, R, TypDoc> {
6
- get onEntityEntered(): Observable<IRigidBodyComponent<D, R, TypDoc>>;
7
- get onEntityLeft(): Observable<IRigidBodyComponent<D, R, TypDoc> | null>;
8
- clone(): ITriggerComponent<D, R, TypDoc>;
5
+ export interface ITriggerComponent<D, R, PTypeDoc extends PhysicsTypeDocRepo<D, R> = PhysicsTypeDocRepo<D, R>> extends IBodyComponent<D, R, PTypeDoc> {
6
+ get onEntityEntered(): Observable<IRigidBodyComponent<D, R, PTypeDoc>>;
7
+ get onEntityLeft(): Observable<IRigidBodyComponent<D, R, PTypeDoc> | null>;
8
+ clone(): ITriggerComponent<D, R, PTypeDoc>;
9
9
  checkOverlaps(): void;
10
10
  }
@@ -1,15 +1,15 @@
1
- import { GgBox, GgWorld, VisualTypeDocRepo } from '../../../base';
1
+ import { GgBox, GgWorld, GgWorldTypeDocVPatch, VisualTypeDocRepo } from '../../../base';
2
2
  import { IWorldComponent } from '../i-world-component';
3
- export interface IDisplayObjectComponent<D, R, TypeDoc extends VisualTypeDocRepo<D, R> = VisualTypeDocRepo<D, R>> extends IWorldComponent<D, R, TypeDoc> {
3
+ export interface IDisplayObjectComponent<D, R, VTypeDoc extends VisualTypeDocRepo<D, R> = VisualTypeDocRepo<D, R>> extends IWorldComponent<D, R, GgWorldTypeDocVPatch<D, R, VTypeDoc>> {
4
4
  position: D;
5
5
  rotation: R;
6
6
  scale: D;
7
7
  visible: boolean;
8
8
  name: string;
9
9
  isEmpty(): boolean;
10
- popChild(name: string): IDisplayObjectComponent<D, R, TypeDoc> | null;
10
+ popChild(name: string): IDisplayObjectComponent<D, R, VTypeDoc> | null;
11
11
  getBoundings(): GgBox<D>;
12
- clone(): IDisplayObjectComponent<D, R, TypeDoc>;
13
- addToWorld(world: GgWorld<D, R, TypeDoc>): void;
14
- removeFromWorld(world: GgWorld<D, R, TypeDoc>): void;
12
+ clone(): IDisplayObjectComponent<D, R, VTypeDoc>;
13
+ addToWorld(world: GgWorld<D, R, GgWorldTypeDocVPatch<D, R, VTypeDoc>>): void;
14
+ removeFromWorld(world: GgWorld<D, R, GgWorldTypeDocVPatch<D, R, VTypeDoc>>): void;
15
15
  }