@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.
Files changed (61) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +9 -0
  3. package/README.md +81 -0
  4. package/dist/bindings/ai.d.ts +66 -0
  5. package/dist/bindings/ai.js +173 -0
  6. package/dist/bindings/animation.d.ts +13 -0
  7. package/dist/bindings/animation.js +177 -0
  8. package/dist/bindings/audio.d.ts +56 -0
  9. package/dist/bindings/audio.js +126 -0
  10. package/dist/bindings/behavior.d.ts +36 -0
  11. package/dist/bindings/behavior.js +101 -0
  12. package/dist/bindings/chemistry.d.ts +72 -0
  13. package/dist/bindings/chemistry.js +253 -0
  14. package/dist/bindings/core.d.ts +172 -0
  15. package/dist/bindings/core.js +814 -0
  16. package/dist/bindings/editor.d.ts +53 -0
  17. package/dist/bindings/editor.js +248 -0
  18. package/dist/bindings/entities.d.ts +73 -0
  19. package/dist/bindings/entities.js +339 -0
  20. package/dist/bindings/interface.d.ts +55 -0
  21. package/dist/bindings/interface.js +200 -0
  22. package/dist/bindings/navigation.d.ts +36 -0
  23. package/dist/bindings/navigation.js +255 -0
  24. package/dist/bindings/network.d.ts +51 -0
  25. package/dist/bindings/network.js +96 -0
  26. package/dist/bindings/render.d.ts +71 -0
  27. package/dist/bindings/render.js +143 -0
  28. package/dist/bindings/rollback.d.ts +41 -0
  29. package/dist/bindings/rollback.js +74 -0
  30. package/dist/bindings/sprites.d.ts +53 -0
  31. package/dist/bindings/sprites.js +186 -0
  32. package/dist/bindings/terrain.d.ts +54 -0
  33. package/dist/bindings/terrain.js +138 -0
  34. package/dist/bindings/xr.d.ts +56 -0
  35. package/dist/bindings/xr.js +155 -0
  36. package/dist/entityHost.d.ts +80 -0
  37. package/dist/entityHost.js +159 -0
  38. package/dist/host.d.ts +153 -0
  39. package/dist/host.js +233 -0
  40. package/dist/index.d.ts +21 -0
  41. package/dist/index.js +18 -0
  42. package/package.json +68 -0
  43. package/src/bindings/ai.ts +306 -0
  44. package/src/bindings/animation.ts +346 -0
  45. package/src/bindings/audio.ts +185 -0
  46. package/src/bindings/behavior.ts +181 -0
  47. package/src/bindings/chemistry.ts +548 -0
  48. package/src/bindings/core.ts +1604 -0
  49. package/src/bindings/editor.ts +393 -0
  50. package/src/bindings/entities.ts +550 -0
  51. package/src/bindings/interface.ts +300 -0
  52. package/src/bindings/navigation.ts +367 -0
  53. package/src/bindings/network.ts +178 -0
  54. package/src/bindings/render.ts +191 -0
  55. package/src/bindings/rollback.ts +110 -0
  56. package/src/bindings/sprites.ts +310 -0
  57. package/src/bindings/terrain.ts +180 -0
  58. package/src/bindings/xr.ts +272 -0
  59. package/src/entityHost.ts +219 -0
  60. package/src/host.ts +465 -0
  61. package/src/index.ts +41 -0
@@ -0,0 +1,548 @@
1
+ /**
2
+ * `drift/chemistry` — Track P's surface, and the thirteenth `drift/*` module.
3
+ *
4
+ * Like every binding here, nothing in it is new engine code: a binding is a **description and a
5
+ * lookup**. Where the surface needed a quantity `@driftengine/chemistry` did not have — a moisture
6
+ * content on a dry basis, a heat release rate, a char depth — that function was grown in the package
7
+ * that owns the subsystem and this describes it, per the rule in `host.ts`.
8
+ *
9
+ * ---
10
+ *
11
+ * ## Parcels are `i32`, and that is `drift/physics`'s convention followed rather than improved on
12
+ *
13
+ * A body is an `i32` there, a negative value means none, and a script author who has learned one
14
+ * surface has learned this one. A second convention for the same shape of thing is the kind of
15
+ * inconsistency nobody can defend a year later. `§20.2`.
16
+ *
17
+ * ## `chemistry.write` is inside the determinism boundary
18
+ *
19
+ * `capability.ts` wrote the rule that decides it and this track is the one entitled to apply it: a
20
+ * parcel's composition and enthalpy are simulated quantities in exactly the sense a component's
21
+ * fields are. They integrate on the fixed step and touch no clock. A rule that refused
22
+ * `chemistry.write` to a `@deterministic` function would refuse the canonical operation of the
23
+ * package.
24
+ *
25
+ * ## Two capabilities the design lists are **not** here, and neither is an oversight
26
+ *
27
+ * - **`wake`.** `§14`'s sleeping, budgets and LOD tiers were the design's CH-6 and were displaced
28
+ * when the build re-scoped that phase onto the temperature reading the ignition criteria had to
29
+ * read. Nothing sleeps, so a capability to wake it would be a no-op wearing a name — which is the
30
+ * rule against silent no-ops, arriving through a binding. It goes in with `§14`.
31
+ * - **`setAmbient`.** The ambient is the far field every live chunk was filled from and every vented
32
+ * kilogram was booked against. Changing it mid-run would leave the field holding air drawn from an
33
+ * atmosphere that no longer exists and the conservation ledger measuring against a moved baseline —
34
+ * which would make `elementTotals`, the single most valuable assertion in the track, meaningless.
35
+ * A consumer who wants a different atmosphere constructs the field with one. **Reversal:**
36
+ * re-baselining every live chunk and re-booking the ledger, which is a feature rather than a
37
+ * setter.
38
+ */
39
+ import {
40
+ AtmosphereField,
41
+ ChemistryWorld,
42
+ ParcelStore,
43
+ SpeciesRegistry,
44
+ SubstanceRegistry,
45
+ } from '@driftengine/chemistry';
46
+ import { type ComponentType, defineComponent } from '@driftengine/entities';
47
+ import { type CapabilityDefinition, type OpaqueType, defineCapability } from 'driftscript';
48
+
49
+ export const CHEMISTRY_MODULE = 'drift/chemistry';
50
+
51
+ const define = (
52
+ name: string,
53
+ params: readonly { name: string; type: string }[],
54
+ returns: string,
55
+ effects: CapabilityDefinition['effects'],
56
+ doc: string,
57
+ ): CapabilityDefinition =>
58
+ defineCapability({
59
+ module: CHEMISTRY_MODULE,
60
+ name,
61
+ signature: `fn(${params.map((p) => `${p.name}: ${p.type}`).join(', ')}) -> ${returns}`,
62
+ params: [...params],
63
+ returns,
64
+ effects,
65
+ /* Every capability in this module is deterministic — see the header. */
66
+ deterministic: true,
67
+ doc,
68
+ implementation: `${CHEMISTRY_MODULE}.${name}`,
69
+ });
70
+
71
+ /**
72
+ * The three opaque types `§20.2` names.
73
+ *
74
+ * `Chemistry` is the world as `PhysicsWorld` is one. A `Substance` and a `Species` are registrations,
75
+ * and a script holds them to pass back rather than to read into — a field on either would be a
76
+ * promise about a representation the engine could then never change.
77
+ */
78
+ export const CHEMISTRY_TYPES: readonly OpaqueType[] = [
79
+ {
80
+ module: CHEMISTRY_MODULE,
81
+ name: 'Chemistry',
82
+ doc: 'A chemistry world: parcels, the air, and the tick that runs them.',
83
+ },
84
+ {
85
+ module: CHEMISTRY_MODULE,
86
+ name: 'Substance',
87
+ doc: 'A registered material: what it is made of and what it can turn into.',
88
+ },
89
+ {
90
+ module: CHEMISTRY_MODULE,
91
+ name: 'Species',
92
+ doc: 'A registered chemical species, such as `O2` or `cellulose`.',
93
+ },
94
+ ];
95
+
96
+ const READ = ['chemistry.read'] as const;
97
+ const WRITE = ['chemistry.write'] as const;
98
+
99
+ const P = { name: 'chem', type: 'Chemistry' };
100
+ const PARCEL = { name: 'parcel', type: 'i32' };
101
+ const XYZ = [
102
+ { name: 'x', type: 'f32' },
103
+ { name: 'y', type: 'f32' },
104
+ { name: 'z', type: 'f32' },
105
+ ];
106
+
107
+ export const CHEMISTRY_CAPABILITIES: readonly CapabilityDefinition[] = [
108
+ /* Registration lookups: the two opaque handles enter a script here and nowhere else. */
109
+ define(
110
+ 'substance',
111
+ [P, { name: 'id', type: 'String' }],
112
+ 'Substance',
113
+ READ,
114
+ 'A registered material by id, such as `oak`. Refuses in words naming what the world has.',
115
+ ),
116
+ define(
117
+ 'species',
118
+ [P, { name: 'id', type: 'String' }],
119
+ 'Species',
120
+ READ,
121
+ 'A registered species by id, such as `O2`. Refuses in words if the world has no such species.',
122
+ ),
123
+
124
+ /* The read surface, §20.3. */
125
+ define(
126
+ 'parcelCount',
127
+ [P],
128
+ 'i32',
129
+ READ,
130
+ 'How many parcel handles have ever been issued, live or not.',
131
+ ),
132
+ define(
133
+ 'parcelAt',
134
+ [P, { name: 'index', type: 'i32' }],
135
+ 'i32',
136
+ READ,
137
+ 'The handle at an index, for iteration. Check `alive` before reading it.',
138
+ ),
139
+ define('alive', [P, PARCEL], 'bool', READ, 'Whether a parcel still exists.'),
140
+ define('substanceOf', [P, PARCEL], 'Substance', READ, 'What material a parcel is made of.'),
141
+ define('temperature', [P, PARCEL], 'f32', READ, 'K, averaged over the whole parcel.'),
142
+ define(
143
+ 'surfaceTemperature',
144
+ [P, PARCEL],
145
+ 'f32',
146
+ READ,
147
+ 'K at the outermost shell — the one that decides whether it catches.',
148
+ ),
149
+ define(
150
+ 'coreTemperature',
151
+ [P, PARCEL],
152
+ 'f32',
153
+ READ,
154
+ 'K at the innermost shell — the one that decides whether it is cooked.',
155
+ ),
156
+ define(
157
+ 'mass',
158
+ [P, PARCEL],
159
+ 'f32',
160
+ READ,
161
+ 'How much of it there is, in kilograms. Falls as it burns.',
162
+ ),
163
+ define(
164
+ 'speciesMass',
165
+ [P, PARCEL, { name: 'species', type: 'Species' }],
166
+ 'f32',
167
+ READ,
168
+ 'kg of one species held anywhere in this parcel.',
169
+ ),
170
+ define(
171
+ 'moisture',
172
+ [P, PARCEL],
173
+ 'f32',
174
+ READ,
175
+ 'kg of water per kg of dry matter — the dry basis, which is how moisture content is quoted. Under 0.25 will burn; over 0.35 will not.',
176
+ ),
177
+ define(
178
+ 'charFraction',
179
+ [P, PARCEL],
180
+ 'f32',
181
+ READ,
182
+ 'Share of the parcel that is now carbon, 0 to 1.',
183
+ ),
184
+ define('charDepth', [P, PARCEL], 'f32', READ, 'Metres of char measured inward from the surface.'),
185
+ define(
186
+ 'wetness',
187
+ [P, PARCEL],
188
+ 'f32',
189
+ READ,
190
+ 'kg of liquid water on the surface, per square metre of it.',
191
+ ),
192
+ define(
193
+ 'phase',
194
+ [P, PARCEL],
195
+ 'i32',
196
+ READ,
197
+ '0 solid, 1 liquid, 2 gas, 3 mixed. Mixed is a real answer: wet wood is a solid and a liquid at once.',
198
+ ),
199
+ define('burning', [P, PARCEL], 'bool', READ, 'Whether a flame stands over it.'),
200
+ define(
201
+ 'smouldering',
202
+ [P, PARCEL],
203
+ 'bool',
204
+ READ,
205
+ 'Whether it is glowing with no flame. A smoulder survives air a flame cannot, which is why smothering leaves embers.',
206
+ ),
207
+ define(
208
+ 'heatRelease',
209
+ [P, PARCEL],
210
+ 'f32',
211
+ READ,
212
+ "kW the reactions released last tick. A fire's size is its heat release rate, not its temperature. Negative while a surface is gasifying, which is correct.",
213
+ ),
214
+ define(
215
+ 'ignitionProgress',
216
+ [P, PARCEL],
217
+ 'f32',
218
+ READ,
219
+ 'How close to catching, 0 to 1: the least satisfied of the five criteria. A readout — nothing branches on it.',
220
+ ),
221
+ define(
222
+ 'structuralIntegrity',
223
+ [P, PARCEL],
224
+ 'f32',
225
+ READ,
226
+ 'How much is still load-bearing, 1 down to 0, as char eats the thickness. This package breaks nothing; it reports.',
227
+ ),
228
+ define('massFlux', [P, PARCEL], 'f32', READ, 'kg/(m²·s) of gas leaving the surface — the smoke.'),
229
+ define(
230
+ 'fuelFlux',
231
+ [P, PARCEL],
232
+ 'f32',
233
+ READ,
234
+ 'The combustible share of that, which is what ignition is measured against.',
235
+ ),
236
+ define('positionX', [P, PARCEL], 'f32', READ, 'Where the consumer said this parcel is.'),
237
+ define('positionY', [P, PARCEL], 'f32', READ, 'Where the consumer said this parcel is.'),
238
+ define('positionZ', [P, PARCEL], 'f32', READ, 'Where the consumer said this parcel is.'),
239
+
240
+ /* The air, sampled at a point. */
241
+ define('ambientTemperature', [P, ...XYZ], 'f32', READ, 'K of the air at a point.'),
242
+ define(
243
+ 'oxygenFraction',
244
+ [P, ...XYZ],
245
+ 'f32',
246
+ READ,
247
+ 'Volume fraction of oxygen. Air is 0.209; a flame stops near 0.14 and a smoulder near 0.05.',
248
+ ),
249
+ define(
250
+ 'humidity',
251
+ [P, ...XYZ],
252
+ 'f32',
253
+ READ,
254
+ 'Relative humidity, 0 to 1. Nothing dries in fog because the vapour pressure deficit is zero.',
255
+ ),
256
+ define(
257
+ 'pressure',
258
+ [P, ...XYZ],
259
+ 'f32',
260
+ READ,
261
+ 'Pascals. Rises where the air is heated and confined, which is what an explosion event reports on.',
262
+ ),
263
+ define(
264
+ 'concentration',
265
+ [P, { name: 'species', type: 'Species' }, ...XYZ],
266
+ 'f32',
267
+ READ,
268
+ 'Parts per million by volume. The reading a game acts on — carbon monoxide is the one that kills.',
269
+ ),
270
+ define(
271
+ 'smokeDensity',
272
+ [P, ...XYZ],
273
+ 'f32',
274
+ READ,
275
+ 'Extinction coefficient, 1/m: what to attenuate a light along a ray by.',
276
+ ),
277
+ define(
278
+ 'visibility',
279
+ [P, ...XYZ],
280
+ 'f32',
281
+ READ,
282
+ 'How far you can see, metres. Clamped at ten kilometres.',
283
+ ),
284
+
285
+ /* The write surface, §20.4. */
286
+ /*
287
+ * **`place`, not `spawn`, and `release`, not `emit`.** `§20.4` named both after words this
288
+ * language already reserves as statement heads — `spawn` starts a task and `emit` starts an event
289
+ * — so `chemistry.spawn(…)` does not parse at all. Renaming the capability is the narrow fix;
290
+ * making either a soft keyword would loosen the grammar everywhere for two names. The store's own
291
+ * method is still `spawn`, because TypeScript has no such collision.
292
+ */
293
+ define(
294
+ 'place',
295
+ [
296
+ P,
297
+ { name: 'substance', type: 'Substance' },
298
+ { name: 'kilograms', type: 'f32' },
299
+ { name: 'area', type: 'f32' },
300
+ ...XYZ,
301
+ ],
302
+ 'i32',
303
+ WRITE,
304
+ 'Put a new parcel in the world. `area` is the exposed surface in m², and it is required rather than defaulted: a characteristic depth is volume over area, so a parcel with no area has no depth.',
305
+ ),
306
+ define('destroy', [P, PARCEL], 'void', WRITE, 'Remove a parcel. Its handle is never reused.'),
307
+ define(
308
+ 'move',
309
+ [P, PARCEL, ...XYZ],
310
+ 'void',
311
+ WRITE,
312
+ 'Where it is. The consumer owns position; this package only reads it to work out what reaches it.',
313
+ ),
314
+ define(
315
+ 'addHeat',
316
+ [P, PARCEL, { name: 'joules', type: 'f32' }],
317
+ 'void',
318
+ WRITE,
319
+ 'Joules into the surface shell, which is what a flux from outside does.',
320
+ ),
321
+ define(
322
+ 'addHeatDeep',
323
+ [P, PARCEL, { name: 'shell', type: 'i32' }, { name: 'joules', type: 'f32' }],
324
+ 'void',
325
+ WRITE,
326
+ 'Joules into a named shell. The difference from `addHeat` is a microwave and an oven.',
327
+ ),
328
+ define(
329
+ 'setTemperature',
330
+ [P, PARCEL, { name: 'kelvin', type: 'f32' }],
331
+ 'void',
332
+ WRITE,
333
+ 'Set every shell to a temperature. **For authoring and tests, not for a tick** — it discards whatever enthalpy was there rather than accounting for it.',
334
+ ),
335
+ define(
336
+ 'wet',
337
+ [P, PARCEL, { name: 'kilograms', type: 'f32' }],
338
+ 'void',
339
+ WRITE,
340
+ 'Pour water on it: real liquid water into the surface shell, where boiling will find it. Refuses, naming the material, where that material holds no water.',
341
+ ),
342
+ define(
343
+ 'dry',
344
+ [P, PARCEL, { name: 'kilograms', type: 'f32' }],
345
+ 'void',
346
+ WRITE,
347
+ 'Take surface water away: a cloth, a hot dry pan, or wind.',
348
+ ),
349
+ define(
350
+ 'ignite',
351
+ [P, PARCEL],
352
+ 'void',
353
+ WRITE,
354
+ 'Supply a pilot for one tick. **This does not start a fire** — it satisfies one of five criteria, so holding a match to wet wood does nothing.',
355
+ ),
356
+ define(
357
+ 'douse',
358
+ [P, PARCEL],
359
+ 'void',
360
+ WRITE,
361
+ 'Remove the pilot and the flame. Embers survive, which is the point of it.',
362
+ ),
363
+ define(
364
+ 'addSpecies',
365
+ [P, PARCEL, { name: 'species', type: 'Species' }, { name: 'kilograms', type: 'f32' }],
366
+ 'void',
367
+ WRITE,
368
+ 'Salt the water, oil the pan, poison the well.',
369
+ ),
370
+ define(
371
+ 'mix',
372
+ [P, { name: 'from', type: 'i32' }, { name: 'into', type: 'i32' }],
373
+ 'void',
374
+ WRITE,
375
+ 'Pour one parcel into another; the first is consumed. Both must be the same material — a mixture of two is a third material, authored as one.',
376
+ ),
377
+ define(
378
+ 'release',
379
+ [P, { name: 'species', type: 'Species' }, { name: 'kilograms', type: 'f32' }, ...XYZ],
380
+ 'void',
381
+ WRITE,
382
+ 'A gas release into the air: a leak, a vent, an extinguisher.',
383
+ ),
384
+ define(
385
+ 'addAirHeat',
386
+ [P, { name: 'joules', type: 'f32' }, ...XYZ],
387
+ 'void',
388
+ WRITE,
389
+ 'Joules into a cell of air, which is what a heater does and what a fire does to the room.',
390
+ ),
391
+
392
+ /* The event buffer, §20.5, in the shape `drift/physics`'s contact buffer established. */
393
+ define('eventCount', [P], 'i32', READ, 'Events this tick. Cleared at the top of every one.'),
394
+ define(
395
+ 'eventKind',
396
+ [P, { name: 'index', type: 'i32' }],
397
+ 'i32',
398
+ READ,
399
+ '0 ignited, 1 extinguished, 2 smoulderStart, 3 smoulderEnd, 4 consumed, 5 charred, 6 structuralFail, 7 frozen, 8 melted, 9 boiled, 10 condensed, 11 sublimed, 12 corroded, 13 calcined, 14 dissolved, 15 denatured, 16 browned, 17 caramelised, 18 fermented, 19 decayed, 20 exploded.',
400
+ ),
401
+ define(
402
+ 'eventParcel',
403
+ [P, { name: 'index', type: 'i32' }],
404
+ 'i32',
405
+ READ,
406
+ 'Which parcel it happened to.',
407
+ ),
408
+ define(
409
+ 'eventSpecies',
410
+ [P, { name: 'index', type: 'i32' }],
411
+ 'i32',
412
+ READ,
413
+ 'Which species, or -1 where the kind does not name one.',
414
+ ),
415
+ define(
416
+ 'eventValue',
417
+ [P, { name: 'index', type: 'i32' }],
418
+ 'f32',
419
+ READ,
420
+ "The kind's own quantity.",
421
+ ),
422
+ ];
423
+
424
+ /**
425
+ * A parcel handle on an entity, and `§19` says why it is declared here.
426
+ *
427
+ * Track M's design is explicit that `@driftengine/entities` declares **no components** — it is the
428
+ * model, not a library of them. And `@driftengine/chemistry` imports no engine package at all, so it
429
+ * cannot declare one either. This binding layer is the one place that already depends on core,
430
+ * entities and now chemistry at once, which makes it the only honest home.
431
+ *
432
+ * `standoff` is not a chemistry quantity and is here because it is the one a consumer always ends up
433
+ * adding: how far this was placed from the fire, which is what `1/r²` is measured over.
434
+ */
435
+ export const CHEMISTRY_COMPONENT: ComponentType = defineComponent('Chemistry', {
436
+ parcel: 'i32',
437
+ standoff: 'f32',
438
+ });
439
+
440
+ /** What a consumer supplies to make `drift/chemistry` reachable. */
441
+ export interface ChemistryServices {
442
+ readonly world: ChemistryWorld;
443
+ readonly parcels: ParcelStore;
444
+ readonly air: AtmosphereField;
445
+ readonly substances: SubstanceRegistry;
446
+ readonly species: SpeciesRegistry;
447
+ }
448
+
449
+ export function chemistryImplementation(services: ChemistryServices): Record<string, unknown> {
450
+ const { world, parcels, air, substances, species } = services;
451
+
452
+ /**
453
+ * Resolve an id, or refuse naming what the world has.
454
+ *
455
+ * **Not `-1` and not a no-op**, for the reason `drift/ecs` gives about a component name: a script
456
+ * that misspells `oak` would otherwise spawn nothing and read zeroes, which is a behaviour that
457
+ * runs, reports success and is wrong.
458
+ */
459
+ const substanceOf = (id: string): number => {
460
+ const index = substances.indexOf(id);
461
+ if (index >= 0) return index;
462
+ throw new Error(
463
+ `no substance is registered as \`${id}\`. Install the library that carries it — ` +
464
+ '`@driftengine/chemistry/library/organic` has `oak`, `pine`, `paper`, `cotton`, `leather` ' +
465
+ 'and `straw` — or define one.',
466
+ );
467
+ };
468
+ const speciesOf = (id: string): number => {
469
+ const index = species.indexOf(id);
470
+ if (index >= 0) return index;
471
+ throw new Error(`no species is registered as \`${id}\`. The standard table has sixty-eight.`);
472
+ };
473
+
474
+ return {
475
+ substance: (_: unknown, id: string) => substanceOf(id),
476
+ species: (_: unknown, id: string) => speciesOf(id),
477
+
478
+ parcelCount: () => parcels.count,
479
+ parcelAt: (_: unknown, index: number) => index,
480
+ alive: (_: unknown, parcel: number) => parcels.alive(parcel),
481
+ substanceOf: (_: unknown, parcel: number) => parcels.substanceOf(parcel),
482
+ temperature: (_: unknown, parcel: number) => parcels.temperatureOf(parcel),
483
+ surfaceTemperature: (_: unknown, parcel: number) => parcels.surfaceTemperatureOf(parcel),
484
+ coreTemperature: (_: unknown, parcel: number) => parcels.coreTemperatureOf(parcel),
485
+ mass: (_: unknown, parcel: number) => parcels.massOf(parcel),
486
+ speciesMass: (_: unknown, parcel: number, s: number) => parcels.parcelSpeciesMass(parcel, s),
487
+ moisture: (_: unknown, parcel: number) => parcels.moistureOf(parcel),
488
+ charFraction: (_: unknown, parcel: number) => parcels.charFractionOf(parcel),
489
+ charDepth: (_: unknown, parcel: number) => parcels.charDepthOf(parcel),
490
+ wetness: (_: unknown, parcel: number) => parcels.wetnessOf(parcel),
491
+ phase: (_: unknown, parcel: number) => parcels.parcelPhaseOf(parcel),
492
+ burning: (_: unknown, parcel: number) => parcels.burning(parcel),
493
+ smouldering: (_: unknown, parcel: number) => parcels.smouldering(parcel),
494
+ /* Watts inside, kilowatts out: a fire is quoted in kW everywhere a person reads one. */
495
+ heatRelease: (_: unknown, parcel: number) => parcels.heatReleaseOf(parcel) / 1000,
496
+ ignitionProgress: (_: unknown, parcel: number) => world.ignitionProgressOf(parcel),
497
+ structuralIntegrity: (_: unknown, parcel: number) => parcels.structuralIntegrityOf(parcel),
498
+ massFlux: (_: unknown, parcel: number) => parcels.massFluxOf(parcel),
499
+ fuelFlux: (_: unknown, parcel: number) => parcels.fuelFluxOf(parcel),
500
+ positionX: (_: unknown, parcel: number) => parcels.positionX(parcel),
501
+ positionY: (_: unknown, parcel: number) => parcels.positionY(parcel),
502
+ positionZ: (_: unknown, parcel: number) => parcels.positionZ(parcel),
503
+
504
+ ambientTemperature: (_: unknown, x: number, y: number, z: number) => air.temperatureAt(x, y, z),
505
+ oxygenFraction: (_: unknown, x: number, y: number, z: number) => air.oxygenFractionAt(x, y, z),
506
+ humidity: (_: unknown, x: number, y: number, z: number) => air.humidityAt(x, y, z),
507
+ pressure: (_: unknown, x: number, y: number, z: number) => air.pressureAt(x, y, z),
508
+ concentration: (_: unknown, s: number, x: number, y: number, z: number) =>
509
+ air.concentrationAt(x, y, z, s),
510
+ smokeDensity: (_: unknown, x: number, y: number, z: number) => air.smokeDensityAt(x, y, z),
511
+ visibility: (_: unknown, x: number, y: number, z: number) => air.visibilityAt(x, y, z),
512
+
513
+ place: (
514
+ _: unknown,
515
+ substance: number,
516
+ kilograms: number,
517
+ area: number,
518
+ x: number,
519
+ y: number,
520
+ z: number,
521
+ ) => parcels.spawn({ substance, mass: kilograms, temperature: 293.15, area, x, y, z }),
522
+ destroy: (_: unknown, parcel: number) => parcels.destroy(parcel),
523
+ move: (_: unknown, parcel: number, x: number, y: number, z: number) =>
524
+ parcels.move(parcel, x, y, z),
525
+ addHeat: (_: unknown, parcel: number, joules: number) => parcels.addSurfaceHeat(parcel, joules),
526
+ addHeatDeep: (_: unknown, parcel: number, shell: number, joules: number) =>
527
+ parcels.addShellHeat(parcel, shell, joules),
528
+ setTemperature: (_: unknown, parcel: number, kelvin: number) =>
529
+ parcels.setTemperature(parcel, kelvin),
530
+ wet: (_: unknown, parcel: number, kilograms: number) => parcels.wet(parcel, kilograms),
531
+ dry: (_: unknown, parcel: number, kilograms: number) => parcels.dry(parcel, kilograms),
532
+ ignite: (_: unknown, parcel: number) => parcels.ignite(parcel),
533
+ douse: (_: unknown, parcel: number) => parcels.douse(parcel),
534
+ addSpecies: (_: unknown, parcel: number, s: number, kilograms: number) =>
535
+ parcels.addSpeciesMass(parcel, 0, s, kilograms),
536
+ mix: (_: unknown, from: number, into: number) => parcels.mix(from, into),
537
+ release: (_: unknown, s: number, kilograms: number, x: number, y: number, z: number) =>
538
+ air.addSpecies(x, y, z, s, kilograms),
539
+ addAirHeat: (_: unknown, joules: number, x: number, y: number, z: number) =>
540
+ air.addHeat(x, y, z, joules),
541
+
542
+ eventCount: () => world.events.count,
543
+ eventKind: (_: unknown, index: number) => world.events.kindOf(index),
544
+ eventParcel: (_: unknown, index: number) => world.events.parcelOf(index),
545
+ eventSpecies: (_: unknown, index: number) => world.events.speciesOf(index),
546
+ eventValue: (_: unknown, index: number) => world.events.valueOf(index),
547
+ };
548
+ }