@driftengine/script 3.61.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/NOTICE +9 -0
- package/README.md +81 -0
- package/dist/bindings/ai.d.ts +66 -0
- package/dist/bindings/ai.js +173 -0
- package/dist/bindings/animation.d.ts +13 -0
- package/dist/bindings/animation.js +177 -0
- package/dist/bindings/audio.d.ts +56 -0
- package/dist/bindings/audio.js +126 -0
- package/dist/bindings/behavior.d.ts +36 -0
- package/dist/bindings/behavior.js +101 -0
- package/dist/bindings/chemistry.d.ts +72 -0
- package/dist/bindings/chemistry.js +253 -0
- package/dist/bindings/core.d.ts +172 -0
- package/dist/bindings/core.js +814 -0
- package/dist/bindings/editor.d.ts +53 -0
- package/dist/bindings/editor.js +248 -0
- package/dist/bindings/entities.d.ts +73 -0
- package/dist/bindings/entities.js +339 -0
- package/dist/bindings/interface.d.ts +55 -0
- package/dist/bindings/interface.js +200 -0
- package/dist/bindings/navigation.d.ts +36 -0
- package/dist/bindings/navigation.js +255 -0
- package/dist/bindings/network.d.ts +51 -0
- package/dist/bindings/network.js +96 -0
- package/dist/bindings/render.d.ts +71 -0
- package/dist/bindings/render.js +143 -0
- package/dist/bindings/rollback.d.ts +41 -0
- package/dist/bindings/rollback.js +74 -0
- package/dist/bindings/sprites.d.ts +53 -0
- package/dist/bindings/sprites.js +186 -0
- package/dist/bindings/terrain.d.ts +54 -0
- package/dist/bindings/terrain.js +138 -0
- package/dist/bindings/xr.d.ts +56 -0
- package/dist/bindings/xr.js +155 -0
- package/dist/entityHost.d.ts +80 -0
- package/dist/entityHost.js +159 -0
- package/dist/host.d.ts +153 -0
- package/dist/host.js +233 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.js +18 -0
- package/package.json +68 -0
- package/src/bindings/ai.ts +306 -0
- package/src/bindings/animation.ts +346 -0
- package/src/bindings/audio.ts +185 -0
- package/src/bindings/behavior.ts +181 -0
- package/src/bindings/chemistry.ts +548 -0
- package/src/bindings/core.ts +1604 -0
- package/src/bindings/editor.ts +393 -0
- package/src/bindings/entities.ts +550 -0
- package/src/bindings/interface.ts +300 -0
- package/src/bindings/navigation.ts +367 -0
- package/src/bindings/network.ts +178 -0
- package/src/bindings/render.ts +191 -0
- package/src/bindings/rollback.ts +110 -0
- package/src/bindings/sprites.ts +310 -0
- package/src/bindings/terrain.ts +180 -0
- package/src/bindings/xr.ts +272 -0
- package/src/entityHost.ts +219 -0
- package/src/host.ts +465 -0
- package/src/index.ts +41 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { CapabilityDefinition, OpaqueType } from 'driftscript';
|
|
2
|
+
export declare const EDITOR_MODULE = "drift/editor";
|
|
3
|
+
/**
|
|
4
|
+
* `drift/editor` — the transform tool, driven from a script that never ships.
|
|
5
|
+
*
|
|
6
|
+
* `Gizmo` in `@driftengine/core` answers what a ray is over and what a drag does to a transform;
|
|
7
|
+
* this is the surface a tool script drives it through.
|
|
8
|
+
*
|
|
9
|
+
* **`editor` is an `Effect` outside `DETERMINISTIC_EFFECTS`, and belongs there.** A gizmo is a view
|
|
10
|
+
* of a transform being edited by a person, so a `@deterministic` system has no business touching
|
|
11
|
+
* one. The annotation refusing it is the property `@editor` exists to pair with.
|
|
12
|
+
*
|
|
13
|
+
* **A name is unique across the module, not per type.** Two opaque types make that easy to forget:
|
|
14
|
+
* a module is one namespace and one implementation map, so `mode` cannot mean the gizmo's mode to a
|
|
15
|
+
* `Gizmo` and the session's to an `Editor`. They are `gizmoMode` and `mode`.
|
|
16
|
+
*
|
|
17
|
+
* **Two opaque types.** `Gizmo` is the transform tool; `Editor` is `@driftengine/editor`'s
|
|
18
|
+
* `EditorHost` — the selection, the tree and the play state. Both reach a script through `uses`,
|
|
19
|
+
* the way a `Terrain` and a `NavGraph` do, so this module needs nothing from the host and is
|
|
20
|
+
* registered unconditionally.
|
|
21
|
+
*
|
|
22
|
+
* ## Nothing here picks, and nothing edits a field by name
|
|
23
|
+
*
|
|
24
|
+
* A pick needs a ray, a ray needs a camera and a pointer, and both belong to the application. The
|
|
25
|
+
* host runs `hover`, `beginDrag` and `updateDrag`; a script reads what they found and says what
|
|
26
|
+
* kind of tool it is. That is what an editor script contains: a key that switches to rotation, a
|
|
27
|
+
* modifier that switches to local space, and a read of where the thing ended up.
|
|
28
|
+
*
|
|
29
|
+
* The same argument one level along: an inspector row is addressed by its index in a list the host
|
|
30
|
+
* built this frame, and a script wanting to write `Health.current` on a selection would be doing
|
|
31
|
+
* entity work `drift/ecs` already does properly, with a component handle and a checked field.
|
|
32
|
+
*
|
|
33
|
+
* ## Three capabilities where an argument would have done, deliberately
|
|
34
|
+
*
|
|
35
|
+
* `translateMode`, `rotateMode` and `scaleMode` rather than `setMode(gizmo, mode)`. The language has
|
|
36
|
+
* no enum, so the argument would be a string or a number, and an unrecognised one leaves exactly two
|
|
37
|
+
* options — change nothing, the silent no-op `host.ts` rules out, or throw inside a frame, which
|
|
38
|
+
* `AGENTS.md` rules out. Three names make the invalid call impossible to write, and the checker
|
|
39
|
+
* rather than a runtime branch is what refuses it. `space` is the same argument again.
|
|
40
|
+
*
|
|
41
|
+
* The *readers* are strings because a reader is total: there is no invalid value to answer.
|
|
42
|
+
*/
|
|
43
|
+
export declare const EDITOR_TYPES: readonly OpaqueType[];
|
|
44
|
+
export declare const EDITOR_CAPABILITIES: readonly CapabilityDefinition[];
|
|
45
|
+
/**
|
|
46
|
+
* **No services, and that is the shape rather than an omission.**
|
|
47
|
+
*
|
|
48
|
+
* Every capability here takes the gizmo it is asking about, so the module needs nothing from the
|
|
49
|
+
* host to answer — a `Gizmo` reaches a script the way a `Terrain` and a `NavGraph` do, as a resource
|
|
50
|
+
* handed in through `uses`. So this is registered unconditionally, and a script that never receives
|
|
51
|
+
* one simply never calls it.
|
|
52
|
+
*/
|
|
53
|
+
export declare function editorImplementation(): Record<string, unknown>;
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import { defineCapability } from 'driftscript';
|
|
2
|
+
export const EDITOR_MODULE = 'drift/editor';
|
|
3
|
+
/**
|
|
4
|
+
* `drift/editor` — the transform tool, driven from a script that never ships.
|
|
5
|
+
*
|
|
6
|
+
* `Gizmo` in `@driftengine/core` answers what a ray is over and what a drag does to a transform;
|
|
7
|
+
* this is the surface a tool script drives it through.
|
|
8
|
+
*
|
|
9
|
+
* **`editor` is an `Effect` outside `DETERMINISTIC_EFFECTS`, and belongs there.** A gizmo is a view
|
|
10
|
+
* of a transform being edited by a person, so a `@deterministic` system has no business touching
|
|
11
|
+
* one. The annotation refusing it is the property `@editor` exists to pair with.
|
|
12
|
+
*
|
|
13
|
+
* **A name is unique across the module, not per type.** Two opaque types make that easy to forget:
|
|
14
|
+
* a module is one namespace and one implementation map, so `mode` cannot mean the gizmo's mode to a
|
|
15
|
+
* `Gizmo` and the session's to an `Editor`. They are `gizmoMode` and `mode`.
|
|
16
|
+
*
|
|
17
|
+
* **Two opaque types.** `Gizmo` is the transform tool; `Editor` is `@driftengine/editor`'s
|
|
18
|
+
* `EditorHost` — the selection, the tree and the play state. Both reach a script through `uses`,
|
|
19
|
+
* the way a `Terrain` and a `NavGraph` do, so this module needs nothing from the host and is
|
|
20
|
+
* registered unconditionally.
|
|
21
|
+
*
|
|
22
|
+
* ## Nothing here picks, and nothing edits a field by name
|
|
23
|
+
*
|
|
24
|
+
* A pick needs a ray, a ray needs a camera and a pointer, and both belong to the application. The
|
|
25
|
+
* host runs `hover`, `beginDrag` and `updateDrag`; a script reads what they found and says what
|
|
26
|
+
* kind of tool it is. That is what an editor script contains: a key that switches to rotation, a
|
|
27
|
+
* modifier that switches to local space, and a read of where the thing ended up.
|
|
28
|
+
*
|
|
29
|
+
* The same argument one level along: an inspector row is addressed by its index in a list the host
|
|
30
|
+
* built this frame, and a script wanting to write `Health.current` on a selection would be doing
|
|
31
|
+
* entity work `drift/ecs` already does properly, with a component handle and a checked field.
|
|
32
|
+
*
|
|
33
|
+
* ## Three capabilities where an argument would have done, deliberately
|
|
34
|
+
*
|
|
35
|
+
* `translateMode`, `rotateMode` and `scaleMode` rather than `setMode(gizmo, mode)`. The language has
|
|
36
|
+
* no enum, so the argument would be a string or a number, and an unrecognised one leaves exactly two
|
|
37
|
+
* options — change nothing, the silent no-op `host.ts` rules out, or throw inside a frame, which
|
|
38
|
+
* `AGENTS.md` rules out. Three names make the invalid call impossible to write, and the checker
|
|
39
|
+
* rather than a runtime branch is what refuses it. `space` is the same argument again.
|
|
40
|
+
*
|
|
41
|
+
* The *readers* are strings because a reader is total: there is no invalid value to answer.
|
|
42
|
+
*/
|
|
43
|
+
export const EDITOR_TYPES = [
|
|
44
|
+
{
|
|
45
|
+
module: EDITOR_MODULE,
|
|
46
|
+
name: 'Gizmo',
|
|
47
|
+
doc: 'A transform tool: what the pointer is over, and the transform a drag is editing.',
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
module: EDITOR_MODULE,
|
|
51
|
+
name: 'Editor',
|
|
52
|
+
doc: 'An editing session: what is selected, the tree of what exists, and whether the world is playing.',
|
|
53
|
+
},
|
|
54
|
+
];
|
|
55
|
+
const define = (name, params, returns, doc) => defineCapability({
|
|
56
|
+
module: EDITOR_MODULE,
|
|
57
|
+
name,
|
|
58
|
+
signature: `fn(${params.map((p) => `${p.name}: ${p.type}`).join(', ')}) -> ${returns}`,
|
|
59
|
+
params: [...params],
|
|
60
|
+
returns,
|
|
61
|
+
effects: ['editor'],
|
|
62
|
+
/*
|
|
63
|
+
* **False on every one of them, reads included.** `editor` is outside
|
|
64
|
+
* `DETERMINISTIC_EFFECTS`, and it should be: what a gizmo holds is a person's pointer part way
|
|
65
|
+
* through moving something, so a system that read it would produce a different simulation on
|
|
66
|
+
* replay. This is the effect `@editor` was designed to pair with.
|
|
67
|
+
*/
|
|
68
|
+
deterministic: false,
|
|
69
|
+
doc,
|
|
70
|
+
implementation: `${EDITOR_MODULE}.${name}`,
|
|
71
|
+
});
|
|
72
|
+
const OF = [{ name: 'gizmo', type: 'Gizmo' }];
|
|
73
|
+
const ON = [{ name: 'editor', type: 'Editor' }];
|
|
74
|
+
/*
|
|
75
|
+
* **`f32` rather than `float`, and the language is right to insist.** `float` is width-polymorphic
|
|
76
|
+
* and takes its width from a `float` parameter; none of these has one, so nothing would fix it.
|
|
77
|
+
* A transform is stored as `Float32Array`, so `f32` is what the number actually is rather than a
|
|
78
|
+
* width chosen to satisfy a checker. `bindings/terrain.ts` records the same finding.
|
|
79
|
+
*/
|
|
80
|
+
export const EDITOR_CAPABILITIES = [
|
|
81
|
+
define('translateMode', OF, 'void', 'Make this an arrow gizmo, which moves what it is on.'),
|
|
82
|
+
define('rotateMode', OF, 'void', 'Make this a ring gizmo, which turns what it is on.'),
|
|
83
|
+
define('scaleMode', OF, 'void', 'Make this a scale gizmo. Scale is always in the object’s own axes, whatever the space says, because a non-uniform scale along a world axis would shear it.'),
|
|
84
|
+
define('gizmoMode', OF, 'String', 'Which of the three the gizmo is: "translate", "rotate" or "scale".'),
|
|
85
|
+
define('worldSpace', OF, 'void', 'Point the handles along the world axes.'),
|
|
86
|
+
define('localSpace', OF, 'void', 'Point the handles along the object’s own axes.'),
|
|
87
|
+
define('space', OF, 'String', 'Which the handles are pointing along: "world" or "local".'),
|
|
88
|
+
define('size', OF, 'f32', 'How big the gizmo is in metres. The host keeps this the same on screen as the camera moves.'),
|
|
89
|
+
define('setSize', [...OF, { name: 'metres', type: 'f32' }], 'void', 'Set how big the gizmo is in metres. Non-positive draws nothing and picks nothing rather than failing.'),
|
|
90
|
+
/*
|
|
91
|
+
* **A name rather than a number, and total.** The handles are integer constants in TypeScript, and
|
|
92
|
+
* a script comparing against a magic number is a script that breaks silently the day a handle is
|
|
93
|
+
* added in the middle. `"none"` is a real answer rather than a missing one.
|
|
94
|
+
*/
|
|
95
|
+
define('hovered', OF, 'String', 'What the pointer is over, as a name: "none", or one of "translate.x", "translate.yz", "rotate.y", "scale.uniform" and their siblings.'),
|
|
96
|
+
define('dragging', OF, 'bool', 'Whether a drag is running.'),
|
|
97
|
+
define('dragAngle', OF, 'f32', 'How far a rotation drag has turned, in radians, signed and past a full turn if it went that far. Zero when the drag is not a rotation. This is what to snap or clamp; the rotation itself cannot tell a three-quarter turn from a quarter turn back.'),
|
|
98
|
+
define('positionX', OF, 'f32', 'Where the thing being edited is, along x.'),
|
|
99
|
+
define('positionY', OF, 'f32', 'The same, along y.'),
|
|
100
|
+
define('positionZ', OF, 'f32', 'The same, along z.'),
|
|
101
|
+
define('setPosition', [...OF, { name: 'x', type: 'f32' }, { name: 'y', type: 'f32' }, { name: 'z', type: 'f32' }], 'void', 'Put the gizmo, and the transform it is editing, somewhere. A drag that is running is not interrupted, so do this between drags.'),
|
|
102
|
+
define('scaleX', OF, 'f32', 'How much the thing being edited is scaled, along x.'),
|
|
103
|
+
define('scaleY', OF, 'f32', 'The same, along y.'),
|
|
104
|
+
define('scaleZ', OF, 'f32', 'The same, along z.'),
|
|
105
|
+
/*
|
|
106
|
+
* **Four reads of a quaternion rather than three Euler angles.** Euler angles would be friendlier
|
|
107
|
+
* to read and would be a different rotation depending on the order they are recomposed in, which
|
|
108
|
+
* is a wrong answer a script author cannot see. `drift/physics` reports a body's orientation the
|
|
109
|
+
* same way, so a script moving one onto the other passes four numbers through unchanged.
|
|
110
|
+
*/
|
|
111
|
+
define('rotationX', OF, 'f32', 'The orientation being edited, as a quaternion: x.'),
|
|
112
|
+
define('rotationY', OF, 'f32', 'The same: y.'),
|
|
113
|
+
define('rotationZ', OF, 'f32', 'The same: z.'),
|
|
114
|
+
define('rotationW', OF, 'f32', 'The same: w. Identity is (0, 0, 0, 1).'),
|
|
115
|
+
/*
|
|
116
|
+
* **The session, from `@driftengine/editor`.** Everything above drives one tool; everything below
|
|
117
|
+
* drives the editor around it — which is what an `@editor` script is actually for.
|
|
118
|
+
*/
|
|
119
|
+
define('mode', ON, 'String', 'What the editor is doing: "edit", "play" or "paused".'),
|
|
120
|
+
define('playing', ON, 'bool', 'Whether the world is advancing. False while paused and while editing.'),
|
|
121
|
+
define('play', ON, 'bool', 'Start playing, taking a snapshot of the world first so `stop` can put it back. Resuming from paused does not re-snapshot. False when no world is bound to snapshot.'),
|
|
122
|
+
define('pause', ON, 'void', 'Hold the world where it is. The frame keeps drawing.'),
|
|
123
|
+
define('step', ON, 'void', 'Advance exactly one fixed tick, then hold. Does nothing while editing, where there is nothing to step.'),
|
|
124
|
+
define('stop', ON, 'bool', 'Leave play and put the world back as it was. Every entity handle changes, so a selection is re-found by its place in the snapshot and one that play created is dropped. False when there was nothing to put back.'),
|
|
125
|
+
define('rowCount', ON, 'i32', 'How many rows the tree showed at its last rebuild.'),
|
|
126
|
+
define('rebuildTree', [...ON, { name: 'root', type: 'Node' }], 'void', 'Walk a node and everything under it into rows, honouring which branches are collapsed.'),
|
|
127
|
+
define('rowName', [...ON, { name: 'row', type: 'i32' }], 'String', 'What the node on a row is called, or "" for a row that does not exist.'),
|
|
128
|
+
define('rowDepth', [...ON, { name: 'row', type: 'i32' }], 'i32', 'How deep a row sits, with the root at zero. Minus one for a row that does not exist.'),
|
|
129
|
+
define('rowExpanded', [...ON, { name: 'row', type: 'i32' }], 'bool', 'Whether a row is showing its children. Meaningless for a row with none.'),
|
|
130
|
+
define('toggleRow', [...ON, { name: 'row', type: 'i32' }], 'void', 'Open a closed row or close an open one. Call `rebuildTree` afterwards to see it.'),
|
|
131
|
+
define('selectRow', [...ON, { name: 'row', type: 'i32' }], 'void', 'Select the node on a row, which points the gizmo at it. A row that does not exist selects nothing.'),
|
|
132
|
+
define('selectedRow', ON, 'i32', 'Which row is selected, or minus one.'),
|
|
133
|
+
define('fieldCount', ON, 'i32', 'How many fields the inspector is showing.'),
|
|
134
|
+
define('fieldLabel', [...ON, { name: 'field', type: 'i32' }], 'String', 'A field\u2019s name as it was declared, or "" for a field that does not exist.'),
|
|
135
|
+
define('fieldGroup', [...ON, { name: 'field', type: 'i32' }], 'String', 'What a field belongs to: a component\u2019s name, or "transform" for a node.'),
|
|
136
|
+
define('fieldKind', [...ON, { name: 'field', type: 'i32' }], 'String', 'How to show a field: "number", "integer", "boolean", "text", "entity" or "enum". Taken from the declared type, because a read cannot tell them apart.'),
|
|
137
|
+
define('fieldNumber', [...ON, { name: 'field', type: 'i32' }], 'f32', 'A field\u2019s value as a number. Zero for a field that is not one, and for one that does not exist.'),
|
|
138
|
+
define('setFieldNumber', [...ON, { name: 'field', type: 'i32' }, { name: 'value', type: 'f32' }], 'bool', 'Write a number into a field. False where the field does not exist or will not take one.'),
|
|
139
|
+
];
|
|
140
|
+
/**
|
|
141
|
+
* Handle constants as the names a script reads, in the order the constants run.
|
|
142
|
+
*
|
|
143
|
+
* A table rather than a `switch`, so adding a handle is one line here and the reader stays total.
|
|
144
|
+
*/
|
|
145
|
+
const HANDLE_NAMES = [
|
|
146
|
+
'none',
|
|
147
|
+
'translate.x',
|
|
148
|
+
'translate.y',
|
|
149
|
+
'translate.z',
|
|
150
|
+
'translate.yz',
|
|
151
|
+
'translate.zx',
|
|
152
|
+
'translate.xy',
|
|
153
|
+
'rotate.x',
|
|
154
|
+
'rotate.y',
|
|
155
|
+
'rotate.z',
|
|
156
|
+
'scale.x',
|
|
157
|
+
'scale.y',
|
|
158
|
+
'scale.z',
|
|
159
|
+
'scale.uniform',
|
|
160
|
+
];
|
|
161
|
+
/**
|
|
162
|
+
* **No services, and that is the shape rather than an omission.**
|
|
163
|
+
*
|
|
164
|
+
* Every capability here takes the gizmo it is asking about, so the module needs nothing from the
|
|
165
|
+
* host to answer — a `Gizmo` reaches a script the way a `Terrain` and a `NavGraph` do, as a resource
|
|
166
|
+
* handed in through `uses`. So this is registered unconditionally, and a script that never receives
|
|
167
|
+
* one simply never calls it.
|
|
168
|
+
*/
|
|
169
|
+
export function editorImplementation() {
|
|
170
|
+
return {
|
|
171
|
+
translateMode: (gizmo) => {
|
|
172
|
+
gizmo.mode = 'translate';
|
|
173
|
+
},
|
|
174
|
+
rotateMode: (gizmo) => {
|
|
175
|
+
gizmo.mode = 'rotate';
|
|
176
|
+
},
|
|
177
|
+
scaleMode: (gizmo) => {
|
|
178
|
+
gizmo.mode = 'scale';
|
|
179
|
+
},
|
|
180
|
+
gizmoMode: (gizmo) => gizmo.mode,
|
|
181
|
+
worldSpace: (gizmo) => {
|
|
182
|
+
gizmo.space = 'world';
|
|
183
|
+
},
|
|
184
|
+
localSpace: (gizmo) => {
|
|
185
|
+
gizmo.space = 'local';
|
|
186
|
+
},
|
|
187
|
+
space: (gizmo) => gizmo.space,
|
|
188
|
+
size: (gizmo) => gizmo.size,
|
|
189
|
+
setSize: (gizmo, metres) => {
|
|
190
|
+
gizmo.size = metres;
|
|
191
|
+
},
|
|
192
|
+
hovered: (gizmo) => HANDLE_NAMES[gizmo.hovered] ?? 'none',
|
|
193
|
+
dragging: (gizmo) => gizmo.dragging,
|
|
194
|
+
dragAngle: (gizmo) => gizmo.dragAngle,
|
|
195
|
+
positionX: (gizmo) => gizmo.position[0] ?? 0,
|
|
196
|
+
positionY: (gizmo) => gizmo.position[1] ?? 0,
|
|
197
|
+
positionZ: (gizmo) => gizmo.position[2] ?? 0,
|
|
198
|
+
setPosition: (gizmo, x, y, z) => {
|
|
199
|
+
gizmo.position[0] = x;
|
|
200
|
+
gizmo.position[1] = y;
|
|
201
|
+
gizmo.position[2] = z;
|
|
202
|
+
},
|
|
203
|
+
scaleX: (gizmo) => gizmo.scale[0] ?? 1,
|
|
204
|
+
scaleY: (gizmo) => gizmo.scale[1] ?? 1,
|
|
205
|
+
scaleZ: (gizmo) => gizmo.scale[2] ?? 1,
|
|
206
|
+
rotationX: (gizmo) => gizmo.rotation[0] ?? 0,
|
|
207
|
+
rotationY: (gizmo) => gizmo.rotation[1] ?? 0,
|
|
208
|
+
rotationZ: (gizmo) => gizmo.rotation[2] ?? 0,
|
|
209
|
+
rotationW: (gizmo) => gizmo.rotation[3] ?? 1,
|
|
210
|
+
mode: (editor) => editor.mode,
|
|
211
|
+
playing: (editor) => editor.mode === 'play',
|
|
212
|
+
play: (editor) => editor.play(),
|
|
213
|
+
pause: (editor) => {
|
|
214
|
+
editor.pause();
|
|
215
|
+
},
|
|
216
|
+
step: (editor) => {
|
|
217
|
+
editor.step();
|
|
218
|
+
},
|
|
219
|
+
stop: (editor) => editor.stop(),
|
|
220
|
+
rowCount: (editor) => editor.tree.rows.length,
|
|
221
|
+
rebuildTree: (editor, root) => {
|
|
222
|
+
editor.tree.rebuild(root);
|
|
223
|
+
},
|
|
224
|
+
/* Every row reader answers a defined value for a row that does not exist, because the language
|
|
225
|
+
has no optional to answer with and a frame loop is the worst place to learn that. */
|
|
226
|
+
rowName: (editor, row) => editor.tree.rows[row]?.name ?? '',
|
|
227
|
+
rowDepth: (editor, row) => editor.tree.rows[row]?.depth ?? -1,
|
|
228
|
+
rowExpanded: (editor, row) => editor.tree.rows[row]?.expanded ?? false,
|
|
229
|
+
toggleRow: (editor, row) => {
|
|
230
|
+
const at = editor.tree.rows[row];
|
|
231
|
+
if (at !== undefined)
|
|
232
|
+
editor.tree.toggle(at.node);
|
|
233
|
+
},
|
|
234
|
+
selectRow: (editor, row) => {
|
|
235
|
+
editor.select(editor.tree.rows[row]?.node ?? null);
|
|
236
|
+
},
|
|
237
|
+
selectedRow: (editor) => editor.tree.selected === null ? -1 : editor.tree.rowOf(editor.tree.selected),
|
|
238
|
+
fieldCount: (editor) => editor.inspector.fields.length,
|
|
239
|
+
fieldLabel: (editor, field) => editor.inspector.fields[field]?.label ?? '',
|
|
240
|
+
fieldGroup: (editor, field) => editor.inspector.fields[field]?.group ?? '',
|
|
241
|
+
fieldKind: (editor, field) => editor.inspector.fields[field]?.kind ?? '',
|
|
242
|
+
fieldNumber: (editor, field) => {
|
|
243
|
+
const value = editor.inspector.fields[field]?.value;
|
|
244
|
+
return typeof value === 'number' ? value : 0;
|
|
245
|
+
},
|
|
246
|
+
setFieldNumber: (editor, field, value) => editor.inspector.set(field, value),
|
|
247
|
+
};
|
|
248
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `drift/ecs` and `drift/prefab` — the entity model as DriftScript reaches it.
|
|
3
|
+
*
|
|
4
|
+
* These are the two surfaces the linker has refused by name since Track N shipped, and this file is
|
|
5
|
+
* what kills that refusal. Like every binding here, nothing in it is new engine code: a binding is a
|
|
6
|
+
* description and a lookup.
|
|
7
|
+
*
|
|
8
|
+
* ---
|
|
9
|
+
*
|
|
10
|
+
* ## A component is named by a string, and that is forced rather than chosen
|
|
11
|
+
*
|
|
12
|
+
* A script cannot hold a `ComponentType` — it is an object with a schema, and the language's types
|
|
13
|
+
* are numbers, strings, booleans and opaque handles. So a component is addressed by name and the
|
|
14
|
+
* host resolves it. **The cost is that a typo is a runtime refusal rather than a compile error**,
|
|
15
|
+
* and the fix is DS-5's `component` declaration, which is Track N's to build now that this exists.
|
|
16
|
+
* Refusing in words naming the component and listing what the world has is what the interim owes.
|
|
17
|
+
*
|
|
18
|
+
* ## There is no query here, and that is not an omission
|
|
19
|
+
*
|
|
20
|
+
* Iterating a query needs a loop form the language does not have for it — that is DS-5 and DS-6.
|
|
21
|
+
* What a script can do today is walk a component's dense array by index, which `while` expresses
|
|
22
|
+
* perfectly. **The order is insertion modified by swap-removal**, so an entity removed during a
|
|
23
|
+
* walk moves the last one into a position already passed; a script that removes while walking
|
|
24
|
+
* should walk backwards, and the doc on `at` says so.
|
|
25
|
+
*/
|
|
26
|
+
import { type ComponentType, type Prefab } from '@driftengine/entities';
|
|
27
|
+
import { type CapabilityDefinition, type OpaqueType } from 'driftscript';
|
|
28
|
+
export declare const ECS_MODULE = "drift/ecs";
|
|
29
|
+
export declare const PREFAB_MODULE = "drift/prefab";
|
|
30
|
+
export declare const ENTITY_TYPES: readonly OpaqueType[];
|
|
31
|
+
/**
|
|
32
|
+
* An entity is `Entity`, and it took the language until DriftScript 1.6.0 to have the word.
|
|
33
|
+
*
|
|
34
|
+
* A handle carries a 26-bit index and a 27-bit generation, which is the entire 53-bit budget a
|
|
35
|
+
* double holds. A `u32` would truncate the generation — and a truncated generation is a stale
|
|
36
|
+
* handle that compares equal to a live one, which is the exact failure the generation exists to
|
|
37
|
+
* prevent. So the width was never negotiable, and until the language could name it these all said
|
|
38
|
+
* `f64` with this paragraph explaining why.
|
|
39
|
+
*
|
|
40
|
+
* **`Entity` is a type of its own there rather than an alias**, which is what makes the change worth
|
|
41
|
+
* making: a handle is assignable *to* an `f64` and not *from* one, so a number a script computed
|
|
42
|
+
* cannot arrive where a handle is wanted, and `e.Health` is legal on a handle and on nothing else.
|
|
43
|
+
* The language's own note asked for this by name — a capability that hands back a handle as an
|
|
44
|
+
* `f64` and expects it to keep working as one is what it said would make the asymmetry wrong.
|
|
45
|
+
*
|
|
46
|
+
* **`read` still answers `f64`**, and that is not an oversight: it reads a component *field*, which
|
|
47
|
+
* is a number, and typing it as a handle would be the same mistake in the other direction.
|
|
48
|
+
*/
|
|
49
|
+
export declare const ECS_CAPABILITIES: readonly CapabilityDefinition[];
|
|
50
|
+
/**
|
|
51
|
+
* The four a query loop is built on.
|
|
52
|
+
*
|
|
53
|
+
* **Nobody writes these by hand.** They are what `for e in query<…>() { … }` compiles to, and a
|
|
54
|
+
* script author never names one — which is why their docs describe the shape of the generated code
|
|
55
|
+
* rather than how to call them. They are capabilities rather than a second bind hook so that
|
|
56
|
+
* generated code reaches them exactly as it reaches everything else, through `__bind($host)`.
|
|
57
|
+
*/
|
|
58
|
+
export declare const QUERY_CAPABILITIES: readonly CapabilityDefinition[];
|
|
59
|
+
export declare const PREFAB_CAPABILITIES: readonly CapabilityDefinition[];
|
|
60
|
+
/** What a host supplies so a name can become a component type or a prefab. */
|
|
61
|
+
export interface EntityServices {
|
|
62
|
+
/**
|
|
63
|
+
* Component types by the name a script writes.
|
|
64
|
+
*
|
|
65
|
+
* There is no world here: every capability takes the world as a parameter, because a consumer
|
|
66
|
+
* with two worlds — a simulation and a preview — would otherwise need two hosts.
|
|
67
|
+
*/
|
|
68
|
+
readonly components: ReadonlyMap<string, ComponentType>;
|
|
69
|
+
/** Prefabs by name. Absent when the host has none, which makes `drift/prefab` refuse in words. */
|
|
70
|
+
readonly prefabs?: ReadonlyMap<string, Prefab>;
|
|
71
|
+
}
|
|
72
|
+
export declare function entitiesImplementation(services: EntityServices): Record<string, unknown>;
|
|
73
|
+
export declare function prefabImplementation(services: EntityServices): Record<string, unknown>;
|