@moku-labs/game 0.2.0 → 0.3.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/README.md +2 -0
- package/dist/assets.d.mts +181 -22
- package/dist/assets.mjs +1186 -160
- package/dist/{commands-C0BljsM5.d.mts → commands-DYO22lzi.d.mts} +22 -5
- package/dist/control.d.mts +3 -3
- package/dist/control.mjs +3 -3
- package/dist/{define-CjP_4H0G.d.mts → define-BKWjWgyQ.d.mts} +1 -1
- package/dist/dev-CQYJPmcD.mjs +1183 -0
- package/dist/flatten-BkXuscAV.mjs +100 -0
- package/dist/index.d.mts +85 -31
- package/dist/index.mjs +3208 -490
- package/dist/{session-DmAxY6Ll.mjs → input-CK4nxBO1.mjs} +41 -12
- package/dist/inspect.d.mts +32 -9
- package/dist/inspect.mjs +2 -2
- package/dist/jsx-dev-runtime.d.mts +1 -1
- package/dist/jsx-dev-runtime.mjs +1 -1
- package/dist/jsx-runtime.d.mts +1 -1
- package/dist/jsx-runtime.mjs +1 -1
- package/dist/{run-tlcn1w7y.mjs → run-BfuMLahc.mjs} +489 -213
- package/dist/{runtime-Bn5TXNxP.d.mts → runtime-Bhu442L8.d.mts} +1 -1
- package/dist/{runtime-DRlwxkIv.mjs → runtime-CBZgWgzx.mjs} +1 -57
- package/dist/{sources-BooL2MsX.mjs → sources-C1uGjz4l.mjs} +198 -21
- package/dist/testing.d.mts +5 -5
- package/dist/testing.mjs +5 -4
- package/dist/{types-CxZy7Pi4.d.mts → types-BnWH-75b.d.mts} +95 -21
- package/dist/{types-CqkrEY06.d.mts → types-CVUHPvMB.d.mts} +5 -434
- package/dist/{types-DmhTHRVW.d.mts → types-DtmFVCHf.d.mts} +1 -1
- package/dist/{types-BZ1OLf24.d.mts → types-Dy1xB68L.d.mts} +2318 -1169
- package/dist/{types-X9jXjka3.d.mts → types-VErbiEXy.d.mts} +867 -198
- package/dist/{visual-NPrDkvUk.mjs → visual-DUvvPPCt.mjs} +51 -1
- package/llms.txt +5 -5
- package/package.json +5 -5
- package/dist/component-DGg5DqKK.mjs +0 -44
- package/dist/dev-CcwFrwok.mjs +0 -413
- package/dist/types-C47fKNcn.d.mts +0 -695
|
@@ -10,7 +10,7 @@ import { AnyPluginInstance, PluginCtx } from "@moku-labs/core";
|
|
|
10
10
|
* createApp({ config: { orientation: "portrait", referenceSide: 1080, referenceLong: 2100 } });
|
|
11
11
|
* ```
|
|
12
12
|
*/
|
|
13
|
-
type Config$
|
|
13
|
+
type Config$8 = {
|
|
14
14
|
/** Screen orientation the game is designed for. */orientation: "portrait" | "landscape"; /** Short side of the reference resolution in pixels. */
|
|
15
15
|
referenceSide: number;
|
|
16
16
|
/**
|
|
@@ -29,7 +29,7 @@ type Config$7 = {
|
|
|
29
29
|
/**
|
|
30
30
|
* Global events. Empty: every event belongs to a plugin.
|
|
31
31
|
*/
|
|
32
|
-
type Events$
|
|
32
|
+
type Events$6 = Record<never, never>;
|
|
33
33
|
/**
|
|
34
34
|
* Public API type of a plugin instance, read from its phantom carrier.
|
|
35
35
|
* Mirrors the kernel's non-exported `ExtractPluginApi`.
|
|
@@ -58,8 +58,8 @@ type Require = <Plugin extends {
|
|
|
58
58
|
readonly events: Record<string, unknown>;
|
|
59
59
|
};
|
|
60
60
|
}>(plugin: Plugin) => ApiOf<Plugin>;
|
|
61
|
-
declare namespace types_d_exports$
|
|
62
|
-
export { Api$
|
|
61
|
+
declare namespace types_d_exports$7 {
|
|
62
|
+
export { Api$7 as Api, ClockCtx, ClockSource, Config$7 as Config, Elapsed, FakeClock, State$7 as State };
|
|
63
63
|
}
|
|
64
64
|
/**
|
|
65
65
|
* Source of time and timers. The system source is the default; a game passes its own when time
|
|
@@ -160,7 +160,7 @@ type Elapsed = {
|
|
|
160
160
|
* createApp({ pluginConfigs: { clock: { source: fakeClock(1000) } } });
|
|
161
161
|
* ```
|
|
162
162
|
*/
|
|
163
|
-
type Config$
|
|
163
|
+
type Config$7 = {
|
|
164
164
|
/**
|
|
165
165
|
* Time source. `undefined` means the system source: `Date.now` and `setTimeout`.
|
|
166
166
|
*/
|
|
@@ -169,7 +169,7 @@ type Config$6 = {
|
|
|
169
169
|
/**
|
|
170
170
|
* clock plugin state.
|
|
171
171
|
*/
|
|
172
|
-
type State$
|
|
172
|
+
type State$7 = {
|
|
173
173
|
source: ClockSource;
|
|
174
174
|
last: number;
|
|
175
175
|
dueAt: number | undefined;
|
|
@@ -190,7 +190,7 @@ type State$6 = {
|
|
|
190
190
|
* app.clock.scheduleAt(nextRefillAt(app.clock.now()));
|
|
191
191
|
* ```
|
|
192
192
|
*/
|
|
193
|
-
type Api$
|
|
193
|
+
type Api$7 = {
|
|
194
194
|
/**
|
|
195
195
|
* Reads trusted time. It never decreases, whatever the device clock does, so a player who sets
|
|
196
196
|
* the clock back gains nothing.
|
|
@@ -262,11 +262,11 @@ type Api$6 = {
|
|
|
262
262
|
/**
|
|
263
263
|
* Domain context of the clock plugin.
|
|
264
264
|
*/
|
|
265
|
-
type ClockCtx = PluginCtx<Config$
|
|
265
|
+
type ClockCtx = PluginCtx<Config$7, State$7> & {
|
|
266
266
|
readonly global: object;
|
|
267
267
|
};
|
|
268
|
-
declare namespace types_d_exports$
|
|
269
|
-
export { Api$
|
|
268
|
+
declare namespace types_d_exports$6 {
|
|
269
|
+
export { Api$6 as Api, Config$6 as Config, Events$5 as Events, LifecycleCtx, PauseReason, State$6 as State };
|
|
270
270
|
}
|
|
271
271
|
/**
|
|
272
272
|
* Why the game is paused. The named reasons are the engine's own; any other string is allowed.
|
|
@@ -289,7 +289,7 @@ type PauseReason = "background" | "devtools" | "system-dialog" | "device-lost" |
|
|
|
289
289
|
* };
|
|
290
290
|
* ```
|
|
291
291
|
*/
|
|
292
|
-
type Events$
|
|
292
|
+
type Events$5 = {
|
|
293
293
|
/**
|
|
294
294
|
* The pause stack changed. `reason` and `action` say what changed; `resumed` is true only on
|
|
295
295
|
* the change that emptied the stack.
|
|
@@ -310,11 +310,11 @@ type Events$4 = {
|
|
|
310
310
|
* const config: Config = {};
|
|
311
311
|
* ```
|
|
312
312
|
*/
|
|
313
|
-
type Config$
|
|
313
|
+
type Config$6 = Record<string, never>;
|
|
314
314
|
/**
|
|
315
315
|
* lifecycle plugin state: the pause reasons in insertion order, no duplicates.
|
|
316
316
|
*/
|
|
317
|
-
type State$
|
|
317
|
+
type State$6 = {
|
|
318
318
|
reasons: PauseReason[];
|
|
319
319
|
};
|
|
320
320
|
/**
|
|
@@ -331,7 +331,7 @@ type State$5 = {
|
|
|
331
331
|
* app.lifecycle.pop("ad"); // time resumes
|
|
332
332
|
* ```
|
|
333
333
|
*/
|
|
334
|
-
type Api$
|
|
334
|
+
type Api$6 = {
|
|
335
335
|
/**
|
|
336
336
|
* Pushes a reason why the game is paused. A reason already on the stack is ignored, so two
|
|
337
337
|
* plugins pausing for the same reason never pause twice.
|
|
@@ -390,7 +390,7 @@ type Api$5 = {
|
|
|
390
390
|
* `index.ts` writes `events` with an annotated `register` (core spec `14` row 8), so the own event
|
|
391
391
|
* reaches a factory passed by direct reference.
|
|
392
392
|
*/
|
|
393
|
-
type LifecycleCtx = PluginCtx<Config$
|
|
393
|
+
type LifecycleCtx = PluginCtx<Config$6, State$6, Events$5> & {
|
|
394
394
|
readonly require: Require;
|
|
395
395
|
};
|
|
396
396
|
//#endregion
|
|
@@ -960,8 +960,8 @@ declare class SaveUnreadableError extends Error {
|
|
|
960
960
|
*/
|
|
961
961
|
constructor(savedVersion: number, schemaVersion: number, cause: unknown);
|
|
962
962
|
}
|
|
963
|
-
declare namespace types_d_exports$
|
|
964
|
-
export { Api$
|
|
963
|
+
declare namespace types_d_exports$5 {
|
|
964
|
+
export { Api$5 as Api, CommitResult, Config$5 as Config, Events$4 as Events, Json, Migration, ModelCtx, Patch, PlayerStateProvider, ProviderCall, RngApi, RngState, RngStream, RngView, Root, SaveDoc, Snapshot, State$5 as State, StoreApi, Transaction };
|
|
965
965
|
}
|
|
966
966
|
/**
|
|
967
967
|
* Plain JSON value. Everything in state is JSON.
|
|
@@ -992,7 +992,7 @@ type Root = "player" | "session" | "rng";
|
|
|
992
992
|
* const payload: Events["model:committed"] = { roots: ["player", "rng"], cause: "edge" };
|
|
993
993
|
* ```
|
|
994
994
|
*/
|
|
995
|
-
type Events$
|
|
995
|
+
type Events$4 = {
|
|
996
996
|
/** Committed state changed. Projections reconcile from the snapshot. */"model:committed": {
|
|
997
997
|
roots: readonly Root[];
|
|
998
998
|
cause: "edge" | "rollback" | "restore" | "load";
|
|
@@ -1006,7 +1006,7 @@ type Events$3 = {
|
|
|
1006
1006
|
* createApp({ pluginConfigs: { model: { initialPlayer: { coins: 0 }, seed: 42 } } });
|
|
1007
1007
|
* ```
|
|
1008
1008
|
*/
|
|
1009
|
-
type Config$
|
|
1009
|
+
type Config$5 = {
|
|
1010
1010
|
/** The save seam. `undefined`: an in-memory provider, nothing is persisted. */playerProvider: PlayerStateProvider | undefined; /** Player state of a new player. Deep-cloned. */
|
|
1011
1011
|
initialPlayer: Json; /** Session state at every start. Deep-cloned. */
|
|
1012
1012
|
initialSession: Json; /** "from-save": a new player gets a random seed once; a number fixes it (tests). */
|
|
@@ -1018,7 +1018,7 @@ type Config$4 = {
|
|
|
1018
1018
|
* model plugin state: one branch per module.
|
|
1019
1019
|
*
|
|
1020
1020
|
*/
|
|
1021
|
-
type State$
|
|
1021
|
+
type State$5 = {
|
|
1022
1022
|
store: StoreState;
|
|
1023
1023
|
rng: Record<string, never>;
|
|
1024
1024
|
};
|
|
@@ -1031,7 +1031,7 @@ type State$4 = {
|
|
|
1031
1031
|
* app.model.rng.peek("chest:42"); // undefined: this chest was never opened
|
|
1032
1032
|
* ```
|
|
1033
1033
|
*/
|
|
1034
|
-
type Api$
|
|
1034
|
+
type Api$5 = {
|
|
1035
1035
|
store: StoreApi;
|
|
1036
1036
|
rng: RngApi;
|
|
1037
1037
|
};
|
|
@@ -1040,12 +1040,12 @@ type Api$4 = {
|
|
|
1040
1040
|
* an annotated `register` (core spec `14` row 8), so the own event reaches a factory passed by
|
|
1041
1041
|
* direct reference.
|
|
1042
1042
|
*/
|
|
1043
|
-
type ModelCtx = PluginCtx<Config$
|
|
1043
|
+
type ModelCtx = PluginCtx<Config$5, State$5, Events$4> & {
|
|
1044
1044
|
readonly global: object;
|
|
1045
1045
|
readonly log: Log.LogApi;
|
|
1046
1046
|
};
|
|
1047
|
-
declare namespace types_d_exports$
|
|
1048
|
-
export { Api$
|
|
1047
|
+
declare namespace types_d_exports$4 {
|
|
1048
|
+
export { Api$4 as Api, Config$4 as Config, FrameCallback, Phase, State$4 as State, Time, TimeCtx };
|
|
1049
1049
|
}
|
|
1050
1050
|
/**
|
|
1051
1051
|
* Frame phases, in call order.
|
|
@@ -1089,7 +1089,7 @@ type FrameCallback = (time: Readonly<Time>) => void;
|
|
|
1089
1089
|
* createApp({ pluginConfigs: { time: { maxFps: 30 } } });
|
|
1090
1090
|
* ```
|
|
1091
1091
|
*/
|
|
1092
|
-
type Config$
|
|
1092
|
+
type Config$4 = {
|
|
1093
1093
|
/**
|
|
1094
1094
|
* Frame rate cap. The default is 60. 120 is for WebViews that really deliver 120 Hz frames:
|
|
1095
1095
|
* Android today; WKWebView on iOS and macOS is capped at 60 by WebKit (bug 294338).
|
|
@@ -1112,7 +1112,7 @@ type Config$3 = {
|
|
|
1112
1112
|
/**
|
|
1113
1113
|
* time plugin state.
|
|
1114
1114
|
*/
|
|
1115
|
-
type State$
|
|
1115
|
+
type State$4 = {
|
|
1116
1116
|
callbacks: Record<Phase, FrameCallback[]>;
|
|
1117
1117
|
/**
|
|
1118
1118
|
* Scratch array of a frame: the six callback lists as they were when the frame started.
|
|
@@ -1155,7 +1155,7 @@ type State$3 = {
|
|
|
1155
1155
|
* off();
|
|
1156
1156
|
* ```
|
|
1157
1157
|
*/
|
|
1158
|
-
type Api$
|
|
1158
|
+
type Api$4 = {
|
|
1159
1159
|
/**
|
|
1160
1160
|
* Registers a frame callback. Callbacks of one phase run in registration order; a callback
|
|
1161
1161
|
* registered during a frame runs from the next frame on.
|
|
@@ -1279,7 +1279,7 @@ type Api$3 = {
|
|
|
1279
1279
|
/**
|
|
1280
1280
|
* Domain context of the time plugin.
|
|
1281
1281
|
*/
|
|
1282
|
-
type TimeCtx = PluginCtx<Config$
|
|
1282
|
+
type TimeCtx = PluginCtx<Config$4, State$4> & {
|
|
1283
1283
|
readonly global: object;
|
|
1284
1284
|
readonly log: Log.LogApi;
|
|
1285
1285
|
};
|
|
@@ -2723,8 +2723,8 @@ declare function to<const TargetName extends string, Payload, Output>(target: Ta
|
|
|
2723
2723
|
* ```
|
|
2724
2724
|
*/
|
|
2725
2725
|
declare function slot(name: string): SlotNode;
|
|
2726
|
-
declare namespace types_d_exports$
|
|
2727
|
-
export { Allow, Answer, AnyFlow, AnyNode, AnyNodeContext, Api$
|
|
2726
|
+
declare namespace types_d_exports$3 {
|
|
2727
|
+
export { Allow, Answer, AnyFlow, AnyNode, AnyNodeContext, Api$3 as Api, Bookmark, BundlesOf, CheckedEdges, Config$3 as Config, Contribution, DefineNode, Deps$3 as Deps, Descriptor, Edges, EmitterIdOf, EnterCallback, Events$3 as Events, Exit, FeatureDescription, FeatureEmitter, FeatureFilter, FeaturePlugin, FeaturesApi, FlowCtx, FlowDefinition, FlowGraph, FlowKit, FlowSpec, FlowState, FxApi, FxHandler, GameState, GameTypes, GateApi, GraphError, GraphNode, GuideOptions, Hint, InboxApi, JournalEntry, KernelSlice$3 as KernelSlice, LifecycleChanged, Mapped, NodeContext, NodeDefinition, NodeFx, NodeInfo, NodeSpec, OutcomeTags, Position, Result, RouteStep, RunnerApi, SceneIdOf, SlotNode, Stage, State$3 as State, StringTableLike, Target, TextStylesOf, TypeTag, WorldEvent };
|
|
2728
2728
|
}
|
|
2729
2729
|
/**
|
|
2730
2730
|
* flow plugin events.
|
|
@@ -2738,7 +2738,7 @@ declare namespace types_d_exports$2 {
|
|
|
2738
2738
|
* }); // the play button on "home" logs { node: "home", outcome: "play" }
|
|
2739
2739
|
* ```
|
|
2740
2740
|
*/
|
|
2741
|
-
type Events$
|
|
2741
|
+
type Events$3 = {
|
|
2742
2742
|
/** An edge was taken and its state committed. */"flow:edge": {
|
|
2743
2743
|
flow: string;
|
|
2744
2744
|
node: string;
|
|
@@ -2771,7 +2771,7 @@ type Events$2 = {
|
|
|
2771
2771
|
* createApp({ pluginConfigs: { flow: { mainFlow, safeNode: "home" } } });
|
|
2772
2772
|
* ```
|
|
2773
2773
|
*/
|
|
2774
|
-
type Config$
|
|
2774
|
+
type Config$3 = {
|
|
2775
2775
|
/** The top-level flow. Required before `run()`. */mainFlow: AnyFlow | undefined; /** Path of the checkpoint entered after a failed retry. `undefined`: the main flow's `start`. */
|
|
2776
2776
|
safeNode: string | undefined; /** Retries of a failed transition before going to `safeNode`. */
|
|
2777
2777
|
retries: number; /** How long `onStop` waits for the active node to settle after abort, in real milliseconds. */
|
|
@@ -2781,7 +2781,7 @@ type Config$2 = {
|
|
|
2781
2781
|
/**
|
|
2782
2782
|
* flow plugin state: one branch per module.
|
|
2783
2783
|
*/
|
|
2784
|
-
type State$
|
|
2784
|
+
type State$3 = {
|
|
2785
2785
|
features: FeaturesState;
|
|
2786
2786
|
fx: FxState;
|
|
2787
2787
|
gate: GateState;
|
|
@@ -2800,7 +2800,7 @@ type State$2 = {
|
|
|
2800
2800
|
* app.flow.inbox.post({ type: "purchased" }); // the shop SDK: waits for a node that lists it
|
|
2801
2801
|
* ```
|
|
2802
2802
|
*/
|
|
2803
|
-
type Api$
|
|
2803
|
+
type Api$3 = RunnerApi & {
|
|
2804
2804
|
gate: GateApi;
|
|
2805
2805
|
inbox: InboxApi;
|
|
2806
2806
|
fx: FxApi;
|
|
@@ -2810,17 +2810,17 @@ type Api$2 = RunnerApi & {
|
|
|
2810
2810
|
* The APIs flow requires from below (`time`, `model`, `clock`), resolved once in `onInit` and
|
|
2811
2811
|
* shared by the runner, gate, inbox and fx.
|
|
2812
2812
|
*/
|
|
2813
|
-
type Deps$
|
|
2814
|
-
time: Api$
|
|
2815
|
-
model: Api$
|
|
2816
|
-
clock: Api$
|
|
2813
|
+
type Deps$3 = {
|
|
2814
|
+
time: Api$4;
|
|
2815
|
+
model: Api$5;
|
|
2816
|
+
clock: Api$7;
|
|
2817
2817
|
};
|
|
2818
2818
|
/**
|
|
2819
2819
|
* What the kernel context offers before the deps are attached. `emit` is the kernel's: `index.ts`
|
|
2820
2820
|
* writes `events` with an annotated `register` (core spec `14` row 8), so the own events reach a
|
|
2821
2821
|
* factory passed by direct reference (`api`, `hooks`, `onInit`, `onStart`).
|
|
2822
2822
|
*/
|
|
2823
|
-
type KernelSlice$
|
|
2823
|
+
type KernelSlice$3 = PluginCtx<Config$3, State$3, Events$3> & {
|
|
2824
2824
|
readonly global: object;
|
|
2825
2825
|
readonly log: Log.LogApi;
|
|
2826
2826
|
readonly require: Require;
|
|
@@ -2828,13 +2828,13 @@ type KernelSlice$2 = PluginCtx<Config$2, State$2, Events$2> & {
|
|
|
2828
2828
|
/**
|
|
2829
2829
|
* Domain context shared by the modules.
|
|
2830
2830
|
*/
|
|
2831
|
-
type FlowCtx = KernelSlice$
|
|
2832
|
-
readonly deps: Deps$
|
|
2831
|
+
type FlowCtx = KernelSlice$3 & {
|
|
2832
|
+
readonly deps: Deps$3;
|
|
2833
2833
|
};
|
|
2834
2834
|
/**
|
|
2835
2835
|
* Payload of the one event flow listens to.
|
|
2836
2836
|
*/
|
|
2837
|
-
type LifecycleChanged = Events$
|
|
2837
|
+
type LifecycleChanged = Events$5["lifecycle:changed"];
|
|
2838
2838
|
/**
|
|
2839
2839
|
* The bound of the `strings` slot of `GameTypes`: the key-to-parameters table every game's
|
|
2840
2840
|
* generated `strings.ts` exports, whatever its keys. `i18n` reads the keys and the parameters;
|
|
@@ -2947,1608 +2947,1801 @@ type Position = {
|
|
|
2947
2947
|
waiting: readonly string[];
|
|
2948
2948
|
};
|
|
2949
2949
|
//#endregion
|
|
2950
|
-
//#region src/plugins/world/
|
|
2950
|
+
//#region src/plugins/world/projection/types.d.ts
|
|
2951
2951
|
/**
|
|
2952
|
-
*
|
|
2953
|
-
* higher generation, so an id is never silently reused.
|
|
2952
|
+
* How a layer orders the entities drawn in it.
|
|
2954
2953
|
*
|
|
2955
2954
|
* @example
|
|
2956
2955
|
* ```ts
|
|
2957
|
-
* const
|
|
2956
|
+
* const sort: LayerSort = "y";
|
|
2958
2957
|
* ```
|
|
2959
2958
|
*/
|
|
2960
|
-
type
|
|
2959
|
+
type LayerSort = "none" | "y" | "order";
|
|
2961
2960
|
/**
|
|
2962
|
-
*
|
|
2963
|
-
* entities and `snapshot()` can say where an entity came from.
|
|
2961
|
+
* One named layer of a scene. The order of the list is draw order.
|
|
2964
2962
|
*
|
|
2965
2963
|
* @example
|
|
2966
2964
|
* ```ts
|
|
2967
|
-
* const
|
|
2965
|
+
* const layer: LayerSpec = { name: "items", sort: "y" };
|
|
2968
2966
|
* ```
|
|
2969
2967
|
*/
|
|
2970
|
-
type
|
|
2971
|
-
kind: "projection" | "node" | "plugin";
|
|
2968
|
+
type LayerSpec = {
|
|
2972
2969
|
name: string;
|
|
2970
|
+
sort: LayerSort;
|
|
2973
2971
|
};
|
|
2974
2972
|
/**
|
|
2975
|
-
*
|
|
2976
|
-
*
|
|
2973
|
+
* Easing of a tween: one of the named curves the driver knows, or a function that gets the
|
|
2974
|
+
* normalised time and returns the eased fraction.
|
|
2977
2975
|
*
|
|
2978
2976
|
* @example
|
|
2979
2977
|
* ```ts
|
|
2980
|
-
* const
|
|
2981
|
-
* id: 1_048_576, index: 0, generation: 1, owner: { kind: "projection", name: "board.items" },
|
|
2982
|
-
* components: { Layer: { name: "items" } }, skipped: ["Display"]
|
|
2983
|
-
* };
|
|
2978
|
+
* const ease: Ease = "outBack";
|
|
2984
2979
|
* ```
|
|
2985
2980
|
*/
|
|
2986
|
-
type
|
|
2987
|
-
id: Entity;
|
|
2988
|
-
index: number;
|
|
2989
|
-
generation: number;
|
|
2990
|
-
owner: Owner;
|
|
2991
|
-
components: Record<string, Json>;
|
|
2992
|
-
skipped: string[];
|
|
2993
|
-
};
|
|
2981
|
+
type Ease = "linear" | "in" | "out" | "inOut" | "inCubic" | "outCubic" | "inOutCubic" | "inBack" | "outBack" | ((t: number) => number);
|
|
2994
2982
|
/**
|
|
2995
|
-
*
|
|
2996
|
-
*
|
|
2983
|
+
* What every motion returns: it can be finished, cancelled and asked whether it still runs.
|
|
2984
|
+
* V3 `anim` hands out the same shape.
|
|
2997
2985
|
*
|
|
2998
2986
|
* @example
|
|
2999
2987
|
* ```ts
|
|
3000
|
-
* const
|
|
2988
|
+
* const handle: MotionHandle = view.toRest(Transform);
|
|
2989
|
+
* handle.active(); // true until the last frame wrote the exact target
|
|
3001
2990
|
* ```
|
|
3002
2991
|
*/
|
|
3003
|
-
type
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
|
|
2992
|
+
type MotionHandle = {
|
|
2993
|
+
finish(): void;
|
|
2994
|
+
cancel(): void;
|
|
2995
|
+
active(): boolean;
|
|
3007
2996
|
};
|
|
3008
2997
|
/**
|
|
3009
|
-
*
|
|
2998
|
+
* What a motion hook returns. `void` means the hook wrote the component directly.
|
|
3010
2999
|
*
|
|
3011
3000
|
* @example
|
|
3012
3001
|
* ```ts
|
|
3013
|
-
* const
|
|
3002
|
+
* const motion: Motion = undefined; // the hook called view.set and is already done
|
|
3014
3003
|
* ```
|
|
3015
3004
|
*/
|
|
3016
|
-
type
|
|
3005
|
+
type Motion = MotionHandle | void;
|
|
3017
3006
|
/**
|
|
3018
|
-
*
|
|
3019
|
-
* `sync` runs in every mode.
|
|
3007
|
+
* Only the numeric fields of a component value: what a tween can interpolate.
|
|
3020
3008
|
*
|
|
3021
3009
|
* @example
|
|
3022
3010
|
* ```ts
|
|
3023
|
-
*
|
|
3011
|
+
* type Numbers = NumericFields<{ x: number; texture: string }>; // { x: number }
|
|
3024
3012
|
* ```
|
|
3025
3013
|
*/
|
|
3026
|
-
type
|
|
3014
|
+
type NumericFields<Value extends object> = { [Key in keyof Value as Value[Key] extends number ? Key : never]: Value[Key] };
|
|
3027
3015
|
/**
|
|
3028
|
-
* One
|
|
3016
|
+
* One keyframe segment of a track: the fraction of the track's `ms` it ends at (`0..1`), the
|
|
3017
|
+
* curve it eases with (the track's `ease` when left out) and the numeric fields it reaches. A
|
|
3018
|
+
* field it does not name holds where the segment before left it.
|
|
3029
3019
|
*
|
|
3030
3020
|
* @example
|
|
3031
3021
|
* ```ts
|
|
3032
|
-
*
|
|
3022
|
+
* // The swing of a popup board: past the rest pose at 42 % of the track, then home.
|
|
3023
|
+
* const segments: TrackSegment[] = [
|
|
3024
|
+
* { at: 0.42, ease: "out", to: { y: 974, rotation: 0.087 } },
|
|
3025
|
+
* { at: 1, ease: "inOut", to: { y: 960, rotation: 0 } }
|
|
3026
|
+
* ];
|
|
3033
3027
|
* ```
|
|
3034
3028
|
*/
|
|
3035
|
-
type
|
|
3036
|
-
|
|
3037
|
-
|
|
3029
|
+
type TrackSegment = {
|
|
3030
|
+
at: number;
|
|
3031
|
+
ease?: Ease;
|
|
3032
|
+
to: Record<string, number>;
|
|
3038
3033
|
};
|
|
3039
3034
|
/**
|
|
3040
|
-
*
|
|
3035
|
+
* Options of `ViewHandle.tween`. An additive tween adds its delta over the value the absolute
|
|
3036
|
+
* writer of the field holds, instead of owning the field. `segments` turns the tween into a
|
|
3037
|
+
* keyframe walk: the segments run one after another over `ms` on one clock, and `to` is where the
|
|
3038
|
+
* last one ends. `repeat` runs the walk again from its first segment when it ends: a number
|
|
3039
|
+
* counts the extra runs, `"forever"` never ends. The world passes it to the driver unchanged;
|
|
3040
|
+
* without a driver the target is written once.
|
|
3041
3041
|
*
|
|
3042
3042
|
* @example
|
|
3043
3043
|
* ```ts
|
|
3044
|
-
* const
|
|
3044
|
+
* const options: TweenOptions = { ms: 350, ease: "out", delayMs: 40 };
|
|
3045
|
+
* const walk: TweenOptions = { ms: 1000, segments: [{ at: 0.42, to: { y: 974 } }, { at: 1, to: { y: 960 } }] };
|
|
3046
|
+
* const sway: TweenOptions = { ms: 2400, additive: true, repeat: "forever", segments: [{ at: 0.5, to: { rotation: 0.05 } }, { at: 1, to: { rotation: 0 } }] };
|
|
3045
3047
|
* ```
|
|
3046
3048
|
*/
|
|
3047
|
-
type
|
|
3048
|
-
|
|
3049
|
-
|
|
3049
|
+
type TweenOptions = {
|
|
3050
|
+
ms: number;
|
|
3051
|
+
ease?: Ease;
|
|
3052
|
+
delayMs?: number;
|
|
3053
|
+
additive?: boolean;
|
|
3054
|
+
segments?: readonly TrackSegment[];
|
|
3055
|
+
repeat?: number | "forever";
|
|
3050
3056
|
};
|
|
3051
3057
|
/**
|
|
3052
|
-
*
|
|
3058
|
+
* Options of `ViewHandle.toRest`. Defaults: the plugin's `settleMs` and ease `"out"`. `delayMs`
|
|
3059
|
+
* lets a hook wait, for example for a flight to land, before the view comes home.
|
|
3053
3060
|
*
|
|
3054
3061
|
* @example
|
|
3055
3062
|
* ```ts
|
|
3056
|
-
* const
|
|
3063
|
+
* const options: RestOptions = { ms: 200, delayMs: 120 };
|
|
3057
3064
|
* ```
|
|
3058
3065
|
*/
|
|
3059
|
-
type
|
|
3060
|
-
|
|
3061
|
-
|
|
3066
|
+
type RestOptions = {
|
|
3067
|
+
ms?: number;
|
|
3068
|
+
ease?: Ease;
|
|
3069
|
+
delayMs?: number;
|
|
3062
3070
|
};
|
|
3063
3071
|
/**
|
|
3064
|
-
*
|
|
3065
|
-
* it
|
|
3072
|
+
* What the driver is told about one track: how long it runs, how it eases, how long it waits,
|
|
3073
|
+
* whether it adds to the field or owns it, and the keyframe segments it walks. A track with
|
|
3074
|
+
* segments walks them over `ms` on one clock and claims every field a segment names; its target
|
|
3075
|
+
* `to` is the last segment's target. A track with `repeat` starts its walk again when it ends
|
|
3076
|
+
* (a number counts the extra runs, `"forever"` never ends); the instant driver ignores it and
|
|
3077
|
+
* writes the end pose once.
|
|
3066
3078
|
*
|
|
3067
3079
|
* @example
|
|
3068
3080
|
* ```ts
|
|
3069
|
-
* const
|
|
3070
|
-
*
|
|
3081
|
+
* const options: TrackOptions = { ms: 250, ease: "outBack", delayMs: 0, additive: false };
|
|
3082
|
+
* const walk: TrackOptions = { ms: 1000, segments: [{ at: 0.42, to: { y: 974 } }, { at: 1, to: { y: 960 } }] };
|
|
3083
|
+
* const twice: TrackOptions = { ms: 400, ease: "inOut", delayMs: 0, additive: true, repeat: 1 };
|
|
3071
3084
|
* ```
|
|
3072
3085
|
*/
|
|
3073
|
-
type
|
|
3086
|
+
type TrackOptions = {
|
|
3087
|
+
ms: number;
|
|
3088
|
+
ease?: Ease;
|
|
3089
|
+
delayMs?: number;
|
|
3090
|
+
additive?: boolean;
|
|
3091
|
+
segments?: readonly TrackSegment[];
|
|
3092
|
+
repeat?: number | "forever";
|
|
3093
|
+
};
|
|
3074
3094
|
/**
|
|
3075
|
-
*
|
|
3076
|
-
*
|
|
3095
|
+
* The tween engine behind `ViewHandle.tween`, `toRest` and `all`. `anim` installs one with
|
|
3096
|
+
* `setDriver`; without it the world writes every target at once.
|
|
3097
|
+
*
|
|
3098
|
+
* A track reads its start values when its delay ends, writes the exact target on its last frame
|
|
3099
|
+
* and goes through `ecs.set`, so `changed()` sees it. Muted fields are never written, also not on
|
|
3100
|
+
* `finish()`.
|
|
3077
3101
|
*
|
|
3078
3102
|
* @example
|
|
3079
3103
|
* ```ts
|
|
3080
|
-
*
|
|
3081
|
-
*
|
|
3082
|
-
*
|
|
3083
|
-
*
|
|
3084
|
-
*
|
|
3085
|
-
* Item.owned; // []: a projection view writes every field
|
|
3104
|
+
* // `anim` hands the world its own tween core in `onStart`.
|
|
3105
|
+
* const driver: TweenDriver = { track: startTrack, cancelAll: cancelTracksOf };
|
|
3106
|
+
* const off = ctx.require(worldPlugin).projection.setDriver(driver);
|
|
3107
|
+
*
|
|
3108
|
+
* off(); // `anim` stopped: the world writes instantly again
|
|
3086
3109
|
* ```
|
|
3087
3110
|
*/
|
|
3088
|
-
type
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3111
|
+
type TweenDriver = {
|
|
3112
|
+
/**
|
|
3113
|
+
* Starts one track on one component of one entity.
|
|
3114
|
+
*
|
|
3115
|
+
* @param entity - The entity to animate.
|
|
3116
|
+
* @param component - The component to animate.
|
|
3117
|
+
* @param to - The numeric target fields.
|
|
3118
|
+
* @param options - Duration, easing, delay and the additive flag.
|
|
3119
|
+
* @param muted - The fields another writer owns, read at every write.
|
|
3120
|
+
* @returns The motion handle of the track.
|
|
3121
|
+
*/
|
|
3122
|
+
track(entity: Entity, component: AnyComponent, to: Record<string, number>, options: TrackOptions, muted: () => ReadonlySet<string>): MotionHandle;
|
|
3123
|
+
/**
|
|
3124
|
+
* Ends every track of one entity. Called when a view despawns and on every flush.
|
|
3125
|
+
*
|
|
3126
|
+
* @param entity - The entity whose tracks end.
|
|
3127
|
+
*/
|
|
3128
|
+
cancelAll(entity: Entity): void;
|
|
3094
3129
|
};
|
|
3095
3130
|
/**
|
|
3096
|
-
*
|
|
3131
|
+
* One node of an element description, as `ui` builds it. The world never imports `ui`: it knows
|
|
3132
|
+
* the shape structurally, carries it in `Tree` and hands it back unchanged.
|
|
3097
3133
|
*
|
|
3098
3134
|
* @example
|
|
3099
3135
|
* ```ts
|
|
3100
|
-
* const
|
|
3101
|
-
* Held(); // { type: Held, value: true }
|
|
3136
|
+
* const node: DescriptionNode = { type: "box", props: { gap: 8 }, children: [] };
|
|
3102
3137
|
* ```
|
|
3103
3138
|
*/
|
|
3104
|
-
type
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3139
|
+
type DescriptionNode = {
|
|
3140
|
+
type: string;
|
|
3141
|
+
key?: string;
|
|
3142
|
+
props: object;
|
|
3143
|
+
children: readonly unknown[];
|
|
3108
3144
|
};
|
|
3109
3145
|
/**
|
|
3110
|
-
* What
|
|
3146
|
+
* What one `view(item)` call returns: the components of the item, or one description node that
|
|
3147
|
+
* the world wraps as `Tree`.
|
|
3111
3148
|
*
|
|
3112
3149
|
* @example
|
|
3113
3150
|
* ```ts
|
|
3114
|
-
* const
|
|
3151
|
+
* const components: ViewOutput = [Level({ level: 2 })];
|
|
3115
3152
|
* ```
|
|
3116
3153
|
*/
|
|
3117
|
-
type
|
|
3118
|
-
readonly componentName: string;
|
|
3119
|
-
readonly kind: "component" | "tag";
|
|
3120
|
-
};
|
|
3154
|
+
type ViewOutput = readonly AnyComponentValue[] | DescriptionNode;
|
|
3121
3155
|
/**
|
|
3122
|
-
* What
|
|
3123
|
-
*
|
|
3124
|
-
* (`Narrowed`) is the same object with a narrower call, so it passes here unchanged.
|
|
3156
|
+
* What a motion hook is handed: the one view it animates. A view never holds a reference to
|
|
3157
|
+
* another view — `peer` answers with the other key's item, never with its entity.
|
|
3125
3158
|
*
|
|
3126
3159
|
* @example
|
|
3127
3160
|
* ```ts
|
|
3128
|
-
* const
|
|
3129
|
-
*
|
|
3161
|
+
* const enter = (view: ViewHandle<Item>, item: Item): Motion => {
|
|
3162
|
+
* view.set(Transform, { scale: 0 });
|
|
3163
|
+
* return view.toRest(Transform, { ms: 250 });
|
|
3164
|
+
* };
|
|
3130
3165
|
* ```
|
|
3131
3166
|
*/
|
|
3132
|
-
type
|
|
3133
|
-
readonly
|
|
3134
|
-
readonly
|
|
3135
|
-
|
|
3167
|
+
type ViewHandle<Item> = {
|
|
3168
|
+
readonly entity: Entity;
|
|
3169
|
+
readonly key: string;
|
|
3170
|
+
get<Value extends object>(component: ComponentType<Value>): Readonly<Value> | undefined;
|
|
3171
|
+
rest<Value extends object>(component: ComponentType<Value>): Readonly<Value> | undefined;
|
|
3172
|
+
set<Value extends object>(component: ComponentType<Value>, patch: Partial<Value>): void;
|
|
3173
|
+
tween<Value extends object>(component: ComponentType<Value>, to: Partial<NumericFields<Value>>, options: TweenOptions): MotionHandle;
|
|
3174
|
+
toRest<Value extends object>(component: ComponentType<Value>, options?: RestOptions): MotionHandle;
|
|
3175
|
+
all(handles: readonly Motion[]): MotionHandle;
|
|
3176
|
+
peer(key: string): Item | undefined;
|
|
3136
3177
|
};
|
|
3137
3178
|
/**
|
|
3138
|
-
*
|
|
3139
|
-
*
|
|
3179
|
+
* One `motion.change` hook, keyed by component name. `previous` and `next` are both passed, so a
|
|
3180
|
+
* rollback that lowers a level can pick another motion than a level-up.
|
|
3140
3181
|
*
|
|
3141
3182
|
* @example
|
|
3142
3183
|
* ```ts
|
|
3143
|
-
* const
|
|
3144
|
-
* Sprite({ texture: "board.cell" }).value.texture; // "board.cell"
|
|
3184
|
+
* const slide: ChangeHook<Item> = view => view.toRest(Transform, { ms: 350 });
|
|
3145
3185
|
* ```
|
|
3146
3186
|
*/
|
|
3147
|
-
type
|
|
3187
|
+
type ChangeHook<Item> = (view: ViewHandle<Item>, previous: Item, next: Item, hint?: Hint) => Motion;
|
|
3148
3188
|
/**
|
|
3149
|
-
*
|
|
3150
|
-
* of the defaults on first read.
|
|
3189
|
+
* The `change` table of a projection: one hook per component name.
|
|
3151
3190
|
*
|
|
3152
3191
|
* @example
|
|
3153
3192
|
* ```ts
|
|
3154
|
-
* const
|
|
3155
|
-
* Pointer.resourceName; // "Pointer"
|
|
3193
|
+
* const change: ChangeHooks<Item> = { Transform: view => view.toRest(Transform) };
|
|
3156
3194
|
* ```
|
|
3157
3195
|
*/
|
|
3158
|
-
type
|
|
3159
|
-
readonly
|
|
3160
|
-
readonly defaults: Readonly<Value>;
|
|
3196
|
+
type ChangeHooks<Item> = {
|
|
3197
|
+
readonly [component: string]: ChangeHook<Item>;
|
|
3161
3198
|
};
|
|
3162
3199
|
/**
|
|
3163
|
-
*
|
|
3200
|
+
* The motion hooks of a projection. Every one is optional; without a hook the component diff is
|
|
3201
|
+
* written directly. `loop` starts what moves a view for its whole life (an additive sway, a spin):
|
|
3202
|
+
* it is played wherever `enter` plays, next to it, and its motion is kept apart from the view's
|
|
3203
|
+
* other motions, so a change never cancels it; it ends when the view dies.
|
|
3164
3204
|
*
|
|
3165
3205
|
* @example
|
|
3166
3206
|
* ```ts
|
|
3167
|
-
* const
|
|
3168
|
-
*
|
|
3207
|
+
* const motion: ProjectionMotion<Item> = {
|
|
3208
|
+
* change: { Transform: view => view.toRest(Transform, { ms: 350 }) }
|
|
3209
|
+
* };
|
|
3210
|
+
* // An order card that sways forever once it entered.
|
|
3211
|
+
* const swaying: ProjectionMotion<Item> = {
|
|
3212
|
+
* loop: view =>
|
|
3213
|
+
* view.tween(Transform, { rotation: 0.05 }, { ms: 1200, additive: true, repeat: "forever" })
|
|
3214
|
+
* };
|
|
3169
3215
|
* ```
|
|
3170
3216
|
*/
|
|
3171
|
-
type
|
|
3172
|
-
|
|
3173
|
-
|
|
3217
|
+
type ProjectionMotion<Item> = {
|
|
3218
|
+
enter?(view: ViewHandle<Item>, item: Item, hint?: Hint): Motion;
|
|
3219
|
+
loop?(view: ViewHandle<Item>): Motion;
|
|
3220
|
+
exit?(view: ViewHandle<Item>, item: Item, hint?: Hint): Motion;
|
|
3221
|
+
readonly change?: ChangeHooks<Item>;
|
|
3222
|
+
settle?(view: ViewHandle<Item>, components: readonly string[]): Motion;
|
|
3174
3223
|
};
|
|
3175
3224
|
/**
|
|
3176
|
-
*
|
|
3225
|
+
* The motion hooks with the item type erased, as the world stores them.
|
|
3177
3226
|
*
|
|
3178
3227
|
* @example
|
|
3179
3228
|
* ```ts
|
|
3180
|
-
* const
|
|
3229
|
+
* const stored: AnyProjectionMotion = { change: {} };
|
|
3181
3230
|
* ```
|
|
3182
3231
|
*/
|
|
3183
|
-
type
|
|
3184
|
-
|
|
3185
|
-
|
|
3232
|
+
type AnyProjectionMotion = {
|
|
3233
|
+
enter?(view: ViewHandle<unknown>, item: unknown, hint?: Hint): Motion;
|
|
3234
|
+
loop?(view: ViewHandle<unknown>): Motion;
|
|
3235
|
+
exit?(view: ViewHandle<unknown>, item: unknown, hint?: Hint): Motion;
|
|
3236
|
+
readonly change?: ChangeHooks<never>;
|
|
3237
|
+
settle?(view: ViewHandle<unknown>, components: readonly string[]): Motion;
|
|
3186
3238
|
};
|
|
3187
3239
|
/**
|
|
3188
|
-
* One
|
|
3240
|
+
* One projection: keyed items of the model turned into entities. `Item` is inferred from the
|
|
3241
|
+
* return of `from`; `layer` and `lift` keep their literal types so a scene can check them.
|
|
3189
3242
|
*
|
|
3190
3243
|
* @example
|
|
3191
3244
|
* ```ts
|
|
3192
|
-
* const
|
|
3245
|
+
* const spec: ProjectionSpec<Item, "items", "lifted", Player, Session> = {
|
|
3246
|
+
* name: "board.items",
|
|
3247
|
+
* layer: "items",
|
|
3248
|
+
* lift: "lifted",
|
|
3249
|
+
* from: player => player.board.items,
|
|
3250
|
+
* key: item => item.id,
|
|
3251
|
+
* view: item => [Item({ level: item.level })]
|
|
3252
|
+
* };
|
|
3193
3253
|
* ```
|
|
3194
3254
|
*/
|
|
3195
|
-
type
|
|
3255
|
+
type ProjectionSpec<Item, LayerName extends string, LiftName extends string, Player, Session> = {
|
|
3256
|
+
readonly name: string;
|
|
3257
|
+
readonly layer: LayerName;
|
|
3258
|
+
readonly lift?: LiftName;
|
|
3259
|
+
from(player: Player, session: Session): readonly Item[] | Item; /** Omitted when `from` returns one plain object: the key is then the name of the projection. */
|
|
3260
|
+
key?(item: Item): string;
|
|
3261
|
+
view(item: Item): ViewOutput;
|
|
3262
|
+
readonly motion?: ProjectionMotion<Item>;
|
|
3263
|
+
};
|
|
3196
3264
|
/**
|
|
3197
|
-
*
|
|
3198
|
-
* plain component.
|
|
3265
|
+
* A projection spec with its item and layer types erased, as the world stores it.
|
|
3199
3266
|
*
|
|
3200
3267
|
* @example
|
|
3201
3268
|
* ```ts
|
|
3202
|
-
*
|
|
3269
|
+
* const stored: AnyProjectionSpec = {
|
|
3270
|
+
* name: "board.items",
|
|
3271
|
+
* layer: "items",
|
|
3272
|
+
* from: () => [],
|
|
3273
|
+
* key: () => "",
|
|
3274
|
+
* view: () => []
|
|
3275
|
+
* };
|
|
3203
3276
|
* ```
|
|
3204
3277
|
*/
|
|
3205
|
-
type
|
|
3278
|
+
type AnyProjectionSpec = {
|
|
3279
|
+
readonly name: string;
|
|
3280
|
+
readonly layer: string;
|
|
3281
|
+
readonly lift?: string;
|
|
3282
|
+
from(player: Json, session: Json): unknown;
|
|
3283
|
+
key?(item: unknown): string;
|
|
3284
|
+
view(item: unknown): ViewOutput;
|
|
3285
|
+
readonly motion?: AnyProjectionMotion;
|
|
3286
|
+
};
|
|
3206
3287
|
/**
|
|
3207
|
-
*
|
|
3288
|
+
* Why a reconcile runs. Merged over the frame; the cause table decides play or direct.
|
|
3208
3289
|
*
|
|
3209
3290
|
* @example
|
|
3210
3291
|
* ```ts
|
|
3211
|
-
*
|
|
3292
|
+
* const cause: Cause = "edge";
|
|
3212
3293
|
* ```
|
|
3213
3294
|
*/
|
|
3214
|
-
type
|
|
3295
|
+
type Cause = "edge" | "rollback" | "restore" | "load" | "mount" | "rerun";
|
|
3215
3296
|
/**
|
|
3216
|
-
* One
|
|
3297
|
+
* One live or exiting view: the entity of one key of one projection, its rest pose and the
|
|
3298
|
+
* motions that still run on it.
|
|
3217
3299
|
*
|
|
3218
3300
|
* @example
|
|
3219
3301
|
* ```ts
|
|
3220
|
-
*
|
|
3302
|
+
* const view: View = {
|
|
3303
|
+
* entity: 1_048_576,
|
|
3304
|
+
* projection: "board.items",
|
|
3305
|
+
* key: "i7",
|
|
3306
|
+
* item: { id: "i7" },
|
|
3307
|
+
* rest: new Map(),
|
|
3308
|
+
* handles: [],
|
|
3309
|
+
* lifted: false,
|
|
3310
|
+
* dropWhenStill: false,
|
|
3311
|
+
* exiting: false
|
|
3312
|
+
* };
|
|
3221
3313
|
* ```
|
|
3222
3314
|
*/
|
|
3223
|
-
type
|
|
3315
|
+
type View$1 = {
|
|
3316
|
+
entity: Entity;
|
|
3317
|
+
projection: string;
|
|
3318
|
+
key: string;
|
|
3319
|
+
item: unknown; /** The last output of `view(item)`, by component name: what the picture equals at rest. */
|
|
3320
|
+
rest: Map<string, AnyComponentValue>;
|
|
3321
|
+
handles: MotionHandle[];
|
|
3322
|
+
lifted: boolean; /** `lift(false)` is waiting for the last motion to end. */
|
|
3323
|
+
dropWhenStill: boolean;
|
|
3324
|
+
exiting: boolean;
|
|
3325
|
+
};
|
|
3224
3326
|
/**
|
|
3225
|
-
*
|
|
3226
|
-
*
|
|
3227
|
-
* writes the model.
|
|
3327
|
+
* One mounted projection: its owner, the live views by key, the despawn queue and the items of
|
|
3328
|
+
* the last reconcile.
|
|
3228
3329
|
*
|
|
3229
3330
|
* @example
|
|
3230
3331
|
* ```ts
|
|
3231
|
-
* const
|
|
3232
|
-
*
|
|
3233
|
-
*
|
|
3332
|
+
* const mounted: Mounted = {
|
|
3333
|
+
* owner: { kind: "projection", name: "board.items" },
|
|
3334
|
+
* live: new Map(),
|
|
3335
|
+
* queue: [],
|
|
3336
|
+
* items: new Map()
|
|
3234
3337
|
* };
|
|
3235
3338
|
* ```
|
|
3236
3339
|
*/
|
|
3237
|
-
type
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
|
|
3241
|
-
|
|
3340
|
+
type Mounted = {
|
|
3341
|
+
owner: Owner;
|
|
3342
|
+
live: Map<string, View$1>;
|
|
3343
|
+
queue: View$1[];
|
|
3344
|
+
items: Map<string, unknown>;
|
|
3242
3345
|
};
|
|
3243
3346
|
/**
|
|
3244
|
-
*
|
|
3347
|
+
* One track the projection started on the driver. The world keeps the component name and the
|
|
3348
|
+
* handle, so it knows which components are driven without knowing how the driver runs them.
|
|
3245
3349
|
*
|
|
3246
3350
|
* @example
|
|
3247
3351
|
* ```ts
|
|
3248
|
-
* const
|
|
3249
|
-
*
|
|
3250
|
-
* phase: "animate",
|
|
3251
|
-
* query: [Transform],
|
|
3252
|
-
* run: entities => {
|
|
3253
|
-
* for (const [, transform] of entities) transform.y; // read-only
|
|
3254
|
-
* }
|
|
3255
|
-
* };
|
|
3352
|
+
* const track: Track = { entity: 1_048_576, component: "Transform", handle: flight };
|
|
3353
|
+
* track.handle.active(); // true while the flight runs
|
|
3256
3354
|
* ```
|
|
3257
3355
|
*/
|
|
3258
|
-
type
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
run(entities: Iterable<QueryTuple<Terms>>, ctx: SystemContext): void;
|
|
3356
|
+
type Track = {
|
|
3357
|
+
entity: Entity;
|
|
3358
|
+
component: string;
|
|
3359
|
+
handle: MotionHandle;
|
|
3263
3360
|
};
|
|
3264
3361
|
/**
|
|
3265
|
-
*
|
|
3362
|
+
* The rest pose of one entity, by component name: the last output of `view(item)` for a view, or
|
|
3363
|
+
* what `setRest` recorded for an element another plugin owns.
|
|
3364
|
+
*/
|
|
3365
|
+
type RestPose = Map<string, AnyComponentValue>;
|
|
3366
|
+
/**
|
|
3367
|
+
* Where a keyed element lives: a plugin above registered it with `registerKey`, so `entityOf`
|
|
3368
|
+
* and `keyOf` answer for it next to the views.
|
|
3266
3369
|
*
|
|
3267
3370
|
* @example
|
|
3268
3371
|
* ```ts
|
|
3269
|
-
* const
|
|
3372
|
+
* const key: ProjectionKey = { projection: "hud", key: "coins" };
|
|
3270
3373
|
* ```
|
|
3271
3374
|
*/
|
|
3272
|
-
type
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
readonly query: readonly QueryTerm[];
|
|
3276
|
-
run(entities: Iterable<readonly unknown[]>, ctx: SystemContext): void;
|
|
3375
|
+
type ProjectionKey = {
|
|
3376
|
+
projection: string;
|
|
3377
|
+
key: string;
|
|
3277
3378
|
};
|
|
3278
3379
|
/**
|
|
3279
|
-
*
|
|
3280
|
-
*
|
|
3380
|
+
* What the next reconcile has to do: why it runs, which roots changed and whether `view` is
|
|
3381
|
+
* forced for every item.
|
|
3281
3382
|
*
|
|
3282
3383
|
* @example
|
|
3283
3384
|
* ```ts
|
|
3284
|
-
* const
|
|
3385
|
+
* const dirty: Dirty = { causes: ["edge"], roots: new Set(["player"]), force: false };
|
|
3285
3386
|
* ```
|
|
3286
3387
|
*/
|
|
3287
|
-
type
|
|
3288
|
-
|
|
3289
|
-
|
|
3388
|
+
type Dirty = {
|
|
3389
|
+
causes: Cause[];
|
|
3390
|
+
roots: Set<Root>;
|
|
3391
|
+
force: boolean;
|
|
3290
3392
|
};
|
|
3291
3393
|
/**
|
|
3292
|
-
*
|
|
3394
|
+
* projection module state.
|
|
3395
|
+
*/
|
|
3396
|
+
type ProjectionState = {
|
|
3397
|
+
specs: Map<string, AnyProjectionSpec>;
|
|
3398
|
+
layers: readonly LayerSpec[];
|
|
3399
|
+
mounted: Map<string, Mounted>;
|
|
3400
|
+
byEntity: Map<Entity, View$1>;
|
|
3401
|
+
dirty: Dirty | undefined;
|
|
3402
|
+
hints: Hint[]; /** The tracks the projection started, with the handle that says whether they still run. */
|
|
3403
|
+
tracks: Track[]; /** The tween engine `anim` installed, or `undefined`: then every target is written at once. */
|
|
3404
|
+
driver: TweenDriver | undefined; /** The rest pose of an entity that is not a view, recorded by its owner through `setRest`. */
|
|
3405
|
+
rests: Map<Entity, RestPose>; /** Projection name to key to the entity a plugin above registered under it. */
|
|
3406
|
+
keys: Map<string, Map<string, Entity>>; /** The reverse of `keys`, so `keyOf` answers for a registered element too. */
|
|
3407
|
+
keysByEntity: Map<Entity, ProjectionKey>; /** Entity to component name to the fields another writer owns. */
|
|
3408
|
+
mutes: Map<Entity, Map<string, Set<string>>>;
|
|
3409
|
+
offHints: Array<() => void>;
|
|
3410
|
+
};
|
|
3411
|
+
/**
|
|
3412
|
+
* projection module API, `app.world.projection`: the bridge from the model to entities.
|
|
3293
3413
|
*
|
|
3294
3414
|
* @example
|
|
3295
3415
|
* ```ts
|
|
3296
|
-
*
|
|
3416
|
+
* // A test mounts the board and asks which entity carries one model key.
|
|
3417
|
+
* app.world.projection.mount(["board.items"], { kind: "plugin", name: "test" });
|
|
3418
|
+
* app.world.projection.entityOf("board.items", "i7"); // 1048576
|
|
3297
3419
|
* ```
|
|
3298
3420
|
*/
|
|
3299
|
-
type
|
|
3300
|
-
kind: "attach";
|
|
3301
|
-
entity: Entity;
|
|
3302
|
-
components: readonly AnyComponentValue[];
|
|
3303
|
-
} | {
|
|
3304
|
-
kind: "despawn";
|
|
3305
|
-
entity: Entity;
|
|
3306
|
-
} | {
|
|
3307
|
-
kind: "despawnOwnedBy";
|
|
3308
|
-
owner: Owner;
|
|
3309
|
-
} | {
|
|
3310
|
-
kind: "add";
|
|
3311
|
-
entity: Entity;
|
|
3312
|
-
value: AnyComponentValue;
|
|
3313
|
-
} | {
|
|
3314
|
-
kind: "remove";
|
|
3315
|
-
entity: Entity;
|
|
3316
|
-
component: string;
|
|
3317
|
-
};
|
|
3318
|
-
/**
|
|
3319
|
-
* A structural listener registered with `onAdded` or `onRemoved`.
|
|
3320
|
-
*
|
|
3321
|
-
* @example
|
|
3322
|
-
* ```ts
|
|
3323
|
-
* const hook: StructuralHook = (entity, value) => void entity + String(value);
|
|
3324
|
-
* ```
|
|
3325
|
-
*/
|
|
3326
|
-
type StructuralHook = (entity: Entity, value: unknown) => void;
|
|
3327
|
-
/**
|
|
3328
|
-
* The frozen model snapshot of one frame, kept so four phases read it once.
|
|
3329
|
-
*
|
|
3330
|
-
* @example
|
|
3331
|
-
* ```ts
|
|
3332
|
-
* const cached: FrameSnapshot = { frame: 12, snapshot: { player: {}, session: {}, rng: rngState } };
|
|
3333
|
-
* ```
|
|
3334
|
-
*/
|
|
3335
|
-
type FrameSnapshot = {
|
|
3336
|
-
frame: number;
|
|
3337
|
-
snapshot: Snapshot;
|
|
3338
|
-
};
|
|
3339
|
-
/**
|
|
3340
|
-
* ecs module state.
|
|
3341
|
-
*/
|
|
3342
|
-
type EcsState = {
|
|
3343
|
-
generations: number[];
|
|
3344
|
-
free: number[];
|
|
3345
|
-
owners: Map<Entity, Owner>; /** Key is `"kind:name"` of the owner. */
|
|
3346
|
-
byOwner: Map<string, Set<Entity>>;
|
|
3347
|
-
types: Map<string, AnyComponentType>;
|
|
3348
|
-
stores: Map<string, Map<Entity, object | true>>;
|
|
3349
|
-
resources: Map<string, object>;
|
|
3350
|
-
systems: Record<WorldPhase, SystemEntry[]>;
|
|
3351
|
-
running: WorldPhase | undefined;
|
|
3352
|
-
commands: Command[];
|
|
3353
|
-
changed: Map<string, Set<Entity>>;
|
|
3354
|
-
added: Map<string, StructuralHook[]>;
|
|
3355
|
-
removed: Map<string, StructuralHook[]>;
|
|
3356
|
-
ownerLeft: Array<(owner: Owner) => void>;
|
|
3357
|
-
mode: WorldMode;
|
|
3358
|
-
offFrame: Array<() => void>;
|
|
3359
|
-
frameSnapshot: FrameSnapshot | undefined;
|
|
3360
|
-
};
|
|
3361
|
-
/**
|
|
3362
|
-
* ecs module API, `app.world.ecs`: the screen as data. Plain-object components in one store per
|
|
3363
|
-
* component type, generational entity ids, a mandatory owner, a command buffer applied between
|
|
3364
|
-
* phases, coarse change detection and fixed phases driven by `time`.
|
|
3365
|
-
*
|
|
3366
|
-
* @example
|
|
3367
|
-
* ```ts
|
|
3368
|
-
* // A test owns its entities and reads them back.
|
|
3369
|
-
* const entity = app.world.ecs.spawn({ kind: "plugin", name: "test" }, [Transform({ x: 10 })]);
|
|
3370
|
-
* app.world.ecs.get(entity, Transform); // { x: 10, y: 0, rotation: 0, scale: 1, pivot: { x: 0, y: 0 } }
|
|
3371
|
-
* ```
|
|
3372
|
-
*/
|
|
3373
|
-
type EcsApi = {
|
|
3421
|
+
type ProjectionApi = {
|
|
3374
3422
|
/**
|
|
3375
|
-
*
|
|
3376
|
-
* from the next frame on.
|
|
3423
|
+
* Stores a projection spec. Nothing is drawn until `mount`.
|
|
3377
3424
|
*
|
|
3378
|
-
* @param
|
|
3379
|
-
* @
|
|
3380
|
-
* @throws {Error} When a system of that name is already registered.
|
|
3425
|
+
* @param spec - The projection, built with `projection()`.
|
|
3426
|
+
* @throws {Error} When a projection of that name is already registered.
|
|
3381
3427
|
* @example
|
|
3382
3428
|
* ```ts
|
|
3383
|
-
* //
|
|
3384
|
-
*
|
|
3385
|
-
*
|
|
3386
|
-
*
|
|
3387
|
-
* off(); // the plugin stops
|
|
3429
|
+
* // A test registers one projection by hand instead of composing a feature.
|
|
3430
|
+
* app.world.projection.register(boardItems);
|
|
3431
|
+
* app.world.projection.entityOf("board.items", "i7"); // undefined: nothing is mounted yet
|
|
3388
3432
|
* ```
|
|
3389
3433
|
*/
|
|
3390
|
-
|
|
3434
|
+
register(spec: AnyProjectionSpec): void;
|
|
3391
3435
|
/**
|
|
3392
|
-
*
|
|
3393
|
-
*
|
|
3436
|
+
* Stores the layer list of the scene. The order of the list is draw order; every call stores a
|
|
3437
|
+
* new frozen array.
|
|
3394
3438
|
*
|
|
3395
|
-
* @param
|
|
3396
|
-
* @param components - Component and tag values the entity starts with.
|
|
3397
|
-
* @returns The new entity id.
|
|
3439
|
+
* @param list - The layers, in draw order.
|
|
3398
3440
|
* @example
|
|
3399
3441
|
* ```ts
|
|
3400
|
-
* // `
|
|
3401
|
-
* const
|
|
3402
|
-
*
|
|
3442
|
+
* // `scenes` switches to the board scene.
|
|
3443
|
+
* const world = ctx.require(worldPlugin);
|
|
3444
|
+
* world.projection.setLayers([
|
|
3445
|
+
* { name: "board", sort: "none" },
|
|
3446
|
+
* { name: "items", sort: "y" },
|
|
3447
|
+
* { name: "lifted", sort: "none" }
|
|
3403
3448
|
* ]);
|
|
3404
|
-
* app.world.ecs.has(entity, Transform); // true
|
|
3405
3449
|
* ```
|
|
3406
3450
|
*/
|
|
3407
|
-
|
|
3451
|
+
setLayers(list: ReadonlyArray<LayerSpec>): void;
|
|
3408
3452
|
/**
|
|
3409
|
-
*
|
|
3410
|
-
*
|
|
3453
|
+
* The current layer list, empty before the first `setLayers`. A new list is a new reference, so
|
|
3454
|
+
* a reader compares by identity.
|
|
3411
3455
|
*
|
|
3412
|
-
* @
|
|
3456
|
+
* @returns The layers, in draw order.
|
|
3413
3457
|
* @example
|
|
3414
3458
|
* ```ts
|
|
3415
|
-
* // `
|
|
3416
|
-
* app.world.
|
|
3417
|
-
*
|
|
3459
|
+
* // `renderer.sync` rebuilds its containers only when the scene really changed.
|
|
3460
|
+
* const current = app.world.projection.layers();
|
|
3461
|
+
* if (current !== lastLayers) rebuildContainers(current); // [{ name: "board", sort: "none" }, ...]
|
|
3418
3462
|
* ```
|
|
3419
3463
|
*/
|
|
3420
|
-
|
|
3464
|
+
layers(): ReadonlyArray<LayerSpec>;
|
|
3421
3465
|
/**
|
|
3422
|
-
*
|
|
3466
|
+
* Marks projections mounted under an owner and reconciles them at once, direct: a scene brings
|
|
3467
|
+
* its own transition. Nothing is mounted when the call throws.
|
|
3423
3468
|
*
|
|
3424
|
-
* @param
|
|
3469
|
+
* @param names - Names of the projections to mount.
|
|
3470
|
+
* @param owner - Who owns the entities of these projections.
|
|
3471
|
+
* @throws {Error} For an unknown name, or a `layer` or `lift` the scene does not declare.
|
|
3425
3472
|
* @example
|
|
3426
3473
|
* ```ts
|
|
3427
|
-
* //
|
|
3428
|
-
*
|
|
3429
|
-
*
|
|
3474
|
+
* // `scenes` mounts the projections of the board scene it just switched to.
|
|
3475
|
+
* const world = ctx.require(worldPlugin);
|
|
3476
|
+
* world.projection.mount(["board.cells", "board.items"], { kind: "plugin", name: "scenes" });
|
|
3477
|
+
* world.projection.entityOf("board.items", "i7"); // 1048576, the picture is already there
|
|
3430
3478
|
* ```
|
|
3431
3479
|
*/
|
|
3432
|
-
|
|
3480
|
+
mount(names: readonly string[], owner: Owner): void;
|
|
3433
3481
|
/**
|
|
3434
|
-
*
|
|
3482
|
+
* Unmounts projections: every motion of their views is finished, and the live views and the
|
|
3483
|
+
* despawn queue are despawned in the same call.
|
|
3435
3484
|
*
|
|
3436
|
-
* @param
|
|
3437
|
-
* @param component - The component type.
|
|
3438
|
-
* @returns The stored value, read-only, or `undefined`.
|
|
3485
|
+
* @param names - Names of the projections to unmount.
|
|
3439
3486
|
* @example
|
|
3440
3487
|
* ```ts
|
|
3441
|
-
* // `
|
|
3488
|
+
* // `scenes` leaves the board scene.
|
|
3442
3489
|
* const world = ctx.require(worldPlugin);
|
|
3443
|
-
* world.
|
|
3444
|
-
* world.
|
|
3490
|
+
* world.projection.unmount(["board.cells", "board.items"]);
|
|
3491
|
+
* world.projection.entityOf("board.items", "i7"); // undefined
|
|
3445
3492
|
* ```
|
|
3446
3493
|
*/
|
|
3447
|
-
|
|
3494
|
+
unmount(names: readonly string[]): void;
|
|
3448
3495
|
/**
|
|
3449
|
-
*
|
|
3450
|
-
*
|
|
3496
|
+
* A drop with no commit: cancels the view's motions and plays settle for every loose component.
|
|
3497
|
+
* In mode `paused` or `fast` the rest pose is written at once. No-op for an entity that is not
|
|
3498
|
+
* a live view.
|
|
3451
3499
|
*
|
|
3452
|
-
* @param entity - The entity
|
|
3453
|
-
* @param component - The component type.
|
|
3454
|
-
* @param patch - The fields to overwrite.
|
|
3455
|
-
* @throws {Error} When the entity is stale or does not carry the component.
|
|
3500
|
+
* @param entity - The entity of the view that was dropped.
|
|
3456
3501
|
* @example
|
|
3457
3502
|
* ```ts
|
|
3458
|
-
* // `input`
|
|
3503
|
+
* // `input` released the finger over nothing, so the gate refused the answer.
|
|
3459
3504
|
* const world = ctx.require(worldPlugin);
|
|
3460
|
-
* world.
|
|
3461
|
-
* world.ecs.get(held, Transform); // { x: 420, y: 810, rotation: 0, scale: 1, pivot: { x: 0, y: 0 } }
|
|
3505
|
+
* world.projection.settle(held); // the item glides back to its cell over settleMs
|
|
3462
3506
|
* ```
|
|
3463
3507
|
*/
|
|
3464
|
-
|
|
3508
|
+
settle(entity: Entity): void;
|
|
3465
3509
|
/**
|
|
3466
|
-
*
|
|
3467
|
-
* entity is marked changed; no `onAdded` fires.
|
|
3510
|
+
* Hands a set of fields to another writer. Tracks never write them, also not on `finish()`.
|
|
3468
3511
|
*
|
|
3469
|
-
* @param entity - The entity
|
|
3470
|
-
* @param
|
|
3512
|
+
* @param entity - The entity of the view.
|
|
3513
|
+
* @param component - The component whose fields are taken over.
|
|
3514
|
+
* @param fields - The field names the caller owns.
|
|
3515
|
+
* @returns The remover; safe after the entity left, and a no-op when called twice.
|
|
3471
3516
|
* @example
|
|
3472
3517
|
* ```ts
|
|
3473
|
-
* // `
|
|
3518
|
+
* // `input` owns the position of the held view while the finger drags it.
|
|
3474
3519
|
* const world = ctx.require(worldPlugin);
|
|
3475
|
-
* world.
|
|
3476
|
-
*
|
|
3520
|
+
* const release = world.projection.mute(held, Transform, ["x", "y"]);
|
|
3521
|
+
*
|
|
3522
|
+
* release(); // the finger let go
|
|
3477
3523
|
* ```
|
|
3478
3524
|
*/
|
|
3479
|
-
|
|
3525
|
+
mute(entity: Entity, component: ComponentType<object>, fields: readonly string[]): () => void;
|
|
3480
3526
|
/**
|
|
3481
|
-
*
|
|
3527
|
+
* Moves a view into or out of its projection's `lift` layer. `lift(entity, false)` on a view
|
|
3528
|
+
* that still moves takes effect when its last motion ends, so a settling view stays above the
|
|
3529
|
+
* board until it is home.
|
|
3482
3530
|
*
|
|
3483
|
-
* @param entity - The entity
|
|
3484
|
-
* @param
|
|
3531
|
+
* @param entity - The entity of the view.
|
|
3532
|
+
* @param on - True to lift, false to drop back.
|
|
3485
3533
|
* @example
|
|
3486
3534
|
* ```ts
|
|
3487
|
-
* // `
|
|
3535
|
+
* // `input` lifts the item the finger picked up so it draws above the board.
|
|
3488
3536
|
* const world = ctx.require(worldPlugin);
|
|
3489
|
-
* world.
|
|
3490
|
-
*
|
|
3537
|
+
* world.projection.lift(held, true);
|
|
3538
|
+
*
|
|
3539
|
+
* world.projection.lift(held, false); // on release: takes effect when the settle motion ends
|
|
3491
3540
|
* ```
|
|
3492
3541
|
*/
|
|
3493
|
-
|
|
3542
|
+
lift(entity: Entity, on: boolean): void;
|
|
3494
3543
|
/**
|
|
3495
|
-
*
|
|
3544
|
+
* The projection and key of an entity. Also answers for a view in the despawn queue and for an
|
|
3545
|
+
* element registered with `registerKey`.
|
|
3496
3546
|
*
|
|
3497
|
-
* @param entity - The entity to
|
|
3498
|
-
* @
|
|
3499
|
-
* @returns True when the entity carries it.
|
|
3547
|
+
* @param entity - The entity to ask about.
|
|
3548
|
+
* @returns The projection name and the key, or `undefined`.
|
|
3500
3549
|
* @example
|
|
3501
3550
|
* ```ts
|
|
3502
|
-
* // `
|
|
3503
|
-
*
|
|
3504
|
-
* world.
|
|
3551
|
+
* // `renderer.sync` labels its display objects for the inspector.
|
|
3552
|
+
* app.world.projection.keyOf(1_048_576); // { projection: "board.items", key: "i7" }
|
|
3553
|
+
* app.world.projection.keyOf(42); // undefined: not a view
|
|
3505
3554
|
* ```
|
|
3506
3555
|
*/
|
|
3507
|
-
|
|
3556
|
+
keyOf(entity: Entity): ProjectionKey | undefined;
|
|
3508
3557
|
/**
|
|
3509
|
-
*
|
|
3558
|
+
* The components a motion still drives on an entity: its running tracks, in start order, each
|
|
3559
|
+
* name once. An entity no motion drives answers `[]`: a resting view, an entity that is not a
|
|
3560
|
+
* view, a stale id. Every call hands out a new array.
|
|
3510
3561
|
*
|
|
3511
|
-
* @param entity - The entity to
|
|
3512
|
-
* @
|
|
3562
|
+
* @param entity - The entity to ask about.
|
|
3563
|
+
* @returns The component names, or `[]`.
|
|
3513
3564
|
* @example
|
|
3514
3565
|
* ```ts
|
|
3515
|
-
* //
|
|
3516
|
-
*
|
|
3517
|
-
*
|
|
3518
|
-
* world.
|
|
3566
|
+
* // The editor explains a coin in flight: its Transform still tweens, its Sprite rests.
|
|
3567
|
+
* app.world.projection.motionsOf(coin); // ["Transform"]
|
|
3568
|
+
* app.time.step(400);
|
|
3569
|
+
* app.world.projection.motionsOf(coin); // []
|
|
3519
3570
|
* ```
|
|
3520
3571
|
*/
|
|
3521
|
-
|
|
3572
|
+
motionsOf(entity: Entity): readonly string[];
|
|
3522
3573
|
/**
|
|
3523
|
-
*
|
|
3574
|
+
* The rest value of one component of an entity: what a view holds when nothing animates it, or
|
|
3575
|
+
* what `setRest` recorded for an element. Read-only and owner-free, so a plugin that owns no
|
|
3576
|
+
* entity can aim at one.
|
|
3524
3577
|
*
|
|
3525
|
-
* @param entity -
|
|
3526
|
-
* @param
|
|
3578
|
+
* @param entity - A projection view or a registered element.
|
|
3579
|
+
* @param component - The component whose rest value is asked.
|
|
3580
|
+
* @returns The rest value, or `undefined` when the entity has none recorded.
|
|
3527
3581
|
* @example
|
|
3528
3582
|
* ```ts
|
|
3529
|
-
* // `
|
|
3583
|
+
* // `anim` builds a coin flight toward the counter: `at(target)` is the counter's rest pose.
|
|
3530
3584
|
* const world = ctx.require(worldPlugin);
|
|
3531
|
-
* world.
|
|
3532
|
-
*
|
|
3585
|
+
* const counter = world.projection.entityOf("hud", "coins") ?? 0;
|
|
3586
|
+
*
|
|
3587
|
+
* world.projection.restOf(counter, Transform); // { x: 40, y: 120, rotation: 0, scale: 1, pivot: { x: 0, y: 0 } }
|
|
3533
3588
|
* ```
|
|
3534
3589
|
*/
|
|
3535
|
-
|
|
3590
|
+
restOf<Value extends object>(entity: Entity, component: ComponentType<Value>): Value | undefined;
|
|
3536
3591
|
/**
|
|
3537
|
-
*
|
|
3538
|
-
*
|
|
3592
|
+
* The entity of one key of one projection: a live view, never the despawn queue, or an element
|
|
3593
|
+
* a plugin above registered under that key.
|
|
3539
3594
|
*
|
|
3540
|
-
* @param
|
|
3541
|
-
* @
|
|
3595
|
+
* @param projection - Name of the projection.
|
|
3596
|
+
* @param key - The model key.
|
|
3597
|
+
* @returns The entity, or `undefined`.
|
|
3542
3598
|
* @example
|
|
3543
3599
|
* ```ts
|
|
3544
|
-
* // `
|
|
3545
|
-
*
|
|
3546
|
-
*
|
|
3547
|
-
*
|
|
3548
|
-
* }
|
|
3600
|
+
* // `input` resolves the target of a scripted drag.
|
|
3601
|
+
* const world = ctx.require(worldPlugin);
|
|
3602
|
+
* world.projection.entityOf("board.items", "i7"); // 1048576
|
|
3603
|
+
* world.projection.entityOf("board.items", "gone"); // undefined
|
|
3549
3604
|
* ```
|
|
3550
3605
|
*/
|
|
3551
|
-
|
|
3606
|
+
entityOf(projection: string, key: string): Entity | undefined;
|
|
3552
3607
|
/**
|
|
3553
|
-
*
|
|
3554
|
-
*
|
|
3608
|
+
* The live view entities of a mounted projection, in the order of its model keys. A view that
|
|
3609
|
+
* plays its exit is left out, and so is an element registered with `registerKey`. Every call
|
|
3610
|
+
* hands out a new array.
|
|
3555
3611
|
*
|
|
3556
|
-
* @param
|
|
3557
|
-
* @returns The
|
|
3612
|
+
* @param name - Name of the projection.
|
|
3613
|
+
* @returns The entities, or `[]` when the projection is not mounted.
|
|
3558
3614
|
* @example
|
|
3559
3615
|
* ```ts
|
|
3560
|
-
* // `
|
|
3616
|
+
* // `ui` hosts the board inside a slot and parents every board item to it on reconcile.
|
|
3561
3617
|
* const world = ctx.require(worldPlugin);
|
|
3562
|
-
*
|
|
3563
|
-
*
|
|
3618
|
+
* world.projection.entitiesOf("board.items"); // [1048580, 1048581]
|
|
3619
|
+
* world.projection.entitiesOf("shop.items"); // []: not mounted
|
|
3564
3620
|
* ```
|
|
3565
3621
|
*/
|
|
3566
|
-
|
|
3622
|
+
entitiesOf(name: string): readonly Entity[];
|
|
3567
3623
|
/**
|
|
3568
|
-
*
|
|
3569
|
-
*
|
|
3624
|
+
* Installs the tween engine every `ViewHandle.tween`, `toRest` and `all` then runs on. Without
|
|
3625
|
+
* one the world writes every target at once and hands back an inactive handle.
|
|
3570
3626
|
*
|
|
3571
|
-
* @param
|
|
3572
|
-
* @
|
|
3573
|
-
* @returns The remover; calling it twice is a no-op.
|
|
3627
|
+
* @param driver - The engine that runs the tracks.
|
|
3628
|
+
* @returns The remover; it puts the instant writes back.
|
|
3574
3629
|
* @example
|
|
3575
3630
|
* ```ts
|
|
3576
|
-
* // `
|
|
3631
|
+
* // `anim` installs its tween core when it starts and takes it back when it stops.
|
|
3577
3632
|
* const world = ctx.require(worldPlugin);
|
|
3578
|
-
*
|
|
3579
|
-
* objects.set(entity, makeSprite(sprite.texture));
|
|
3580
|
-
* });
|
|
3633
|
+
* ctx.state.removeDriver = world.projection.setDriver(createDriver(ctx, ctx.state));
|
|
3581
3634
|
*
|
|
3582
|
-
*
|
|
3635
|
+
* ctx.state.removeDriver(); // onStop: motions are instant again
|
|
3583
3636
|
* ```
|
|
3584
3637
|
*/
|
|
3585
|
-
|
|
3638
|
+
setDriver(driver: TweenDriver): () => void;
|
|
3586
3639
|
/**
|
|
3587
|
-
*
|
|
3588
|
-
*
|
|
3640
|
+
* A view handle for an entity that is not a projection view, so an element another plugin owns
|
|
3641
|
+
* can be animated the same way. `set`, `get`, `tween`, `toRest` and `all` work; `rest` answers
|
|
3642
|
+
* what `setRest` recorded; `peer` answers `undefined`.
|
|
3589
3643
|
*
|
|
3590
|
-
* @param
|
|
3591
|
-
* @param
|
|
3592
|
-
* @returns The
|
|
3644
|
+
* @param entity - The entity of the element.
|
|
3645
|
+
* @param owner - Who is asking; without it only the kind of the owner is checked.
|
|
3646
|
+
* @returns The handle, or `undefined` for a projection view and for a foreign entity.
|
|
3593
3647
|
* @example
|
|
3594
3648
|
* ```ts
|
|
3595
|
-
* // `ui`
|
|
3649
|
+
* // `ui` plays the `motion` prop of an element it owns.
|
|
3596
3650
|
* const world = ctx.require(worldPlugin);
|
|
3597
|
-
* const
|
|
3651
|
+
* const handle = world.projection.viewOf(button, { kind: "plugin", name: "ui" });
|
|
3598
3652
|
*
|
|
3599
|
-
*
|
|
3600
|
-
* off(); // ui stops
|
|
3653
|
+
* handle?.tween(Transform, { scale: 1.1 }, { ms: 120, ease: "outBack" });
|
|
3601
3654
|
* ```
|
|
3602
3655
|
*/
|
|
3603
|
-
|
|
3656
|
+
viewOf(entity: Entity, owner: Owner): ViewHandle<unknown> | undefined;
|
|
3604
3657
|
/**
|
|
3605
|
-
*
|
|
3658
|
+
* Records what one component of an element looks like at rest, so `toRest` knows where home is.
|
|
3606
3659
|
*
|
|
3607
|
-
* @param
|
|
3608
|
-
* @param
|
|
3609
|
-
* @
|
|
3660
|
+
* @param entity - The entity of the element.
|
|
3661
|
+
* @param component - The component whose rest value is recorded.
|
|
3662
|
+
* @param value - The value the element holds when nothing animates.
|
|
3610
3663
|
* @example
|
|
3611
3664
|
* ```ts
|
|
3612
|
-
* // `
|
|
3665
|
+
* // `ui` laid the coin counter out and tells the world where it belongs.
|
|
3613
3666
|
* const world = ctx.require(worldPlugin);
|
|
3614
|
-
*
|
|
3615
|
-
* objects.get(entity)?.destroy();
|
|
3616
|
-
* objects.delete(entity);
|
|
3617
|
-
* });
|
|
3667
|
+
* world.projection.setRest(counter, Transform, { x: 40, y: 120, rotation: 0, scale: 1, pivot: { x: 0, y: 0 } });
|
|
3618
3668
|
*
|
|
3619
|
-
*
|
|
3669
|
+
* world.projection.viewOf(counter, { kind: "plugin", name: "ui" })?.rest(Transform); // { x: 40, y: 120, rotation: 0, scale: 1, pivot: { x: 0, y: 0 } }
|
|
3620
3670
|
* ```
|
|
3621
3671
|
*/
|
|
3622
|
-
|
|
3672
|
+
setRest<Value extends object>(entity: Entity, component: ComponentType<Value>, value: Value): void;
|
|
3623
3673
|
/**
|
|
3624
|
-
*
|
|
3625
|
-
*
|
|
3674
|
+
* Gives an entity a projection and key of its own, so `entityOf` and `keyOf` answer for it next
|
|
3675
|
+
* to the live views. That is how an element becomes a target for `anim`, `input` and `guide`.
|
|
3626
3676
|
*
|
|
3627
|
-
* @param
|
|
3628
|
-
* @param
|
|
3629
|
-
* @
|
|
3677
|
+
* @param projection - Name the element is addressed under.
|
|
3678
|
+
* @param key - Key the element is addressed under.
|
|
3679
|
+
* @param entity - The entity of the element.
|
|
3680
|
+
* @returns The remover; it drops the key again.
|
|
3681
|
+
* @throws {Error} When a live view of that projection already holds the key.
|
|
3630
3682
|
* @example
|
|
3631
3683
|
* ```ts
|
|
3632
|
-
* // `ui`
|
|
3684
|
+
* // `ui` publishes the coin counter of the HUD.
|
|
3633
3685
|
* const world = ctx.require(worldPlugin);
|
|
3634
|
-
* const
|
|
3686
|
+
* const drop = world.projection.registerKey("hud", "coins", counter);
|
|
3635
3687
|
*
|
|
3636
|
-
* world.
|
|
3637
|
-
*
|
|
3688
|
+
* world.projection.entityOf("hud", "coins"); // the entity of the counter
|
|
3689
|
+
* drop(); // the element left the screen
|
|
3638
3690
|
* ```
|
|
3639
3691
|
*/
|
|
3640
|
-
|
|
3692
|
+
registerKey(projection: string, key: string, entity: Entity): () => void;
|
|
3641
3693
|
/**
|
|
3642
|
-
*
|
|
3643
|
-
*
|
|
3694
|
+
* Marks every mounted projection dirty and forces `view` for every item, so the next frame
|
|
3695
|
+
* rebuilds the picture directly.
|
|
3644
3696
|
*
|
|
3645
|
-
* @param component - The component type to ask about.
|
|
3646
|
-
* @returns The entities marked changed for it this frame.
|
|
3647
3697
|
* @example
|
|
3648
3698
|
* ```ts
|
|
3649
|
-
* // `
|
|
3650
|
-
*
|
|
3651
|
-
*
|
|
3652
|
-
* display.get(entity)?.position.set(transform?.x ?? 0, transform?.y ?? 0);
|
|
3653
|
-
* }
|
|
3699
|
+
* // `i18n` switched the locale: every label has to be projected again.
|
|
3700
|
+
* const world = ctx.require(worldPlugin);
|
|
3701
|
+
* world.projection.rerunAll(); // the next frame writes the new strings with no motion
|
|
3654
3702
|
* ```
|
|
3655
3703
|
*/
|
|
3656
|
-
|
|
3704
|
+
rerunAll(): void;
|
|
3705
|
+
};
|
|
3706
|
+
/**
|
|
3707
|
+
* projection methods the plugin root drives from the frame and the hooks. Not public.
|
|
3708
|
+
*/
|
|
3709
|
+
type ProjectionInternal = {
|
|
3657
3710
|
/**
|
|
3658
|
-
*
|
|
3711
|
+
* Records a commit. In fast mode it reconciles at once, direct.
|
|
3659
3712
|
*
|
|
3660
|
-
* @
|
|
3661
|
-
* @
|
|
3662
|
-
* ```ts
|
|
3663
|
-
* // A test checks that a fast walk froze the tweens.
|
|
3664
|
-
* app.flow.setMode("fast");
|
|
3665
|
-
* app.world.ecs.mode(); // "fast", even though setMode was never called on the world
|
|
3666
|
-
* ```
|
|
3713
|
+
* @param roots - The state roots the commit touched.
|
|
3714
|
+
* @param cause - Why the commit happened.
|
|
3667
3715
|
*/
|
|
3668
|
-
|
|
3716
|
+
markDirty(roots: readonly Root[], cause: Cause): void;
|
|
3669
3717
|
/**
|
|
3670
|
-
*
|
|
3671
|
-
*
|
|
3672
|
-
* @param mode - The new stored mode.
|
|
3673
|
-
* @example
|
|
3674
|
-
* ```ts
|
|
3675
|
-
* // A headless test wants the final picture without stepping frames.
|
|
3676
|
-
* app.world.ecs.setMode("fast");
|
|
3677
|
-
* app.world.ecs.mode(); // "fast"
|
|
3678
|
-
* ```
|
|
3718
|
+
* Reconciles when the world is dirty. Runs at the start of `time` phase `input`.
|
|
3679
3719
|
*/
|
|
3680
|
-
|
|
3720
|
+
reconcileIfDirty(): void;
|
|
3681
3721
|
/**
|
|
3682
|
-
*
|
|
3683
|
-
* wrote, read or queried yet is unknown.
|
|
3684
|
-
*
|
|
3685
|
-
* @param name - The storage name of the component.
|
|
3686
|
-
* @returns The type, or `undefined` when the world never met it.
|
|
3687
|
-
* @example
|
|
3688
|
-
* ```ts
|
|
3689
|
-
* // `text` resolves the component a label binds its number to.
|
|
3690
|
-
* const world = ctx.require(worldPlugin);
|
|
3691
|
-
* const type = world.ecs.typeOf("Coins"); // the Coins component, registered when it was written
|
|
3692
|
-
* const pose = type === undefined ? undefined : world.ecs.get(counter, type);
|
|
3693
|
-
*
|
|
3694
|
-
* pose?.amount; // 120
|
|
3695
|
-
* ```
|
|
3722
|
+
* Empties the hint buffer. Runs once per frame, reconcile or not.
|
|
3696
3723
|
*/
|
|
3697
|
-
|
|
3724
|
+
dropHints(): void;
|
|
3698
3725
|
/**
|
|
3699
|
-
*
|
|
3700
|
-
* index came back with a new generation. `projection` asks before it hands out a view handle for
|
|
3701
|
-
* an entity another plugin owns; `renderer` asks before it forgets the filters of an entity whose
|
|
3702
|
-
* visual left.
|
|
3726
|
+
* Buffers one released hint.
|
|
3703
3727
|
*
|
|
3704
|
-
* @param
|
|
3705
|
-
* @returns The owner, or `undefined` when the id is stale.
|
|
3706
|
-
* @example
|
|
3707
|
-
* ```ts
|
|
3708
|
-
* // renderer forgets the filter slots of an entity only after it despawned
|
|
3709
|
-
* const ecs = ctx.require(worldPlugin).ecs;
|
|
3710
|
-
* ecs.ownerOf(button); // { kind: "projection", name: "hud" }
|
|
3711
|
-
* ecs.despawn(button);
|
|
3712
|
-
* ecs.ownerOf(button); // undefined
|
|
3713
|
-
* ```
|
|
3728
|
+
* @param hint - The hint `flow.fx` released after a commit.
|
|
3714
3729
|
*/
|
|
3715
|
-
|
|
3730
|
+
pushHint(hint: Hint): void;
|
|
3716
3731
|
/**
|
|
3717
|
-
*
|
|
3718
|
-
*
|
|
3719
|
-
*
|
|
3720
|
-
* @returns The world as JSON: the mode, the entities and the resources.
|
|
3721
|
-
* @example
|
|
3722
|
-
* ```ts
|
|
3723
|
-
* // A test asserts what the board holds after the first reconcile.
|
|
3724
|
-
* app.world.ecs.snapshot();
|
|
3725
|
-
* // { mode: "live", entities: [{ id: 1048576, index: 0, generation: 1,
|
|
3726
|
-
* // owner: { kind: "projection", name: "board.items" },
|
|
3727
|
-
* // components: { Layer: { name: "items" } }, skipped: [] }], resources: {} }
|
|
3728
|
-
* ```
|
|
3732
|
+
* Sweeps the views whose motions ended: the despawn queue, the convergence check and the
|
|
3733
|
+
* pending drops. The tracks themselves are advanced by the driver.
|
|
3729
3734
|
*/
|
|
3730
|
-
|
|
3731
|
-
};
|
|
3732
|
-
/**
|
|
3733
|
-
* ecs methods the plugin root calls: frame phases, the owner listener, the clears. Not public.
|
|
3734
|
-
*/
|
|
3735
|
-
type EcsInternal = {
|
|
3735
|
+
sweep(): void;
|
|
3736
3736
|
/**
|
|
3737
|
-
*
|
|
3738
|
-
* that left.
|
|
3739
|
-
*
|
|
3740
|
-
* @param fn - Called with the owner whose entities were despawned.
|
|
3741
|
-
* @returns The remover.
|
|
3737
|
+
* Finishes every motion and flushes every despawn queue. Called when the mode turns `fast`.
|
|
3742
3738
|
*/
|
|
3743
|
-
|
|
3739
|
+
flushAll(): void;
|
|
3744
3740
|
/**
|
|
3745
|
-
*
|
|
3741
|
+
* Drops the views of a projection whose owner despawned its entities.
|
|
3746
3742
|
*
|
|
3747
|
-
* @param
|
|
3748
|
-
* @param time - The frame's `Time`.
|
|
3749
|
-
*/
|
|
3750
|
-
runPhase(phase: WorldPhase, time: Readonly<Time>): void;
|
|
3751
|
-
/**
|
|
3752
|
-
* Applies the queued structural commands in call order.
|
|
3753
|
-
*/
|
|
3754
|
-
flush(): void;
|
|
3755
|
-
/**
|
|
3756
|
-
* Clears every change set. Called in `time` phase `signals`.
|
|
3743
|
+
* @param owner - The owner that left.
|
|
3757
3744
|
*/
|
|
3758
|
-
|
|
3745
|
+
ownerLeft(owner: Owner): void;
|
|
3759
3746
|
/**
|
|
3760
|
-
* Drops every
|
|
3747
|
+
* Drops every view, track, mute and spec.
|
|
3761
3748
|
*/
|
|
3762
3749
|
clear(): void;
|
|
3763
3750
|
};
|
|
3764
3751
|
//#endregion
|
|
3765
|
-
//#region src/plugins/world/
|
|
3752
|
+
//#region src/plugins/world/ecs/types.d.ts
|
|
3766
3753
|
/**
|
|
3767
|
-
*
|
|
3754
|
+
* Entity id: `generation * 2 ** 20 + index`, a safe integer. A freed index comes back with a
|
|
3755
|
+
* higher generation, so an id is never silently reused.
|
|
3768
3756
|
*
|
|
3769
3757
|
* @example
|
|
3770
3758
|
* ```ts
|
|
3771
|
-
* const
|
|
3759
|
+
* const entity: Entity = 1_048_576; // generation 1, index 0
|
|
3772
3760
|
* ```
|
|
3773
3761
|
*/
|
|
3774
|
-
type
|
|
3762
|
+
type Entity = number;
|
|
3775
3763
|
/**
|
|
3776
|
-
*
|
|
3764
|
+
* Who owns an entity. Every `spawn` names one, so `despawnOwnedBy` can free exactly one owner's
|
|
3765
|
+
* entities and `snapshot()` can say where an entity came from.
|
|
3777
3766
|
*
|
|
3778
3767
|
* @example
|
|
3779
3768
|
* ```ts
|
|
3780
|
-
* const
|
|
3769
|
+
* const owner: Owner = { kind: "projection", name: "board.items" };
|
|
3781
3770
|
* ```
|
|
3782
3771
|
*/
|
|
3783
|
-
type
|
|
3772
|
+
type Owner = {
|
|
3773
|
+
kind: "projection" | "node" | "plugin";
|
|
3784
3774
|
name: string;
|
|
3785
|
-
sort: LayerSort;
|
|
3786
3775
|
};
|
|
3787
3776
|
/**
|
|
3788
|
-
*
|
|
3789
|
-
*
|
|
3777
|
+
* One entity as `snapshot()` reads it: its id and the two halves of the id, its owner, every
|
|
3778
|
+
* component value that is plain JSON, and the names of the ones that are not.
|
|
3790
3779
|
*
|
|
3791
3780
|
* @example
|
|
3792
3781
|
* ```ts
|
|
3793
|
-
* const
|
|
3794
|
-
*
|
|
3795
|
-
|
|
3796
|
-
|
|
3782
|
+
* const entity: EntitySnapshot = {
|
|
3783
|
+
* id: 1_048_576, index: 0, generation: 1, owner: { kind: "projection", name: "board.items" },
|
|
3784
|
+
* components: { Layer: { name: "items" } }, skipped: ["Display"]
|
|
3785
|
+
* };
|
|
3786
|
+
* ```
|
|
3787
|
+
*/
|
|
3788
|
+
type EntitySnapshot = {
|
|
3789
|
+
id: Entity;
|
|
3790
|
+
index: number;
|
|
3791
|
+
generation: number;
|
|
3792
|
+
owner: Owner;
|
|
3793
|
+
components: Record<string, Json>;
|
|
3794
|
+
skipped: string[];
|
|
3795
|
+
};
|
|
3797
3796
|
/**
|
|
3798
|
-
*
|
|
3799
|
-
*
|
|
3797
|
+
* The whole world as plain JSON: the effective mode, every entity sorted by index and every
|
|
3798
|
+
* resource that is JSON.
|
|
3800
3799
|
*
|
|
3801
3800
|
* @example
|
|
3802
3801
|
* ```ts
|
|
3803
|
-
* const
|
|
3804
|
-
* handle.active(); // true until the last frame wrote the exact target
|
|
3802
|
+
* const empty: WorldSnapshot = { mode: "live", entities: [], resources: {} };
|
|
3805
3803
|
* ```
|
|
3806
3804
|
*/
|
|
3807
|
-
type
|
|
3808
|
-
|
|
3809
|
-
|
|
3810
|
-
|
|
3805
|
+
type WorldSnapshot = {
|
|
3806
|
+
mode: WorldMode;
|
|
3807
|
+
entities: EntitySnapshot[];
|
|
3808
|
+
resources: Record<string, Json>;
|
|
3811
3809
|
};
|
|
3812
3810
|
/**
|
|
3813
|
-
*
|
|
3811
|
+
* The four phases systems run in, in frame order.
|
|
3814
3812
|
*
|
|
3815
3813
|
* @example
|
|
3816
3814
|
* ```ts
|
|
3817
|
-
* const
|
|
3815
|
+
* const phase: WorldPhase = "animate";
|
|
3818
3816
|
* ```
|
|
3819
3817
|
*/
|
|
3820
|
-
type
|
|
3818
|
+
type WorldPhase = "input" | "animate" | "layout" | "sync";
|
|
3821
3819
|
/**
|
|
3822
|
-
*
|
|
3820
|
+
* World mode. `"paused"` and `"fast"` skip the phases `input`, `animate` and `layout`; phase
|
|
3821
|
+
* `sync` runs in every mode.
|
|
3823
3822
|
*
|
|
3824
3823
|
* @example
|
|
3825
3824
|
* ```ts
|
|
3826
|
-
*
|
|
3825
|
+
* const mode: WorldMode = "fast";
|
|
3827
3826
|
* ```
|
|
3828
3827
|
*/
|
|
3829
|
-
type
|
|
3828
|
+
type WorldMode = "live" | "paused" | "fast";
|
|
3830
3829
|
/**
|
|
3831
|
-
* One
|
|
3832
|
-
* curve it eases with (the track's `ease` when left out) and the numeric fields it reaches. A
|
|
3833
|
-
* field it does not name holds where the segment before left it.
|
|
3830
|
+
* One component value ready to be written: the storage key and the frozen data.
|
|
3834
3831
|
*
|
|
3835
3832
|
* @example
|
|
3836
3833
|
* ```ts
|
|
3837
|
-
*
|
|
3838
|
-
* const segments: TrackSegment[] = [
|
|
3839
|
-
* { at: 0.42, ease: "out", to: { y: 974, rotation: 0.087 } },
|
|
3840
|
-
* { at: 1, ease: "inOut", to: { y: 960, rotation: 0 } }
|
|
3841
|
-
* ];
|
|
3834
|
+
* const value: ComponentValue<{ level: number }> = { type: Item, value: { level: 2 } };
|
|
3842
3835
|
* ```
|
|
3843
3836
|
*/
|
|
3844
|
-
type
|
|
3845
|
-
|
|
3846
|
-
|
|
3847
|
-
to: Record<string, number>;
|
|
3837
|
+
type ComponentValue<Value extends object> = {
|
|
3838
|
+
readonly type: ComponentType<Value>;
|
|
3839
|
+
readonly value: Readonly<Value>;
|
|
3848
3840
|
};
|
|
3849
3841
|
/**
|
|
3850
|
-
*
|
|
3851
|
-
* writer of the field holds, instead of owning the field. `segments` turns the tween into a
|
|
3852
|
-
* keyframe walk: the segments run one after another over `ms` on one clock, and `to` is where the
|
|
3853
|
-
* last one ends. `repeat` runs the walk again from its first segment when it ends: a number
|
|
3854
|
-
* counts the extra runs, `"forever"` never ends. The world passes it to the driver unchanged;
|
|
3855
|
-
* without a driver the target is written once.
|
|
3842
|
+
* A tag value. A tag carries no data and is stored as `true`.
|
|
3856
3843
|
*
|
|
3857
3844
|
* @example
|
|
3858
3845
|
* ```ts
|
|
3859
|
-
* const
|
|
3860
|
-
* const walk: TweenOptions = { ms: 1000, segments: [{ at: 0.42, to: { y: 974 } }, { at: 1, to: { y: 960 } }] };
|
|
3861
|
-
* const sway: TweenOptions = { ms: 2400, additive: true, repeat: "forever", segments: [{ at: 0.5, to: { rotation: 0.05 } }, { at: 1, to: { rotation: 0 } }] };
|
|
3846
|
+
* const held: TagValue = { type: Held, value: true };
|
|
3862
3847
|
* ```
|
|
3863
3848
|
*/
|
|
3864
|
-
type
|
|
3865
|
-
|
|
3866
|
-
|
|
3867
|
-
delayMs?: number;
|
|
3868
|
-
additive?: boolean;
|
|
3869
|
-
segments?: readonly TrackSegment[];
|
|
3870
|
-
repeat?: number | "forever";
|
|
3849
|
+
type TagValue = {
|
|
3850
|
+
readonly type: TagType;
|
|
3851
|
+
readonly value: true;
|
|
3871
3852
|
};
|
|
3872
3853
|
/**
|
|
3873
|
-
*
|
|
3874
|
-
* lets a hook wait, for example for a flight to land, before the view comes home.
|
|
3854
|
+
* Any component or tag value, as `spawn` and `view` return them.
|
|
3875
3855
|
*
|
|
3876
3856
|
* @example
|
|
3877
3857
|
* ```ts
|
|
3878
|
-
* const
|
|
3858
|
+
* const values: AnyComponentValue[] = [Held()];
|
|
3879
3859
|
* ```
|
|
3880
3860
|
*/
|
|
3881
|
-
type
|
|
3882
|
-
|
|
3883
|
-
|
|
3884
|
-
delayMs?: number;
|
|
3861
|
+
type AnyComponentValue = {
|
|
3862
|
+
readonly type: AnyComponentType;
|
|
3863
|
+
readonly value: object | true;
|
|
3885
3864
|
};
|
|
3886
3865
|
/**
|
|
3887
|
-
*
|
|
3888
|
-
*
|
|
3889
|
-
* segments walks them over `ms` on one clock and claims every field a segment names; its target
|
|
3890
|
-
* `to` is the last segment's target. A track with `repeat` starts its walk again when it ends
|
|
3891
|
-
* (a number counts the extra runs, `"forever"` never ends); the instant driver ignores it and
|
|
3892
|
-
* writes the end pose once.
|
|
3866
|
+
* A component type whose value shape is erased to a plain record: what the projection writes when
|
|
3867
|
+
* it only knows the component by name.
|
|
3893
3868
|
*
|
|
3894
3869
|
* @example
|
|
3895
3870
|
* ```ts
|
|
3896
|
-
* const
|
|
3897
|
-
*
|
|
3898
|
-
* const twice: TrackOptions = { ms: 400, ease: "inOut", delayMs: 0, additive: true, repeat: 1 };
|
|
3871
|
+
* const erased: AnyComponent = component("Transform", { x: 0, y: 0 }) as AnyComponent;
|
|
3872
|
+
* erased.componentName; // "Transform"
|
|
3899
3873
|
* ```
|
|
3900
3874
|
*/
|
|
3901
|
-
type
|
|
3902
|
-
ms: number;
|
|
3903
|
-
ease?: Ease;
|
|
3904
|
-
delayMs?: number;
|
|
3905
|
-
additive?: boolean;
|
|
3906
|
-
segments?: readonly TrackSegment[];
|
|
3907
|
-
repeat?: number | "forever";
|
|
3908
|
-
};
|
|
3875
|
+
type AnyComponent = ComponentType<Record<string, unknown>>;
|
|
3909
3876
|
/**
|
|
3910
|
-
*
|
|
3911
|
-
*
|
|
3912
|
-
*
|
|
3913
|
-
* A track reads its start values when its delay ends, writes the exact target on its last frame
|
|
3914
|
-
* and goes through `ecs.set`, so `changed()` sees it. Muted fields are never written, also not on
|
|
3915
|
-
* `finish()`.
|
|
3877
|
+
* A component type made by `component()`: callable to build a value, and carrying the storage
|
|
3878
|
+
* name plus the defaults that give the TypeScript type, the JSON shape and the inspector schema.
|
|
3916
3879
|
*
|
|
3917
3880
|
* @example
|
|
3918
3881
|
* ```ts
|
|
3919
|
-
*
|
|
3920
|
-
*
|
|
3921
|
-
*
|
|
3922
|
-
*
|
|
3923
|
-
*
|
|
3882
|
+
* const Item: ComponentType<{ kind: string; level: number }> = component("Item", {
|
|
3883
|
+
* kind: "",
|
|
3884
|
+
* level: 1
|
|
3885
|
+
* });
|
|
3886
|
+
* Item({ level: 2 }); // { type: Item, value: { kind: "", level: 2 } }
|
|
3887
|
+
* Item.owned; // []: a projection view writes every field
|
|
3924
3888
|
* ```
|
|
3925
3889
|
*/
|
|
3926
|
-
type
|
|
3927
|
-
|
|
3928
|
-
|
|
3929
|
-
|
|
3930
|
-
|
|
3931
|
-
|
|
3932
|
-
* @param to - The numeric target fields.
|
|
3933
|
-
* @param options - Duration, easing, delay and the additive flag.
|
|
3934
|
-
* @param muted - The fields another writer owns, read at every write.
|
|
3935
|
-
* @returns The motion handle of the track.
|
|
3936
|
-
*/
|
|
3937
|
-
track(entity: Entity, component: AnyComponent, to: Record<string, number>, options: TrackOptions, muted: () => ReadonlySet<string>): MotionHandle;
|
|
3938
|
-
/**
|
|
3939
|
-
* Ends every track of one entity. Called when a view despawns and on every flush.
|
|
3940
|
-
*
|
|
3941
|
-
* @param entity - The entity whose tracks end.
|
|
3942
|
-
*/
|
|
3943
|
-
cancelAll(entity: Entity): void;
|
|
3890
|
+
type ComponentType<Value extends object> = {
|
|
3891
|
+
(patch?: Partial<Value>): ComponentValue<Value>;
|
|
3892
|
+
readonly componentName: string;
|
|
3893
|
+
readonly defaults: Readonly<Value>;
|
|
3894
|
+
readonly kind: "component"; /** Fields a plugin writes and a projection view never does, so a view never corrects them. */
|
|
3895
|
+
readonly owned: readonly string[];
|
|
3944
3896
|
};
|
|
3945
3897
|
/**
|
|
3946
|
-
*
|
|
3947
|
-
* the shape structurally, carries it in `Tree` and hands it back unchanged.
|
|
3898
|
+
* A tag type made by `tag()`: a component with no data.
|
|
3948
3899
|
*
|
|
3949
3900
|
* @example
|
|
3950
3901
|
* ```ts
|
|
3951
|
-
* const
|
|
3902
|
+
* const Held: TagType = tag("Held");
|
|
3903
|
+
* Held(); // { type: Held, value: true }
|
|
3952
3904
|
* ```
|
|
3953
3905
|
*/
|
|
3954
|
-
type
|
|
3955
|
-
|
|
3956
|
-
|
|
3957
|
-
|
|
3958
|
-
children: readonly unknown[];
|
|
3906
|
+
type TagType = {
|
|
3907
|
+
(): TagValue;
|
|
3908
|
+
readonly componentName: string;
|
|
3909
|
+
readonly kind: "tag";
|
|
3959
3910
|
};
|
|
3960
3911
|
/**
|
|
3961
|
-
* What
|
|
3962
|
-
* the world wraps as `Tree`.
|
|
3912
|
+
* What the world stores about a component or tag type, independent of its data type.
|
|
3963
3913
|
*
|
|
3964
3914
|
* @example
|
|
3965
3915
|
* ```ts
|
|
3966
|
-
* const
|
|
3916
|
+
* const registered: AnyComponentType = { componentName: "Held", kind: "tag" };
|
|
3967
3917
|
* ```
|
|
3968
3918
|
*/
|
|
3969
|
-
type
|
|
3919
|
+
type AnyComponentType = {
|
|
3920
|
+
readonly componentName: string;
|
|
3921
|
+
readonly kind: "component" | "tag";
|
|
3922
|
+
};
|
|
3970
3923
|
/**
|
|
3971
|
-
* What
|
|
3972
|
-
*
|
|
3924
|
+
* What the ECS needs from a component type to read and write its store: the name and the
|
|
3925
|
+
* defaults, never the call signature. A component bound to a game's keys by a `…For` binder
|
|
3926
|
+
* (`Narrowed`) is the same object with a narrower call, so it passes here unchanged.
|
|
3973
3927
|
*
|
|
3974
3928
|
* @example
|
|
3975
3929
|
* ```ts
|
|
3976
|
-
* const
|
|
3977
|
-
*
|
|
3978
|
-
* return view.toRest(Transform, { ms: 250 });
|
|
3979
|
-
* };
|
|
3930
|
+
* const ref: ComponentHandle<{ x: number }> = component("Pos", { x: 0 });
|
|
3931
|
+
* ref.defaults; // { x: 0 }
|
|
3980
3932
|
* ```
|
|
3981
3933
|
*/
|
|
3982
|
-
type
|
|
3983
|
-
readonly
|
|
3984
|
-
readonly
|
|
3985
|
-
|
|
3986
|
-
rest<Value extends object>(component: ComponentType<Value>): Readonly<Value> | undefined;
|
|
3987
|
-
set<Value extends object>(component: ComponentType<Value>, patch: Partial<Value>): void;
|
|
3988
|
-
tween<Value extends object>(component: ComponentType<Value>, to: Partial<NumericFields<Value>>, options: TweenOptions): MotionHandle;
|
|
3989
|
-
toRest<Value extends object>(component: ComponentType<Value>, options?: RestOptions): MotionHandle;
|
|
3990
|
-
all(handles: readonly Motion[]): MotionHandle;
|
|
3991
|
-
peer(key: string): Item | undefined;
|
|
3934
|
+
type ComponentHandle<Value extends object> = {
|
|
3935
|
+
readonly componentName: string;
|
|
3936
|
+
readonly defaults: Readonly<Value>;
|
|
3937
|
+
readonly kind: "component";
|
|
3992
3938
|
};
|
|
3993
3939
|
/**
|
|
3994
|
-
*
|
|
3995
|
-
*
|
|
3940
|
+
* A component type whose call signature takes a narrower patch: the same object, the same
|
|
3941
|
+
* store, the compiler refuses what the game's generated keys do not know.
|
|
3996
3942
|
*
|
|
3997
3943
|
* @example
|
|
3998
3944
|
* ```ts
|
|
3999
|
-
* const
|
|
3945
|
+
* const Sprite: Narrowed<{ texture: string }, { texture?: "board.cell" }> = component("Sprite", { texture: "" });
|
|
3946
|
+
* Sprite({ texture: "board.cell" }).value.texture; // "board.cell"
|
|
4000
3947
|
* ```
|
|
4001
3948
|
*/
|
|
4002
|
-
type
|
|
3949
|
+
type Narrowed<Value extends object, Patch> = Omit<ComponentType<Value>, never> & ((patch?: Patch) => ComponentValue<Value>);
|
|
4003
3950
|
/**
|
|
4004
|
-
*
|
|
3951
|
+
* A resource type made by `resource()`: one mutable object per world, created from a deep clone
|
|
3952
|
+
* of the defaults on first read.
|
|
4005
3953
|
*
|
|
4006
3954
|
* @example
|
|
4007
3955
|
* ```ts
|
|
4008
|
-
* const
|
|
3956
|
+
* const Pointer: ResourceType<{ x: number; y: number }> = resource("Pointer", { x: 0, y: 0 });
|
|
3957
|
+
* Pointer.resourceName; // "Pointer"
|
|
4009
3958
|
* ```
|
|
4010
3959
|
*/
|
|
4011
|
-
type
|
|
4012
|
-
readonly
|
|
3960
|
+
type ResourceType<Value extends object> = {
|
|
3961
|
+
readonly resourceName: string;
|
|
3962
|
+
readonly defaults: Readonly<Value>;
|
|
4013
3963
|
};
|
|
4014
3964
|
/**
|
|
4015
|
-
*
|
|
4016
|
-
* written directly. `loop` starts what moves a view for its whole life (an additive sway, a spin):
|
|
4017
|
-
* it is played wherever `enter` plays, next to it, and its motion is kept apart from the view's
|
|
4018
|
-
* other motions, so a change never cancels it; it ends when the view dies.
|
|
3965
|
+
* A query term marked as written by `mut()`. Its only effect is change detection.
|
|
4019
3966
|
*
|
|
4020
3967
|
* @example
|
|
4021
3968
|
* ```ts
|
|
4022
|
-
* const
|
|
4023
|
-
*
|
|
4024
|
-
* };
|
|
4025
|
-
* // An order card that sways forever once it entered.
|
|
4026
|
-
* const swaying: ProjectionMotion<Item> = {
|
|
4027
|
-
* loop: view =>
|
|
4028
|
-
* view.tween(Transform, { rotation: 0.05 }, { ms: 1200, additive: true, repeat: "forever" })
|
|
4029
|
-
* };
|
|
3969
|
+
* const written: Mut<{ x: number }> = mut(Transform);
|
|
3970
|
+
* written.kind; // "mut"
|
|
4030
3971
|
* ```
|
|
4031
3972
|
*/
|
|
4032
|
-
type
|
|
4033
|
-
|
|
4034
|
-
|
|
4035
|
-
exit?(view: ViewHandle<Item>, item: Item, hint?: Hint): Motion;
|
|
4036
|
-
readonly change?: ChangeHooks<Item>;
|
|
4037
|
-
settle?(view: ViewHandle<Item>, components: readonly string[]): Motion;
|
|
3973
|
+
type Mut<Value extends object> = {
|
|
3974
|
+
readonly kind: "mut";
|
|
3975
|
+
readonly of: ComponentHandle<Value>;
|
|
4038
3976
|
};
|
|
4039
3977
|
/**
|
|
4040
|
-
*
|
|
3978
|
+
* A `mut()` term with its data type erased, as the world stores it.
|
|
4041
3979
|
*
|
|
4042
3980
|
* @example
|
|
4043
3981
|
* ```ts
|
|
4044
|
-
* const
|
|
3982
|
+
* const term: AnyMut = { kind: "mut", of: { componentName: "Transform", kind: "component" } };
|
|
4045
3983
|
* ```
|
|
4046
3984
|
*/
|
|
4047
|
-
type
|
|
4048
|
-
|
|
4049
|
-
|
|
4050
|
-
exit?(view: ViewHandle<unknown>, item: unknown, hint?: Hint): Motion;
|
|
4051
|
-
readonly change?: ChangeHooks<never>;
|
|
4052
|
-
settle?(view: ViewHandle<unknown>, components: readonly string[]): Motion;
|
|
3985
|
+
type AnyMut = {
|
|
3986
|
+
readonly kind: "mut";
|
|
3987
|
+
readonly of: AnyComponentType;
|
|
4053
3988
|
};
|
|
4054
3989
|
/**
|
|
4055
|
-
* One
|
|
4056
|
-
* return of `from`; `layer` and `lift` keep their literal types so a scene can check them.
|
|
3990
|
+
* One term of a query: a component type, a tag type, or a `mut()` of a component type.
|
|
4057
3991
|
*
|
|
4058
3992
|
* @example
|
|
4059
3993
|
* ```ts
|
|
4060
|
-
* const
|
|
4061
|
-
*
|
|
4062
|
-
|
|
4063
|
-
|
|
4064
|
-
|
|
4065
|
-
*
|
|
4066
|
-
*
|
|
3994
|
+
* const terms: QueryTerm[] = [mut(Transform), Sprite, Held];
|
|
3995
|
+
* ```
|
|
3996
|
+
*/
|
|
3997
|
+
type QueryTerm = AnyComponentType | AnyMut;
|
|
3998
|
+
/**
|
|
3999
|
+
* The value a query yields for one term: writable for `mut()`, `true` for a tag, read-only for a
|
|
4000
|
+
* plain component.
|
|
4001
|
+
*
|
|
4002
|
+
* @example
|
|
4003
|
+
* ```ts
|
|
4004
|
+
* type Written = ValueOf<Mut<{ x: number }>>; // { x: number }
|
|
4005
|
+
* ```
|
|
4006
|
+
*/
|
|
4007
|
+
type ValueOf<Term> = Term extends Mut<infer Value> ? Value : Term extends TagType ? true : Term extends ComponentHandle<infer Value> ? Readonly<Value> : never;
|
|
4008
|
+
/**
|
|
4009
|
+
* The value part of a query row, one entry per term, in term order.
|
|
4010
|
+
*
|
|
4011
|
+
* @example
|
|
4012
|
+
* ```ts
|
|
4013
|
+
* type Values = QueryValues<[TagType]>; // [true]
|
|
4014
|
+
* ```
|
|
4015
|
+
*/
|
|
4016
|
+
type QueryValues<Terms extends readonly QueryTerm[]> = { -readonly [Index in keyof Terms]: ValueOf<Terms[Index]> };
|
|
4017
|
+
/**
|
|
4018
|
+
* One row a query yields: the entity, then one value per term.
|
|
4019
|
+
*
|
|
4020
|
+
* @example
|
|
4021
|
+
* ```ts
|
|
4022
|
+
* type Row = QueryTuple<[ComponentType<{ x: number }>]>; // [Entity, Readonly<{ x: number }>]
|
|
4023
|
+
* ```
|
|
4024
|
+
*/
|
|
4025
|
+
type QueryTuple<Terms extends readonly QueryTerm[]> = [Entity, ...QueryValues<Terms>];
|
|
4026
|
+
/**
|
|
4027
|
+
* What a system receives next to its entities: the world, the mutable resources, the frozen model
|
|
4028
|
+
* snapshot of the frame and the frame's `Time`. A system may read pure game rules; it never
|
|
4029
|
+
* writes the model.
|
|
4030
|
+
*
|
|
4031
|
+
* @example
|
|
4032
|
+
* ```ts
|
|
4033
|
+
* const run = (entities: Iterable<[Entity]>, ctx: SystemContext): void => {
|
|
4034
|
+
* ctx.res(Pointer).x = 0;
|
|
4035
|
+
* ctx.time.delta; // 16
|
|
4067
4036
|
* };
|
|
4068
4037
|
* ```
|
|
4069
4038
|
*/
|
|
4070
|
-
type
|
|
4071
|
-
readonly
|
|
4072
|
-
|
|
4073
|
-
readonly
|
|
4074
|
-
|
|
4075
|
-
key?(item: Item): string;
|
|
4076
|
-
view(item: Item): ViewOutput;
|
|
4077
|
-
readonly motion?: ProjectionMotion<Item>;
|
|
4039
|
+
type SystemContext = {
|
|
4040
|
+
readonly world: EcsApi;
|
|
4041
|
+
res<Value extends object>(resource: ResourceType<Value>): Value;
|
|
4042
|
+
readonly snapshot: Snapshot;
|
|
4043
|
+
readonly time: Readonly<Time>;
|
|
4078
4044
|
};
|
|
4079
4045
|
/**
|
|
4080
|
-
* A
|
|
4046
|
+
* A system definition, typed over its query tuple.
|
|
4081
4047
|
*
|
|
4082
4048
|
* @example
|
|
4083
4049
|
* ```ts
|
|
4084
|
-
* const
|
|
4085
|
-
* name: "
|
|
4086
|
-
*
|
|
4087
|
-
*
|
|
4088
|
-
*
|
|
4089
|
-
*
|
|
4050
|
+
* const bob: SystemDefinition<[ComponentType<{ y: number }>]> = {
|
|
4051
|
+
* name: "bob",
|
|
4052
|
+
* phase: "animate",
|
|
4053
|
+
* query: [Transform],
|
|
4054
|
+
* run: entities => {
|
|
4055
|
+
* for (const [, transform] of entities) transform.y; // read-only
|
|
4056
|
+
* }
|
|
4090
4057
|
* };
|
|
4091
4058
|
* ```
|
|
4092
4059
|
*/
|
|
4093
|
-
type
|
|
4060
|
+
type SystemDefinition<Terms extends readonly QueryTerm[]> = {
|
|
4094
4061
|
readonly name: string;
|
|
4095
|
-
readonly
|
|
4096
|
-
readonly
|
|
4097
|
-
|
|
4098
|
-
key?(item: unknown): string;
|
|
4099
|
-
view(item: unknown): ViewOutput;
|
|
4100
|
-
readonly motion?: AnyProjectionMotion;
|
|
4062
|
+
readonly phase: WorldPhase;
|
|
4063
|
+
readonly query: Terms;
|
|
4064
|
+
run(entities: Iterable<QueryTuple<Terms>>, ctx: SystemContext): void;
|
|
4101
4065
|
};
|
|
4102
4066
|
/**
|
|
4103
|
-
*
|
|
4067
|
+
* A system with its query tuple erased, as the world stores it.
|
|
4104
4068
|
*
|
|
4105
4069
|
* @example
|
|
4106
4070
|
* ```ts
|
|
4107
|
-
* const
|
|
4071
|
+
* const entry: AnySystem = { name: "bob", phase: "animate", query: [], run: () => {} };
|
|
4108
4072
|
* ```
|
|
4109
4073
|
*/
|
|
4110
|
-
type
|
|
4074
|
+
type AnySystem = {
|
|
4075
|
+
readonly name: string;
|
|
4076
|
+
readonly phase: WorldPhase;
|
|
4077
|
+
readonly query: readonly QueryTerm[];
|
|
4078
|
+
run(entities: Iterable<readonly unknown[]>, ctx: SystemContext): void;
|
|
4079
|
+
};
|
|
4111
4080
|
/**
|
|
4112
|
-
* One
|
|
4113
|
-
*
|
|
4081
|
+
* One registered system and the frame it was registered in. A system registered during a frame
|
|
4082
|
+
* runs from the next frame on.
|
|
4114
4083
|
*
|
|
4115
4084
|
* @example
|
|
4116
4085
|
* ```ts
|
|
4117
|
-
* const
|
|
4118
|
-
* entity: 1_048_576,
|
|
4119
|
-
* projection: "board.items",
|
|
4120
|
-
* key: "i7",
|
|
4121
|
-
* item: { id: "i7" },
|
|
4122
|
-
* rest: new Map(),
|
|
4123
|
-
* handles: [],
|
|
4124
|
-
* lifted: false,
|
|
4125
|
-
* dropWhenStill: false,
|
|
4126
|
-
* exiting: false
|
|
4127
|
-
* };
|
|
4086
|
+
* const entry: SystemEntry = { definition: bobSystem, frame: 12 };
|
|
4128
4087
|
* ```
|
|
4129
4088
|
*/
|
|
4130
|
-
type
|
|
4131
|
-
|
|
4132
|
-
|
|
4133
|
-
key: string;
|
|
4134
|
-
item: unknown; /** The last output of `view(item)`, by component name: what the picture equals at rest. */
|
|
4135
|
-
rest: Map<string, AnyComponentValue>;
|
|
4136
|
-
handles: MotionHandle[];
|
|
4137
|
-
lifted: boolean; /** `lift(false)` is waiting for the last motion to end. */
|
|
4138
|
-
dropWhenStill: boolean;
|
|
4139
|
-
exiting: boolean;
|
|
4089
|
+
type SystemEntry = {
|
|
4090
|
+
readonly definition: AnySystem;
|
|
4091
|
+
readonly frame: number;
|
|
4140
4092
|
};
|
|
4141
4093
|
/**
|
|
4142
|
-
*
|
|
4143
|
-
* the last reconcile.
|
|
4094
|
+
* A structural change queued while a phase runs. Applied in call order when the phase ends.
|
|
4144
4095
|
*
|
|
4145
4096
|
* @example
|
|
4146
4097
|
* ```ts
|
|
4147
|
-
* const
|
|
4148
|
-
* owner: { kind: "projection", name: "board.items" },
|
|
4149
|
-
* live: new Map(),
|
|
4150
|
-
* queue: [],
|
|
4151
|
-
* items: new Map()
|
|
4152
|
-
* };
|
|
4098
|
+
* const command: Command = { kind: "remove", entity: 1_048_576, component: "Held" };
|
|
4153
4099
|
* ```
|
|
4154
4100
|
*/
|
|
4155
|
-
type
|
|
4101
|
+
type Command = {
|
|
4102
|
+
kind: "attach";
|
|
4103
|
+
entity: Entity;
|
|
4104
|
+
components: readonly AnyComponentValue[];
|
|
4105
|
+
} | {
|
|
4106
|
+
kind: "despawn";
|
|
4107
|
+
entity: Entity;
|
|
4108
|
+
} | {
|
|
4109
|
+
kind: "despawnOwnedBy";
|
|
4156
4110
|
owner: Owner;
|
|
4157
|
-
|
|
4158
|
-
|
|
4159
|
-
|
|
4111
|
+
} | {
|
|
4112
|
+
kind: "add";
|
|
4113
|
+
entity: Entity;
|
|
4114
|
+
value: AnyComponentValue;
|
|
4115
|
+
} | {
|
|
4116
|
+
kind: "remove";
|
|
4117
|
+
entity: Entity;
|
|
4118
|
+
component: string;
|
|
4160
4119
|
};
|
|
4161
4120
|
/**
|
|
4162
|
-
*
|
|
4163
|
-
* handle, so it knows which components are driven without knowing how the driver runs them.
|
|
4121
|
+
* A structural listener registered with `onAdded` or `onRemoved`.
|
|
4164
4122
|
*
|
|
4165
4123
|
* @example
|
|
4166
4124
|
* ```ts
|
|
4167
|
-
* const
|
|
4168
|
-
* track.handle.active(); // true while the flight runs
|
|
4125
|
+
* const hook: StructuralHook = (entity, value) => void entity + String(value);
|
|
4169
4126
|
* ```
|
|
4170
4127
|
*/
|
|
4171
|
-
type
|
|
4172
|
-
|
|
4173
|
-
|
|
4174
|
-
|
|
4128
|
+
type StructuralHook = (entity: Entity, value: unknown) => void;
|
|
4129
|
+
/**
|
|
4130
|
+
* The frozen model snapshot of one frame, kept so four phases read it once.
|
|
4131
|
+
*
|
|
4132
|
+
* @example
|
|
4133
|
+
* ```ts
|
|
4134
|
+
* const cached: FrameSnapshot = { frame: 12, snapshot: { player: {}, session: {}, rng: rngState } };
|
|
4135
|
+
* ```
|
|
4136
|
+
*/
|
|
4137
|
+
type FrameSnapshot = {
|
|
4138
|
+
frame: number;
|
|
4139
|
+
snapshot: Snapshot;
|
|
4175
4140
|
};
|
|
4176
4141
|
/**
|
|
4177
|
-
* The
|
|
4178
|
-
*
|
|
4142
|
+
* The JSON kind of one component field, read from its default value: what the editor's component
|
|
4143
|
+
* palette shows next to the field name. A nested shape is not described; `defaults` carries it.
|
|
4144
|
+
*
|
|
4145
|
+
* @example
|
|
4146
|
+
* ```ts
|
|
4147
|
+
* const kinds: Record<string, FieldKind> = { x: "number", pivot: "object", tags: "array" };
|
|
4148
|
+
* ```
|
|
4179
4149
|
*/
|
|
4180
|
-
type
|
|
4150
|
+
type FieldKind = "number" | "string" | "boolean" | "object" | "array" | "null";
|
|
4181
4151
|
/**
|
|
4182
|
-
*
|
|
4183
|
-
* and `keyOf` answer for it next to the views.
|
|
4152
|
+
* One component or tag type the world met, as `ecs.schema()` lists it.
|
|
4184
4153
|
*
|
|
4185
4154
|
* @example
|
|
4186
4155
|
* ```ts
|
|
4187
|
-
* const
|
|
4156
|
+
* const held: ComponentSchema = {
|
|
4157
|
+
* name: "Held", kind: "tag", json: true, fields: {}, defaults: true, owned: []
|
|
4158
|
+
* };
|
|
4159
|
+
* const display: ComponentSchema = {
|
|
4160
|
+
* name: "Display", kind: "component", json: false, fields: {}, defaults: null, owned: []
|
|
4161
|
+
* };
|
|
4188
4162
|
* ```
|
|
4189
4163
|
*/
|
|
4190
|
-
type
|
|
4191
|
-
|
|
4192
|
-
|
|
4164
|
+
type ComponentSchema = {
|
|
4165
|
+
/** The storage name, `componentName`. */name: string;
|
|
4166
|
+
kind: "component" | "tag"; /** False when the defaults are not JSON, as `Display`'s: `fields` is then `{}` and `defaults` `null`. */
|
|
4167
|
+
json: boolean; /** Field name to the JSON kind of its default. */
|
|
4168
|
+
fields: Record<string, FieldKind>; /** The defaults as JSON; `true` for a tag. */
|
|
4169
|
+
defaults: Json; /** The fields a plugin writes and a view never does; `[]` for a tag. */
|
|
4170
|
+
owned: readonly string[];
|
|
4193
4171
|
};
|
|
4194
4172
|
/**
|
|
4195
|
-
*
|
|
4196
|
-
*
|
|
4173
|
+
* The first and the last value of one component inside a frame window. `null` on a side means
|
|
4174
|
+
* the entity did not carry the component there: component values are objects or `true`, never
|
|
4175
|
+
* `null`.
|
|
4197
4176
|
*
|
|
4198
4177
|
* @example
|
|
4199
4178
|
* ```ts
|
|
4200
|
-
* const
|
|
4179
|
+
* const levelUp: ComponentChange = { from: { level: 2 }, to: { level: 3 } };
|
|
4180
|
+
* const picked: ComponentChange = { from: null, to: true }; // the tag Held was added
|
|
4201
4181
|
* ```
|
|
4202
4182
|
*/
|
|
4203
|
-
type
|
|
4204
|
-
|
|
4205
|
-
|
|
4206
|
-
force: boolean;
|
|
4183
|
+
type ComponentChange = {
|
|
4184
|
+
from: Json | null;
|
|
4185
|
+
to: Json | null;
|
|
4207
4186
|
};
|
|
4208
4187
|
/**
|
|
4209
|
-
*
|
|
4188
|
+
* What happened to one entity between two frames: who owns it, where it lives now, whether it
|
|
4189
|
+
* came, went or changed, and every component that really changed.
|
|
4190
|
+
*
|
|
4191
|
+
* @example
|
|
4192
|
+
* ```ts
|
|
4193
|
+
* const merged: EntityDiff = {
|
|
4194
|
+
* id: 1_048_577,
|
|
4195
|
+
* owner: { kind: "projection", name: "board.items" },
|
|
4196
|
+
* key: { projection: "board.items", key: "i7" },
|
|
4197
|
+
* change: "changed",
|
|
4198
|
+
* components: { Level: { from: { level: 2 }, to: { level: 3 } } }
|
|
4199
|
+
* };
|
|
4200
|
+
* ```
|
|
4210
4201
|
*/
|
|
4211
|
-
type
|
|
4212
|
-
|
|
4213
|
-
|
|
4214
|
-
|
|
4215
|
-
|
|
4216
|
-
|
|
4217
|
-
hints: Hint[]; /** The tracks the projection started, with the handle that says whether they still run. */
|
|
4218
|
-
tracks: Track[]; /** The tween engine `anim` installed, or `undefined`: then every target is written at once. */
|
|
4219
|
-
driver: TweenDriver | undefined; /** The rest pose of an entity that is not a view, recorded by its owner through `setRest`. */
|
|
4220
|
-
rests: Map<Entity, RestPose>; /** Projection name to key to the entity a plugin above registered under it. */
|
|
4221
|
-
keys: Map<string, Map<string, Entity>>; /** The reverse of `keys`, so `keyOf` answers for a registered element too. */
|
|
4222
|
-
keysByEntity: Map<Entity, ProjectionKey>; /** Entity to component name to the fields another writer owns. */
|
|
4223
|
-
mutes: Map<Entity, Map<string, Set<string>>>;
|
|
4224
|
-
offHints: Array<() => void>;
|
|
4202
|
+
type EntityDiff = {
|
|
4203
|
+
id: Entity;
|
|
4204
|
+
owner: Owner; /** The live address at read time; `undefined` once the entity is gone, or when it has no key. */
|
|
4205
|
+
key: ProjectionKey | undefined;
|
|
4206
|
+
change: "spawned" | "despawned" | "changed";
|
|
4207
|
+
components: Record<string, ComponentChange>;
|
|
4225
4208
|
};
|
|
4226
4209
|
/**
|
|
4227
|
-
*
|
|
4210
|
+
* What changed between the world at the end of frame `from` and the world at the end of frame
|
|
4211
|
+
* `to`, as `ecs.diff` answers it. The entities are sorted by id.
|
|
4228
4212
|
*
|
|
4229
4213
|
* @example
|
|
4230
4214
|
* ```ts
|
|
4231
|
-
*
|
|
4232
|
-
* app.world.projection.mount(["board.items"], { kind: "plugin", name: "test" });
|
|
4233
|
-
* app.world.projection.entityOf("board.items", "i7"); // 1048576
|
|
4215
|
+
* const quiet: FrameDiff = { from: 120, to: 180, entities: [] };
|
|
4234
4216
|
* ```
|
|
4235
4217
|
*/
|
|
4236
|
-
type
|
|
4237
|
-
|
|
4238
|
-
|
|
4239
|
-
|
|
4240
|
-
|
|
4241
|
-
|
|
4242
|
-
|
|
4243
|
-
|
|
4244
|
-
|
|
4245
|
-
|
|
4246
|
-
|
|
4247
|
-
|
|
4248
|
-
|
|
4249
|
-
|
|
4250
|
-
|
|
4251
|
-
|
|
4252
|
-
|
|
4218
|
+
type FrameDiff = {
|
|
4219
|
+
from: number;
|
|
4220
|
+
to: number;
|
|
4221
|
+
entities: readonly EntityDiff[];
|
|
4222
|
+
};
|
|
4223
|
+
/**
|
|
4224
|
+
* One record of the frame history: one component of one entity, before and after. `null` means
|
|
4225
|
+
* absent, so a spawn records `from: null` and a despawn `to: null`.
|
|
4226
|
+
*
|
|
4227
|
+
* @example
|
|
4228
|
+
* ```ts
|
|
4229
|
+
* const levelUp: HistoryChange = {
|
|
4230
|
+
* id: 1_048_577,
|
|
4231
|
+
* owner: { kind: "projection", name: "board.items" },
|
|
4232
|
+
* component: "Level",
|
|
4233
|
+
* from: { level: 2 },
|
|
4234
|
+
* to: { level: 3 }
|
|
4235
|
+
* };
|
|
4236
|
+
* ```
|
|
4237
|
+
*/
|
|
4238
|
+
type HistoryChange = {
|
|
4239
|
+
id: Entity;
|
|
4240
|
+
owner: Owner;
|
|
4241
|
+
component: string;
|
|
4242
|
+
from: Json | null;
|
|
4243
|
+
to: Json | null;
|
|
4244
|
+
};
|
|
4245
|
+
/**
|
|
4246
|
+
* The records of one frame, and the entities that frame spawned and despawned: a record alone
|
|
4247
|
+
* cannot tell a spawn from a component added to a live entity.
|
|
4248
|
+
*
|
|
4249
|
+
* @example
|
|
4250
|
+
* ```ts
|
|
4251
|
+
* const quiet: HistorySlot = { frame: 96, changes: [], spawned: [], despawned: [] };
|
|
4252
|
+
* ```
|
|
4253
|
+
*/
|
|
4254
|
+
type HistorySlot = {
|
|
4255
|
+
frame: number;
|
|
4256
|
+
changes: HistoryChange[];
|
|
4257
|
+
spawned: Entity[];
|
|
4258
|
+
despawned: Entity[];
|
|
4259
|
+
};
|
|
4260
|
+
/**
|
|
4261
|
+
* The last recorded JSON of one live entity: its owner and its components by name.
|
|
4262
|
+
*/
|
|
4263
|
+
type ShadowEntry = {
|
|
4264
|
+
owner: Owner;
|
|
4265
|
+
components: Map<string, Json>;
|
|
4266
|
+
};
|
|
4267
|
+
/**
|
|
4268
|
+
* The frame history of a dev build: one shadow copy of the world as JSON and a ring of the
|
|
4269
|
+
* records of the last frames, one slot per frame at index `frame % HISTORY_FRAMES`.
|
|
4270
|
+
*/
|
|
4271
|
+
type History = {
|
|
4272
|
+
/** The last recorded JSON of every live entity. */shadow: Map<Entity, ShadowEntry>; /** `HISTORY_FRAMES` long. */
|
|
4273
|
+
slots: Array<HistorySlot | undefined>; /** The last frame recorded, `undefined` before the first one. */
|
|
4274
|
+
newest: number | undefined; /** The frame the recording started on: the shadow is the world at its end. */
|
|
4275
|
+
started: number | undefined;
|
|
4276
|
+
};
|
|
4277
|
+
/**
|
|
4278
|
+
* ecs module state.
|
|
4279
|
+
*/
|
|
4280
|
+
type EcsState = {
|
|
4281
|
+
generations: number[];
|
|
4282
|
+
free: number[];
|
|
4283
|
+
owners: Map<Entity, Owner>; /** Key is `"kind:name"` of the owner. */
|
|
4284
|
+
byOwner: Map<string, Set<Entity>>;
|
|
4285
|
+
types: Map<string, AnyComponentType>;
|
|
4286
|
+
stores: Map<string, Map<Entity, object | true>>;
|
|
4287
|
+
resources: Map<string, object>;
|
|
4288
|
+
systems: Record<WorldPhase, SystemEntry[]>;
|
|
4289
|
+
running: WorldPhase | undefined;
|
|
4290
|
+
commands: Command[];
|
|
4291
|
+
changed: Map<string, Set<Entity>>;
|
|
4292
|
+
added: Map<string, StructuralHook[]>;
|
|
4293
|
+
removed: Map<string, StructuralHook[]>;
|
|
4294
|
+
ownerLeft: Array<(owner: Owner) => void>;
|
|
4295
|
+
mode: WorldMode;
|
|
4296
|
+
offFrame: Array<() => void>;
|
|
4297
|
+
frameSnapshot: FrameSnapshot | undefined; /** The frame history: dev builds only, from the first frame on; `undefined` before it. */
|
|
4298
|
+
history: History | undefined;
|
|
4299
|
+
};
|
|
4300
|
+
/**
|
|
4301
|
+
* ecs module API, `app.world.ecs`: the screen as data. Plain-object components in one store per
|
|
4302
|
+
* component type, generational entity ids, a mandatory owner, a command buffer applied between
|
|
4303
|
+
* phases, coarse change detection and fixed phases driven by `time`.
|
|
4304
|
+
*
|
|
4305
|
+
* @example
|
|
4306
|
+
* ```ts
|
|
4307
|
+
* // A test owns its entities and reads them back.
|
|
4308
|
+
* const entity = app.world.ecs.spawn({ kind: "plugin", name: "test" }, [Transform({ x: 10 })]);
|
|
4309
|
+
* app.world.ecs.get(entity, Transform); // { x: 10, y: 0, rotation: 0, scale: 1, pivot: { x: 0, y: 0 } }
|
|
4310
|
+
* ```
|
|
4311
|
+
*/
|
|
4312
|
+
type EcsApi = {
|
|
4313
|
+
/**
|
|
4314
|
+
* Registers a system at the end of its phase list. A system registered during a frame runs
|
|
4315
|
+
* from the next frame on.
|
|
4253
4316
|
*
|
|
4254
|
-
* @param
|
|
4317
|
+
* @param definition - The system, built with `system()`.
|
|
4318
|
+
* @returns The remover; calling it twice is a no-op.
|
|
4319
|
+
* @throws {Error} When a system of that name is already registered.
|
|
4255
4320
|
* @example
|
|
4256
4321
|
* ```ts
|
|
4257
|
-
* // `
|
|
4322
|
+
* // `text` registers its layout system when the plugin starts.
|
|
4258
4323
|
* const world = ctx.require(worldPlugin);
|
|
4259
|
-
* world.
|
|
4260
|
-
*
|
|
4261
|
-
*
|
|
4262
|
-
* { name: "lifted", sort: "none" }
|
|
4263
|
-
* ]);
|
|
4324
|
+
* const off = world.ecs.system(measureLabels); // runs from the next frame
|
|
4325
|
+
*
|
|
4326
|
+
* off(); // the plugin stops
|
|
4264
4327
|
* ```
|
|
4265
4328
|
*/
|
|
4266
|
-
|
|
4329
|
+
system(definition: AnySystem): () => void;
|
|
4267
4330
|
/**
|
|
4268
|
-
*
|
|
4269
|
-
*
|
|
4331
|
+
* Creates an entity. The id is reserved and returned at once, also inside a phase, where the
|
|
4332
|
+
* components are attached when the phase ends.
|
|
4270
4333
|
*
|
|
4271
|
-
* @
|
|
4334
|
+
* @param owner - Who owns the entity. Required by the type.
|
|
4335
|
+
* @param components - Component and tag values the entity starts with.
|
|
4336
|
+
* @returns The new entity id.
|
|
4272
4337
|
* @example
|
|
4273
4338
|
* ```ts
|
|
4274
|
-
* // `
|
|
4275
|
-
* const
|
|
4276
|
-
*
|
|
4339
|
+
* // `ui` spawns a popup entity it owns, so closing the node frees it in one call.
|
|
4340
|
+
* const entity = app.world.ecs.spawn({ kind: "node", name: "reward" }, [
|
|
4341
|
+
* Transform({ x: 540, y: 960 })
|
|
4342
|
+
* ]);
|
|
4343
|
+
* app.world.ecs.has(entity, Transform); // true
|
|
4277
4344
|
* ```
|
|
4278
4345
|
*/
|
|
4279
|
-
|
|
4346
|
+
spawn(owner: Owner, components: readonly AnyComponentValue[]): Entity;
|
|
4280
4347
|
/**
|
|
4281
|
-
*
|
|
4282
|
-
*
|
|
4348
|
+
* Removes every component of an entity, each firing `onRemoved`, frees the index and bumps the
|
|
4349
|
+
* generation. A stale id is a no-op.
|
|
4283
4350
|
*
|
|
4284
|
-
* @param
|
|
4285
|
-
* @param owner - Who owns the entities of these projections.
|
|
4286
|
-
* @throws {Error} For an unknown name, or a `layer` or `lift` the scene does not declare.
|
|
4351
|
+
* @param entity - The entity to remove.
|
|
4287
4352
|
* @example
|
|
4288
4353
|
* ```ts
|
|
4289
|
-
* // `
|
|
4290
|
-
*
|
|
4291
|
-
* world.
|
|
4292
|
-
* world.projection.entityOf("board.items", "i7"); // 1048576, the picture is already there
|
|
4354
|
+
* // `ui` closes the popup it spawned. Asking again answers with the empty world.
|
|
4355
|
+
* app.world.ecs.despawn(popup);
|
|
4356
|
+
* app.world.ecs.has(popup, Transform); // false
|
|
4293
4357
|
* ```
|
|
4294
4358
|
*/
|
|
4295
|
-
|
|
4359
|
+
despawn(entity: Entity): void;
|
|
4296
4360
|
/**
|
|
4297
|
-
*
|
|
4298
|
-
* despawn queue are despawned in the same call.
|
|
4361
|
+
* Despawns exactly the entities of one owner and tells `projection` that the owner left.
|
|
4299
4362
|
*
|
|
4300
|
-
* @param
|
|
4363
|
+
* @param owner - The owner whose entities go.
|
|
4301
4364
|
* @example
|
|
4302
4365
|
* ```ts
|
|
4303
|
-
* //
|
|
4304
|
-
*
|
|
4305
|
-
* world.
|
|
4306
|
-
* world.projection.entityOf("board.items", "i7"); // undefined
|
|
4366
|
+
* // A test cleans up after itself without touching the projections of the game.
|
|
4367
|
+
* app.world.ecs.despawnOwnedBy({ kind: "plugin", name: "test" });
|
|
4368
|
+
* app.world.ecs.snapshot(); // { mode: "live", entities: [], resources: {} }
|
|
4307
4369
|
* ```
|
|
4308
4370
|
*/
|
|
4309
|
-
|
|
4371
|
+
despawnOwnedBy(owner: Owner): void;
|
|
4310
4372
|
/**
|
|
4311
|
-
*
|
|
4312
|
-
* In mode `paused` or `fast` the rest pose is written at once. No-op for an entity that is not
|
|
4313
|
-
* a live view.
|
|
4373
|
+
* Reads a component value. A stale id or a missing component gives `undefined`.
|
|
4314
4374
|
*
|
|
4315
|
-
* @param entity - The entity
|
|
4375
|
+
* @param entity - The entity to read.
|
|
4376
|
+
* @param component - The component type.
|
|
4377
|
+
* @returns The stored value, read-only, or `undefined`.
|
|
4316
4378
|
* @example
|
|
4317
4379
|
* ```ts
|
|
4318
|
-
* // `input`
|
|
4380
|
+
* // `input` asks whether the entity under the finger may be dragged.
|
|
4319
4381
|
* const world = ctx.require(worldPlugin);
|
|
4320
|
-
* world.
|
|
4382
|
+
* world.ecs.get(hit, Draggable); // { payload: { id: "i7" } }
|
|
4383
|
+
* world.ecs.get(hit, DropTarget); // undefined: this item takes no drop
|
|
4321
4384
|
* ```
|
|
4322
4385
|
*/
|
|
4323
|
-
|
|
4386
|
+
get<Value extends object>(entity: Entity, component: ComponentHandle<Value>): Readonly<Value> | undefined;
|
|
4324
4387
|
/**
|
|
4325
|
-
*
|
|
4388
|
+
* Shallow-merges a patch into a stored component and marks the entity changed for it. Never
|
|
4389
|
+
* queued: a `set` inside a phase is visible to the next system of that phase.
|
|
4326
4390
|
*
|
|
4327
|
-
* @param entity - The entity
|
|
4328
|
-
* @param component - The component
|
|
4329
|
-
* @param
|
|
4330
|
-
* @
|
|
4391
|
+
* @param entity - The entity to write.
|
|
4392
|
+
* @param component - The component type.
|
|
4393
|
+
* @param patch - The fields to overwrite.
|
|
4394
|
+
* @throws {Error} When the entity is stale or does not carry the component.
|
|
4331
4395
|
* @example
|
|
4332
4396
|
* ```ts
|
|
4333
|
-
* // `input`
|
|
4397
|
+
* // `input` moves the held view with the finger.
|
|
4334
4398
|
* const world = ctx.require(worldPlugin);
|
|
4335
|
-
*
|
|
4336
|
-
*
|
|
4337
|
-
* release(); // the finger let go
|
|
4399
|
+
* world.ecs.set(held, Transform, { x: 420, y: 810 });
|
|
4400
|
+
* world.ecs.get(held, Transform); // { x: 420, y: 810, rotation: 0, scale: 1, pivot: { x: 0, y: 0 } }
|
|
4338
4401
|
* ```
|
|
4339
4402
|
*/
|
|
4340
|
-
|
|
4403
|
+
set<Value extends object>(entity: Entity, component: ComponentHandle<Value>, patch: Partial<Value>): void;
|
|
4341
4404
|
/**
|
|
4342
|
-
*
|
|
4343
|
-
*
|
|
4344
|
-
* board until it is home.
|
|
4405
|
+
* Adds a component value. On an entity that already carries it the value is replaced and the
|
|
4406
|
+
* entity is marked changed; no `onAdded` fires.
|
|
4345
4407
|
*
|
|
4346
|
-
* @param entity - The entity
|
|
4347
|
-
* @param
|
|
4408
|
+
* @param entity - The entity to write.
|
|
4409
|
+
* @param value - The component or tag value, built by calling its type.
|
|
4348
4410
|
* @example
|
|
4349
4411
|
* ```ts
|
|
4350
|
-
* // `
|
|
4412
|
+
* // `text` gives the label entity its display object once the atlas is there.
|
|
4351
4413
|
* const world = ctx.require(worldPlugin);
|
|
4352
|
-
* world.
|
|
4353
|
-
*
|
|
4354
|
-
* world.projection.lift(held, false); // on release: takes effect when the settle motion ends
|
|
4414
|
+
* world.ecs.add(label, Display({ object: bitmapText }));
|
|
4415
|
+
* world.ecs.has(label, Display); // true
|
|
4355
4416
|
* ```
|
|
4356
4417
|
*/
|
|
4357
|
-
|
|
4418
|
+
add(entity: Entity, value: AnyComponentValue): void;
|
|
4358
4419
|
/**
|
|
4359
|
-
*
|
|
4360
|
-
* element registered with `registerKey`.
|
|
4420
|
+
* Removes a component. Fires `onRemoved` when the entity carried it.
|
|
4361
4421
|
*
|
|
4362
|
-
* @param entity - The entity to
|
|
4363
|
-
* @
|
|
4422
|
+
* @param entity - The entity to write.
|
|
4423
|
+
* @param component - The component or tag type to drop.
|
|
4364
4424
|
* @example
|
|
4365
4425
|
* ```ts
|
|
4366
|
-
* // `
|
|
4367
|
-
*
|
|
4368
|
-
*
|
|
4426
|
+
* // `text` drops the display object when the label leaves the screen.
|
|
4427
|
+
* const world = ctx.require(worldPlugin);
|
|
4428
|
+
* world.ecs.remove(label, Display);
|
|
4429
|
+
* world.ecs.has(label, Display); // false
|
|
4369
4430
|
* ```
|
|
4370
4431
|
*/
|
|
4371
|
-
|
|
4432
|
+
remove(entity: Entity, component: AnyComponentType): void;
|
|
4372
4433
|
/**
|
|
4373
|
-
*
|
|
4374
|
-
* what `setRest` recorded for an element. Read-only and owner-free, so a plugin that owns no
|
|
4375
|
-
* entity can aim at one.
|
|
4434
|
+
* Tells whether an entity carries a component or tag. False for a stale id.
|
|
4376
4435
|
*
|
|
4377
|
-
* @param entity -
|
|
4378
|
-
* @param component - The component
|
|
4379
|
-
* @returns
|
|
4436
|
+
* @param entity - The entity to read.
|
|
4437
|
+
* @param component - The component or tag type.
|
|
4438
|
+
* @returns True when the entity carries it.
|
|
4380
4439
|
* @example
|
|
4381
4440
|
* ```ts
|
|
4382
|
-
* // `
|
|
4441
|
+
* // `input` never hit-tests a view that is on its way out.
|
|
4383
4442
|
* const world = ctx.require(worldPlugin);
|
|
4384
|
-
*
|
|
4385
|
-
*
|
|
4386
|
-
* world.projection.restOf(counter, Transform); // { x: 40, y: 120, rotation: 0, scale: 1, pivot: { x: 0, y: 0 } }
|
|
4443
|
+
* world.ecs.has(candidate, Exiting); // true while the merge animation plays
|
|
4387
4444
|
* ```
|
|
4388
4445
|
*/
|
|
4389
|
-
|
|
4446
|
+
has(entity: Entity, component: AnyComponentType): boolean;
|
|
4390
4447
|
/**
|
|
4391
|
-
*
|
|
4392
|
-
* a plugin above registered under that key.
|
|
4448
|
+
* Adds a tag. Structural and idempotent.
|
|
4393
4449
|
*
|
|
4394
|
-
* @param
|
|
4395
|
-
* @param
|
|
4396
|
-
* @returns The entity, or `undefined`.
|
|
4450
|
+
* @param entity - The entity to write.
|
|
4451
|
+
* @param tagType - The tag type.
|
|
4397
4452
|
* @example
|
|
4398
4453
|
* ```ts
|
|
4399
|
-
* // `input`
|
|
4454
|
+
* // `input` marks the view under the finger while the drag runs.
|
|
4400
4455
|
* const world = ctx.require(worldPlugin);
|
|
4401
|
-
* world.
|
|
4402
|
-
* world.
|
|
4456
|
+
* world.ecs.tag(dragged, Held);
|
|
4457
|
+
* world.ecs.has(dragged, Held); // true
|
|
4403
4458
|
* ```
|
|
4404
4459
|
*/
|
|
4405
|
-
|
|
4460
|
+
tag(entity: Entity, tagType: TagType): void;
|
|
4406
4461
|
/**
|
|
4407
|
-
*
|
|
4408
|
-
* plays its exit is left out, and so is an element registered with `registerKey`. Every call
|
|
4409
|
-
* hands out a new array.
|
|
4462
|
+
* Removes a tag. Structural and idempotent.
|
|
4410
4463
|
*
|
|
4411
|
-
* @param
|
|
4412
|
-
* @
|
|
4464
|
+
* @param entity - The entity to write.
|
|
4465
|
+
* @param tagType - The tag type.
|
|
4413
4466
|
* @example
|
|
4414
4467
|
* ```ts
|
|
4415
|
-
* // `
|
|
4468
|
+
* // `input` releases the view on pointer up.
|
|
4416
4469
|
* const world = ctx.require(worldPlugin);
|
|
4417
|
-
* world.
|
|
4418
|
-
* world.
|
|
4470
|
+
* world.ecs.untag(dragged, Held);
|
|
4471
|
+
* world.ecs.has(dragged, Held); // false
|
|
4419
4472
|
* ```
|
|
4420
4473
|
*/
|
|
4421
|
-
|
|
4474
|
+
untag(entity: Entity, tagType: TagType): void;
|
|
4422
4475
|
/**
|
|
4423
|
-
*
|
|
4424
|
-
*
|
|
4476
|
+
* Walks the store of the FIRST term in insertion order and keeps the entities that carry every
|
|
4477
|
+
* term. Deterministic. A `mut()` term marks every yielded entity changed for that component.
|
|
4425
4478
|
*
|
|
4426
|
-
* @param
|
|
4427
|
-
* @returns The
|
|
4479
|
+
* @param terms - Component types, tag types and `mut()` terms.
|
|
4480
|
+
* @returns The matching rows: the entity and one value per term.
|
|
4428
4481
|
* @example
|
|
4429
4482
|
* ```ts
|
|
4430
|
-
* // `
|
|
4431
|
-
* const
|
|
4432
|
-
*
|
|
4483
|
+
* // `renderer.sync` walks every sprite that has a place on the screen.
|
|
4484
|
+
* for (const [entity, sprite, transform] of app.world.ecs.query(Sprite, Transform)) {
|
|
4485
|
+
* display.get(entity)?.position.set(transform.x, transform.y);
|
|
4486
|
+
* sprite.texture; // "board.item-red-1"
|
|
4487
|
+
* }
|
|
4488
|
+
* ```
|
|
4489
|
+
*/
|
|
4490
|
+
query<const Terms extends readonly QueryTerm[]>(...terms: Terms): Iterable<QueryTuple<Terms>>;
|
|
4491
|
+
/**
|
|
4492
|
+
* Returns the one mutable value of a resource, created from a deep clone of its defaults on
|
|
4493
|
+
* first read.
|
|
4433
4494
|
*
|
|
4434
|
-
*
|
|
4495
|
+
* @param resourceType - The resource type.
|
|
4496
|
+
* @returns The mutable resource value.
|
|
4497
|
+
* @example
|
|
4498
|
+
* ```ts
|
|
4499
|
+
* // `input` writes the pointer once per frame; every game system reads the same object.
|
|
4500
|
+
* const world = ctx.require(worldPlugin);
|
|
4501
|
+
* const pointer = world.ecs.resource(Pointer);
|
|
4502
|
+
* pointer.down = true;
|
|
4435
4503
|
* ```
|
|
4436
4504
|
*/
|
|
4437
|
-
|
|
4505
|
+
resource<Value extends object>(resourceType: ResourceType<Value>): Value;
|
|
4438
4506
|
/**
|
|
4439
|
-
*
|
|
4440
|
-
*
|
|
4441
|
-
* what `setRest` recorded; `peer` answers `undefined`.
|
|
4507
|
+
* Registers a listener for the moment a component is added, called when the structural change
|
|
4508
|
+
* is applied. A throwing listener is logged and the others still run.
|
|
4442
4509
|
*
|
|
4443
|
-
* @param
|
|
4444
|
-
* @param
|
|
4445
|
-
* @returns The
|
|
4510
|
+
* @param component - The component type to watch.
|
|
4511
|
+
* @param fn - Called with the entity and the stored value.
|
|
4512
|
+
* @returns The remover; calling it twice is a no-op.
|
|
4446
4513
|
* @example
|
|
4447
4514
|
* ```ts
|
|
4448
|
-
* // `
|
|
4515
|
+
* // `renderer.sync` creates a Pixi object for every new sprite.
|
|
4449
4516
|
* const world = ctx.require(worldPlugin);
|
|
4450
|
-
* const
|
|
4517
|
+
* const off = world.ecs.onAdded(Sprite, (entity, sprite) => {
|
|
4518
|
+
* objects.set(entity, makeSprite(sprite.texture));
|
|
4519
|
+
* });
|
|
4451
4520
|
*
|
|
4452
|
-
*
|
|
4521
|
+
* off(); // the renderer stops
|
|
4453
4522
|
* ```
|
|
4454
4523
|
*/
|
|
4455
|
-
|
|
4524
|
+
onAdded<Value extends object>(component: ComponentHandle<Value>, fn: (entity: Entity, value: Readonly<Value>) => void): () => void;
|
|
4456
4525
|
/**
|
|
4457
|
-
*
|
|
4526
|
+
* Registers a listener for the moment a tag is added: `tag` on an entity without it, or a spawn
|
|
4527
|
+
* that carries it. A tag has no value, so the listener gets the entity only.
|
|
4458
4528
|
*
|
|
4459
|
-
* @param
|
|
4460
|
-
* @param
|
|
4461
|
-
* @
|
|
4529
|
+
* @param tagType - The tag type to watch.
|
|
4530
|
+
* @param fn - Called with the entity that got the tag.
|
|
4531
|
+
* @returns The remover; calling it twice is a no-op.
|
|
4462
4532
|
* @example
|
|
4463
4533
|
* ```ts
|
|
4464
|
-
* // `ui`
|
|
4534
|
+
* // `ui` shows the pressed look while `input` holds the `Pressed` tag on a button.
|
|
4465
4535
|
* const world = ctx.require(worldPlugin);
|
|
4466
|
-
* world.
|
|
4536
|
+
* const off = world.ecs.onAdded(Pressed, entity => markPointer(entity, "pressed", true));
|
|
4467
4537
|
*
|
|
4468
|
-
* world.
|
|
4538
|
+
* world.ecs.tag(button, Pressed); // markPointer(button, "pressed", true)
|
|
4539
|
+
* off(); // ui stops
|
|
4469
4540
|
* ```
|
|
4470
4541
|
*/
|
|
4471
|
-
|
|
4542
|
+
onAdded(tagType: TagType, fn: (entity: Entity) => void): () => void;
|
|
4472
4543
|
/**
|
|
4473
|
-
*
|
|
4474
|
-
* to the live views. That is how an element becomes a target for `anim`, `input` and `guide`.
|
|
4544
|
+
* Registers a listener for the moment a component is removed.
|
|
4475
4545
|
*
|
|
4476
|
-
* @param
|
|
4477
|
-
* @param
|
|
4478
|
-
* @
|
|
4479
|
-
* @returns The remover; it drops the key again.
|
|
4480
|
-
* @throws {Error} When a live view of that projection already holds the key.
|
|
4546
|
+
* @param component - The component type to watch.
|
|
4547
|
+
* @param fn - Called with the entity and the value it had.
|
|
4548
|
+
* @returns The remover; calling it twice is a no-op.
|
|
4481
4549
|
* @example
|
|
4482
4550
|
* ```ts
|
|
4483
|
-
* // `
|
|
4551
|
+
* // `renderer.sync` destroys the Pixi object of a sprite that left.
|
|
4484
4552
|
* const world = ctx.require(worldPlugin);
|
|
4485
|
-
* const
|
|
4553
|
+
* const off = world.ecs.onRemoved(Sprite, entity => {
|
|
4554
|
+
* objects.get(entity)?.destroy();
|
|
4555
|
+
* objects.delete(entity);
|
|
4556
|
+
* });
|
|
4486
4557
|
*
|
|
4487
|
-
*
|
|
4488
|
-
* drop(); // the element left the screen
|
|
4558
|
+
* off(); // the renderer stops
|
|
4489
4559
|
* ```
|
|
4490
4560
|
*/
|
|
4491
|
-
|
|
4561
|
+
onRemoved<Value extends object>(component: ComponentHandle<Value>, fn: (entity: Entity, value: Readonly<Value>) => void): () => void;
|
|
4492
4562
|
/**
|
|
4493
|
-
*
|
|
4494
|
-
*
|
|
4563
|
+
* Registers a listener for the moment a tag is removed: `untag` on an entity that had it, or a
|
|
4564
|
+
* despawn. The listener gets the entity only.
|
|
4495
4565
|
*
|
|
4566
|
+
* @param tagType - The tag type to watch.
|
|
4567
|
+
* @param fn - Called with the entity that lost the tag.
|
|
4568
|
+
* @returns The remover; calling it twice is a no-op.
|
|
4496
4569
|
* @example
|
|
4497
4570
|
* ```ts
|
|
4498
|
-
* // `
|
|
4571
|
+
* // `ui` drops the pressed look when `input` releases the button.
|
|
4499
4572
|
* const world = ctx.require(worldPlugin);
|
|
4500
|
-
* world.
|
|
4573
|
+
* const off = world.ecs.onRemoved(Pressed, entity => markPointer(entity, "pressed", false));
|
|
4574
|
+
*
|
|
4575
|
+
* world.ecs.untag(button, Pressed); // markPointer(button, "pressed", false)
|
|
4576
|
+
* off(); // ui stops
|
|
4501
4577
|
* ```
|
|
4502
4578
|
*/
|
|
4503
|
-
|
|
4504
|
-
};
|
|
4505
|
-
/**
|
|
4506
|
-
* projection methods the plugin root drives from the frame and the hooks. Not public.
|
|
4507
|
-
*/
|
|
4508
|
-
type ProjectionInternal = {
|
|
4579
|
+
onRemoved(tagType: TagType, fn: (entity: Entity) => void): () => void;
|
|
4509
4580
|
/**
|
|
4510
|
-
*
|
|
4581
|
+
* The coarse change set of the frame, cleared in `time` phase `signals` after every `sync`
|
|
4582
|
+
* system ran.
|
|
4511
4583
|
*
|
|
4512
|
-
* @param
|
|
4513
|
-
* @
|
|
4584
|
+
* @param component - The component type to ask about.
|
|
4585
|
+
* @returns The entities marked changed for it this frame.
|
|
4586
|
+
* @example
|
|
4587
|
+
* ```ts
|
|
4588
|
+
* // `renderer.sync` moves only what moved.
|
|
4589
|
+
* for (const entity of app.world.ecs.changed(Transform)) {
|
|
4590
|
+
* const transform = app.world.ecs.get(entity, Transform);
|
|
4591
|
+
* display.get(entity)?.position.set(transform?.x ?? 0, transform?.y ?? 0);
|
|
4592
|
+
* }
|
|
4593
|
+
* ```
|
|
4514
4594
|
*/
|
|
4515
|
-
|
|
4595
|
+
changed(component: AnyComponentType): Iterable<Entity>;
|
|
4516
4596
|
/**
|
|
4517
|
-
*
|
|
4597
|
+
* The effective mode: `"fast"` while the flow walks fast, otherwise the stored one.
|
|
4598
|
+
*
|
|
4599
|
+
* @returns The mode the frame runs in.
|
|
4600
|
+
* @example
|
|
4601
|
+
* ```ts
|
|
4602
|
+
* // A test checks that a fast walk froze the tweens.
|
|
4603
|
+
* app.flow.setMode("fast");
|
|
4604
|
+
* app.world.ecs.mode(); // "fast", even though setMode was never called on the world
|
|
4605
|
+
* ```
|
|
4518
4606
|
*/
|
|
4519
|
-
|
|
4607
|
+
mode(): WorldMode;
|
|
4520
4608
|
/**
|
|
4521
|
-
*
|
|
4609
|
+
* Stores the world mode. `"fast"` finishes every motion and flushes every despawn queue at once.
|
|
4610
|
+
*
|
|
4611
|
+
* @param mode - The new stored mode.
|
|
4612
|
+
* @example
|
|
4613
|
+
* ```ts
|
|
4614
|
+
* // A headless test wants the final picture without stepping frames.
|
|
4615
|
+
* app.world.ecs.setMode("fast");
|
|
4616
|
+
* app.world.ecs.mode(); // "fast"
|
|
4617
|
+
* ```
|
|
4522
4618
|
*/
|
|
4523
|
-
|
|
4619
|
+
setMode(mode: WorldMode): void;
|
|
4524
4620
|
/**
|
|
4525
|
-
*
|
|
4621
|
+
* The component type behind a name. A type registers itself on first use, so a name nobody
|
|
4622
|
+
* wrote, read or queried yet is unknown.
|
|
4526
4623
|
*
|
|
4527
|
-
* @param
|
|
4624
|
+
* @param name - The storage name of the component.
|
|
4625
|
+
* @returns The type, or `undefined` when the world never met it.
|
|
4626
|
+
* @example
|
|
4627
|
+
* ```ts
|
|
4628
|
+
* // `text` resolves the component a label binds its number to.
|
|
4629
|
+
* const world = ctx.require(worldPlugin);
|
|
4630
|
+
* const type = world.ecs.typeOf("Coins"); // the Coins component, registered when it was written
|
|
4631
|
+
* const pose = type === undefined ? undefined : world.ecs.get(counter, type);
|
|
4632
|
+
*
|
|
4633
|
+
* pose?.amount; // 120
|
|
4634
|
+
* ```
|
|
4528
4635
|
*/
|
|
4529
|
-
|
|
4636
|
+
typeOf(name: string): AnyComponent | undefined;
|
|
4530
4637
|
/**
|
|
4531
|
-
*
|
|
4532
|
-
*
|
|
4638
|
+
* The owner of an entity, which is also the liveness check: a stale id has none, also after its
|
|
4639
|
+
* index came back with a new generation. `projection` asks before it hands out a view handle for
|
|
4640
|
+
* an entity another plugin owns; `renderer` asks before it forgets the filters of an entity whose
|
|
4641
|
+
* visual left.
|
|
4642
|
+
*
|
|
4643
|
+
* @param entity - The entity to ask about.
|
|
4644
|
+
* @returns The owner, or `undefined` when the id is stale.
|
|
4645
|
+
* @example
|
|
4646
|
+
* ```ts
|
|
4647
|
+
* // renderer forgets the filter slots of an entity only after it despawned
|
|
4648
|
+
* const ecs = ctx.require(worldPlugin).ecs;
|
|
4649
|
+
* ecs.ownerOf(button); // { kind: "projection", name: "hud" }
|
|
4650
|
+
* ecs.despawn(button);
|
|
4651
|
+
* ecs.ownerOf(button); // undefined
|
|
4652
|
+
* ```
|
|
4533
4653
|
*/
|
|
4534
|
-
|
|
4654
|
+
ownerOf(entity: Entity): Owner | undefined;
|
|
4535
4655
|
/**
|
|
4536
|
-
*
|
|
4656
|
+
* The whole world as plain JSON, sorted by entity index. A component value that is not JSON is
|
|
4657
|
+
* left out and its name is listed in `skipped`.
|
|
4658
|
+
*
|
|
4659
|
+
* @returns The world as JSON: the mode, the entities and the resources.
|
|
4660
|
+
* @example
|
|
4661
|
+
* ```ts
|
|
4662
|
+
* // A test asserts what the board holds after the first reconcile.
|
|
4663
|
+
* app.world.ecs.snapshot();
|
|
4664
|
+
* // { mode: "live", entities: [{ id: 1048576, index: 0, generation: 1,
|
|
4665
|
+
* // owner: { kind: "projection", name: "board.items" },
|
|
4666
|
+
* // components: { Layer: { name: "items" } }, skipped: [] }], resources: {} }
|
|
4667
|
+
* ```
|
|
4537
4668
|
*/
|
|
4538
|
-
|
|
4669
|
+
snapshot(): WorldSnapshot;
|
|
4539
4670
|
/**
|
|
4540
|
-
*
|
|
4671
|
+
* What changed between the world at the end of frame `from` and the world at the end of frame
|
|
4672
|
+
* `to`: one entry per entity, sorted by id, with the first and the last value of every
|
|
4673
|
+
* component that really changed. A value that went and came back inside the window is left
|
|
4674
|
+
* out, and so is an entity spawned and despawned inside it. Dev builds only: the world keeps
|
|
4675
|
+
* the change records of the last 120 frames.
|
|
4541
4676
|
*
|
|
4542
|
-
* @param
|
|
4677
|
+
* @param from - The frame the window starts after.
|
|
4678
|
+
* @param to - The last frame of the window.
|
|
4679
|
+
* @returns The entities that changed, spawned or despawned in the window.
|
|
4680
|
+
* @throws {Error} In a production build, for a frame outside the last 120, and when `from` is
|
|
4681
|
+
* after `to`.
|
|
4682
|
+
* @example
|
|
4683
|
+
* ```ts
|
|
4684
|
+
* // The editor asks what the last second of play changed: item i7 merged up to level 3.
|
|
4685
|
+
* app.world.ecs.diff(120, 180);
|
|
4686
|
+
* // { from: 120, to: 180, entities: [{ id: 1048577,
|
|
4687
|
+
* // owner: { kind: "projection", name: "board.items" },
|
|
4688
|
+
* // key: { projection: "board.items", key: "i7" }, change: "changed",
|
|
4689
|
+
* // components: { Level: { from: { level: 2 }, to: { level: 3 } } } }] }
|
|
4690
|
+
* ```
|
|
4543
4691
|
*/
|
|
4544
|
-
|
|
4692
|
+
diff(from: number, to: number): FrameDiff;
|
|
4545
4693
|
/**
|
|
4546
|
-
*
|
|
4694
|
+
* Every component and tag type the world met, sorted by name: its kind, whether its defaults
|
|
4695
|
+
* are JSON, the JSON kind of each field, the defaults and the fields a plugin owns. A type
|
|
4696
|
+
* registers on first use, so the list is what the world has met so far, not what the game
|
|
4697
|
+
* declared. Every call builds a new list.
|
|
4698
|
+
*
|
|
4699
|
+
* @returns One entry per type.
|
|
4700
|
+
* @example
|
|
4701
|
+
* ```ts
|
|
4702
|
+
* // The editor's component palette shows the fields of Transform.
|
|
4703
|
+
* app.world.ecs.schema().find(entry => entry.name === "Transform");
|
|
4704
|
+
* // { name: "Transform", kind: "component", json: true,
|
|
4705
|
+
* // fields: { x: "number", y: "number", rotation: "number", scale: "number", pivot: "object" },
|
|
4706
|
+
* // defaults: { x: 0, y: 0, rotation: 0, scale: 1, pivot: { x: 0, y: 0 } }, owned: [] }
|
|
4707
|
+
* ```
|
|
4708
|
+
*/
|
|
4709
|
+
schema(): readonly ComponentSchema[];
|
|
4710
|
+
};
|
|
4711
|
+
/**
|
|
4712
|
+
* ecs methods the plugin root calls: frame phases, the owner listener, the clears. Not public.
|
|
4713
|
+
*/
|
|
4714
|
+
type EcsInternal = {
|
|
4715
|
+
/**
|
|
4716
|
+
* Registers a listener for `despawnOwnedBy`, so `projection` can drop the views of an owner
|
|
4717
|
+
* that left.
|
|
4718
|
+
*
|
|
4719
|
+
* @param fn - Called with the owner whose entities were despawned.
|
|
4720
|
+
* @returns The remover.
|
|
4721
|
+
*/
|
|
4722
|
+
onOwnerLeft(fn: (owner: Owner) => void): () => void;
|
|
4723
|
+
/**
|
|
4724
|
+
* Runs the systems of one phase, then flushes the command buffer.
|
|
4725
|
+
*
|
|
4726
|
+
* @param phase - The phase to run.
|
|
4727
|
+
* @param time - The frame's `Time`.
|
|
4728
|
+
*/
|
|
4729
|
+
runPhase(phase: WorldPhase, time: Readonly<Time>): void;
|
|
4730
|
+
/**
|
|
4731
|
+
* Applies the queued structural commands in call order.
|
|
4732
|
+
*/
|
|
4733
|
+
flush(): void;
|
|
4734
|
+
/**
|
|
4735
|
+
* Clears every change set. Called in `time` phase `signals`.
|
|
4736
|
+
*/
|
|
4737
|
+
clearChanges(): void;
|
|
4738
|
+
/**
|
|
4739
|
+
* Drops every entity, component, resource, system and hook.
|
|
4547
4740
|
*/
|
|
4548
4741
|
clear(): void;
|
|
4549
4742
|
};
|
|
4550
|
-
declare namespace types_d_exports$
|
|
4551
|
-
export { AnyComponentType, AnyComponentValue, AnyProjectionSpec, AnySystem, Api$
|
|
4743
|
+
declare namespace types_d_exports$2 {
|
|
4744
|
+
export { AnyComponentType, AnyComponentValue, AnyProjectionSpec, AnySystem, Api$2 as Api, Cause, ChangeHook, ComponentHandle, ComponentSchema, ComponentType, ComponentValue, Config$2 as Config, Deps$2 as Deps, DescriptionNode, Ease, EcsApi, EcsModule, Entity, EntityDiff, EntitySnapshot, Events$2 as Events, Explained, FrameDiff, KernelSlice$2 as KernelSlice, LayerSort, LayerSpec, ModelCommitted, Motion, MotionHandle, Mut, Narrowed, NumericFields, Owner, ProjectionApi, ProjectionKey, ProjectionModule, ProjectionMotion, ProjectionSpec, QueryTerm, QueryTuple, ResourceType, RestOptions, State$2 as State, SystemContext, SystemDefinition, TagType, TrackOptions, TrackSegment, TweenDriver, TweenOptions, ViewHandle, ViewOutput, WorldCtx, WorldMode, WorldPhase, WorldSnapshot };
|
|
4552
4745
|
}
|
|
4553
4746
|
/**
|
|
4554
4747
|
* world plugin events.
|
|
@@ -4562,7 +4755,7 @@ declare namespace types_d_exports$1 {
|
|
|
4562
4755
|
* }); // one merge logs { mode: "play", entered: 0, changed: 1, exited: 1, ... }
|
|
4563
4756
|
* ```
|
|
4564
4757
|
*/
|
|
4565
|
-
type Events$
|
|
4758
|
+
type Events$2 = {
|
|
4566
4759
|
/** Dev only (config `reconciledEvent`). Counts of one reconcile. */"world:reconciled": {
|
|
4567
4760
|
mode: "play" | "direct";
|
|
4568
4761
|
projections: number;
|
|
@@ -4583,14 +4776,14 @@ type Events$1 = {
|
|
|
4583
4776
|
* createApp({ pluginConfigs: { world: { settleMs: 250, reconciledEvent: true } } });
|
|
4584
4777
|
* ```
|
|
4585
4778
|
*/
|
|
4586
|
-
type Config$
|
|
4779
|
+
type Config$2 = {
|
|
4587
4780
|
/** Duration of the built-in settle motion, in game milliseconds. */settleMs: number; /** Emit `world:reconciled` after every reconcile. Off in production; tests and the editor turn it on. */
|
|
4588
4781
|
reconciledEvent: boolean;
|
|
4589
4782
|
};
|
|
4590
4783
|
/**
|
|
4591
4784
|
* world plugin state: one branch per module.
|
|
4592
4785
|
*/
|
|
4593
|
-
type State$
|
|
4786
|
+
type State$2 = {
|
|
4594
4787
|
ecs: EcsState;
|
|
4595
4788
|
projection: ProjectionState;
|
|
4596
4789
|
};
|
|
@@ -4603,17 +4796,17 @@ type State$1 = {
|
|
|
4603
4796
|
* app.world.projection.entityOf("board.items", "i7"); // 1048576
|
|
4604
4797
|
* ```
|
|
4605
4798
|
*/
|
|
4606
|
-
type Api$
|
|
4799
|
+
type Api$2 = {
|
|
4607
4800
|
ecs: EcsApi;
|
|
4608
4801
|
projection: ProjectionApi;
|
|
4609
4802
|
};
|
|
4610
4803
|
/**
|
|
4611
4804
|
* Resolved dependency APIs.
|
|
4612
4805
|
*/
|
|
4613
|
-
type Deps$
|
|
4614
|
-
time: Api$
|
|
4615
|
-
model: Api$
|
|
4616
|
-
flow: Api$
|
|
4806
|
+
type Deps$2 = {
|
|
4807
|
+
time: Api$4;
|
|
4808
|
+
model: Api$5;
|
|
4809
|
+
flow: Api$3;
|
|
4617
4810
|
};
|
|
4618
4811
|
/**
|
|
4619
4812
|
* What the kernel context offers before the deps are attached.
|
|
@@ -4622,7 +4815,7 @@ type Deps$1 = {
|
|
|
4622
4815
|
* row 8), so the own event reaches the context the kernel hands the factories and `emit` is the
|
|
4623
4816
|
* kernel's own, world-typed one. No member of the context is cast.
|
|
4624
4817
|
*/
|
|
4625
|
-
type KernelSlice$
|
|
4818
|
+
type KernelSlice$2 = PluginCtx<Config$2, State$2, Events$2> & {
|
|
4626
4819
|
readonly global: object;
|
|
4627
4820
|
readonly log: Log.LogApi;
|
|
4628
4821
|
readonly require: Require;
|
|
@@ -4630,13 +4823,37 @@ type KernelSlice$1 = PluginCtx<Config$1, State$1, Events$1> & {
|
|
|
4630
4823
|
/**
|
|
4631
4824
|
* Domain context shared by the two modules.
|
|
4632
4825
|
*/
|
|
4633
|
-
type WorldCtx = KernelSlice$
|
|
4634
|
-
readonly deps: Deps$
|
|
4826
|
+
type WorldCtx = KernelSlice$2 & {
|
|
4827
|
+
readonly deps: Deps$2;
|
|
4635
4828
|
};
|
|
4636
4829
|
/**
|
|
4637
4830
|
* Payload of the one event world listens to.
|
|
4638
4831
|
*/
|
|
4639
|
-
type ModelCommitted = Events$
|
|
4832
|
+
type ModelCommitted = Events$4["model:committed"];
|
|
4833
|
+
/**
|
|
4834
|
+
* One entity in full, as the `/inspect` source `game.explain` reads it: its row of
|
|
4835
|
+
* `ecs.snapshot()`, its projection key and the components a motion still drives.
|
|
4836
|
+
*
|
|
4837
|
+
* @example
|
|
4838
|
+
* ```ts
|
|
4839
|
+
* const sliding: Explained = {
|
|
4840
|
+
* id: 1_048_577,
|
|
4841
|
+
* owner: { kind: "projection", name: "board.items" },
|
|
4842
|
+
* key: { projection: "board.items", key: "i7" },
|
|
4843
|
+
* components: { Layer: { name: "items" } },
|
|
4844
|
+
* skipped: ["Display"],
|
|
4845
|
+
* motions: ["Transform"]
|
|
4846
|
+
* };
|
|
4847
|
+
* ```
|
|
4848
|
+
*/
|
|
4849
|
+
type Explained = {
|
|
4850
|
+
id: Entity;
|
|
4851
|
+
owner: Owner; /** `projection.keyOf`: the view address, also for an element registered under a key. */
|
|
4852
|
+
key: ProjectionKey | undefined; /** The JSON components, as `EntitySnapshot.components`. */
|
|
4853
|
+
components: Record<string, Json>; /** The names of the components that are not JSON, as `EntitySnapshot.skipped`. */
|
|
4854
|
+
skipped: string[]; /** `projection.motionsOf`: the components a running track drives, in start order. */
|
|
4855
|
+
motions: readonly string[];
|
|
4856
|
+
};
|
|
4640
4857
|
/**
|
|
4641
4858
|
* Both halves of the `ecs` module: what a game calls and what `projection` gets injected.
|
|
4642
4859
|
*/
|
|
@@ -4833,11 +5050,21 @@ type HostInternal = {
|
|
|
4833
5050
|
*/
|
|
4834
5051
|
render(): void;
|
|
4835
5052
|
/**
|
|
4836
|
-
* Draws the stage into a PNG over the whole canvas.
|
|
5053
|
+
* Draws the stage into a PNG over the whole canvas. Every container in `hidden` is set
|
|
5054
|
+
* `visible = false` around the synchronous render inside Pixi's extract and set back right after
|
|
5055
|
+
* that call returns its promise, so no drawn frame sees it hidden. A failed read is logged.
|
|
4837
5056
|
*
|
|
5057
|
+
* @param hidden - The layer containers to leave out of the picture; `[]` for the whole stage.
|
|
4838
5058
|
* @returns A PNG data URL, or `undefined` while nothing draws or when Pixi could not read it.
|
|
4839
5059
|
*/
|
|
4840
|
-
extract(): Promise<string | undefined>;
|
|
5060
|
+
extract(hidden: readonly PixiContainer[]): Promise<string | undefined>;
|
|
5061
|
+
/**
|
|
5062
|
+
* The resolution the application draws at: device pixels per CSS pixel, capped by
|
|
5063
|
+
* `maxResolution`. A picture of the canvas is its CSS size times this.
|
|
5064
|
+
*
|
|
5065
|
+
* @returns The resolution; 1 while inert.
|
|
5066
|
+
*/
|
|
5067
|
+
resolution(): number;
|
|
4841
5068
|
/**
|
|
4842
5069
|
* The texture sources the GPU holds and their estimated memory, read at call time.
|
|
4843
5070
|
*
|
|
@@ -4862,7 +5089,7 @@ type HostInternal = {
|
|
|
4862
5089
|
* const orientation: Orientation = "portrait";
|
|
4863
5090
|
* ```
|
|
4864
5091
|
*/
|
|
4865
|
-
type Orientation = Config$
|
|
5092
|
+
type Orientation = Config$8["orientation"];
|
|
4866
5093
|
/**
|
|
4867
5094
|
* A point in reference units.
|
|
4868
5095
|
*
|
|
@@ -4980,10 +5207,9 @@ type ViewportApi = {
|
|
|
4980
5207
|
* units, since there is no canvas to place them on.
|
|
4981
5208
|
* @example
|
|
4982
5209
|
* ```ts
|
|
4983
|
-
* // The `game.
|
|
4984
|
-
* // phone, the canvas at the top left: the scale is 390 / 1080.
|
|
4985
|
-
*
|
|
4986
|
-
* renderer.viewport.toScreen({ x: 540, y: 960 }); // { x: 195, y: 346.666… }
|
|
5210
|
+
* // The `game.locate` source places Home's Play plank on the page for an e2e script. A
|
|
5211
|
+
* // 390x844 phone, the canvas at the top left: the scale is 390 / 1080.
|
|
5212
|
+
* app.renderer.viewport.toScreen({ x: 540, y: 960 }); // { x: 195, y: 346.666… }
|
|
4987
5213
|
* ```
|
|
4988
5214
|
*/
|
|
4989
5215
|
toScreen(point: Point): Point;
|
|
@@ -5079,7 +5305,14 @@ type DisplayEntry = {
|
|
|
5079
5305
|
removers: Array<() => void>;
|
|
5080
5306
|
};
|
|
5081
5307
|
/**
|
|
5082
|
-
*
|
|
5308
|
+
* A rectangle: the one a hit test checks, in the local space of the view with the anchor already
|
|
5309
|
+
* applied, or the bounds of a view in reference units.
|
|
5310
|
+
*
|
|
5311
|
+
* @example
|
|
5312
|
+
* ```ts
|
|
5313
|
+
* // A 64 x 64 sprite with the default anchor, in its own units.
|
|
5314
|
+
* const box: HitBox = { x: -32, y: -32, width: 64, height: 64 };
|
|
5315
|
+
* ```
|
|
5083
5316
|
*/
|
|
5084
5317
|
type HitBox = {
|
|
5085
5318
|
x: number;
|
|
@@ -5149,7 +5382,7 @@ type TextureProvider = (key: string) => PixiTexture | undefined;
|
|
|
5149
5382
|
* const borders: NineBorders = [12, 12, 12, 12];
|
|
5150
5383
|
* ```
|
|
5151
5384
|
*/
|
|
5152
|
-
type NineBorders = readonly [number, number, number, number];
|
|
5385
|
+
type NineBorders$1 = readonly [number, number, number, number];
|
|
5153
5386
|
/**
|
|
5154
5387
|
* Options of `textures.create`.
|
|
5155
5388
|
*
|
|
@@ -5158,8 +5391,8 @@ type NineBorders = readonly [number, number, number, number];
|
|
|
5158
5391
|
* const options: CreateTextureOptions = { nine: [12, 12, 12, 12] };
|
|
5159
5392
|
* ```
|
|
5160
5393
|
*/
|
|
5161
|
-
type CreateTextureOptions = {
|
|
5162
|
-
nine?: NineBorders;
|
|
5394
|
+
type CreateTextureOptions$1 = {
|
|
5395
|
+
nine?: NineBorders$1;
|
|
5163
5396
|
};
|
|
5164
5397
|
/**
|
|
5165
5398
|
* Where `textures.slice` cuts a texture out of an atlas page, in page pixels. The manifest `atlas`
|
|
@@ -5221,7 +5454,7 @@ type TexturesApi = {
|
|
|
5221
5454
|
* const texture = renderer.sync.textures.create(bitmap, { nine: [12, 12, 12, 12] });
|
|
5222
5455
|
* ```
|
|
5223
5456
|
*/
|
|
5224
|
-
create(image: ImageBitmap | HTMLImageElement, options?: CreateTextureOptions): PixiTexture;
|
|
5457
|
+
create(image: ImageBitmap | HTMLImageElement, options?: CreateTextureOptions$1): PixiTexture;
|
|
5225
5458
|
/**
|
|
5226
5459
|
* Cuts a texture out of an atlas page, so `assets` never imports Pixi. The slice shares the
|
|
5227
5460
|
* page's source: no pixel is copied, and the page keeps the GPU memory. A frame is measured
|
|
@@ -5244,7 +5477,7 @@ type TexturesApi = {
|
|
|
5244
5477
|
* renderer.sync.textures.destroy(button); // the wrapper goes, the page's source stays
|
|
5245
5478
|
* ```
|
|
5246
5479
|
*/
|
|
5247
|
-
slice(page: PixiTexture, frame: SliceFrame, options?: CreateTextureOptions): PixiTexture;
|
|
5480
|
+
slice(page: PixiTexture, frame: SliceFrame, options?: CreateTextureOptions$1): PixiTexture;
|
|
5248
5481
|
/**
|
|
5249
5482
|
* Destroys a texture and its source. A slice loses only its wrapper: the source belongs to its
|
|
5250
5483
|
* page, which frees it. The crops of `"cover"` sprites cut from the texture go with it.
|
|
@@ -5483,6 +5716,54 @@ type SyncApi = {
|
|
|
5483
5716
|
* ```
|
|
5484
5717
|
*/
|
|
5485
5718
|
hitTest(x: number, y: number, accept: (entity: Entity) => boolean): Entity | undefined;
|
|
5719
|
+
/**
|
|
5720
|
+
* Every entity whose hit box holds the point, topmost first: the walk and the skip rules of
|
|
5721
|
+
* `hitTest` (a hidden or fully transparent view, a point outside a clipping ancestor), with no
|
|
5722
|
+
* filter, so the caller filters. `hitTest` stays the fast path of a pointer event: it stops at
|
|
5723
|
+
* the first accepted hit.
|
|
5724
|
+
*
|
|
5725
|
+
* @param x - Reference x.
|
|
5726
|
+
* @param y - Reference y.
|
|
5727
|
+
* @returns The entities, topmost first, as a fresh array. Inert, and where nothing is drawn: `[]`.
|
|
5728
|
+
* @example
|
|
5729
|
+
* ```ts
|
|
5730
|
+
* // The `game.at` door lists what lies under a point: a coin drawn over its board cell.
|
|
5731
|
+
* app.renderer.sync.hitAll(540, 300); // [1048581, 1048576]
|
|
5732
|
+
* app.renderer.sync.hitAll(20, 20); // []: nothing is drawn there
|
|
5733
|
+
* ```
|
|
5734
|
+
*/
|
|
5735
|
+
hitAll(x: number, y: number): readonly Entity[];
|
|
5736
|
+
/**
|
|
5737
|
+
* Where a view is drawn, in reference units: the axis-aligned bounds of the four corners of its
|
|
5738
|
+
* hit box brought through its `Transform` and its `Parent` chain, rotation and scale included.
|
|
5739
|
+
* Only a view the player can see has bounds, so a located element is one a click reaches.
|
|
5740
|
+
*
|
|
5741
|
+
* @param entity - The entity to place.
|
|
5742
|
+
* @returns The bounds, a fresh object. `undefined` for an entity without a view, a view that
|
|
5743
|
+
* hangs in no tree, a view hidden or with an effective alpha of 0.01 or less on the way to the
|
|
5744
|
+
* root, and bounds without area. Inert: always `undefined`.
|
|
5745
|
+
* @example
|
|
5746
|
+
* ```ts
|
|
5747
|
+
* // The `game.locate` door finds a 64 x 64 coin drawn at (540, 300) with the default anchor.
|
|
5748
|
+
* app.renderer.sync.boundsOf(coin); // { x: 508, y: 268, width: 64, height: 64 }
|
|
5749
|
+
* ```
|
|
5750
|
+
*/
|
|
5751
|
+
boundsOf(entity: Entity): HitBox | undefined;
|
|
5752
|
+
/**
|
|
5753
|
+
* The very box `hitTest` tests for a view, in the entity's own units: the anchor applied, before
|
|
5754
|
+
* the `Transform` and the `Parent` chain, so no rotation. `boundsOf` is this box brought to
|
|
5755
|
+
* reference units.
|
|
5756
|
+
*
|
|
5757
|
+
* @param entity - The entity to ask about.
|
|
5758
|
+
* @returns The box, a fresh object, or `undefined` for an entity without a view.
|
|
5759
|
+
* @example
|
|
5760
|
+
* ```ts
|
|
5761
|
+
* // `input` fits the hit circle of a trace inside a cell: a 64 x 64 Sprite, default anchor.
|
|
5762
|
+
* const renderer = ctx.require(rendererPlugin);
|
|
5763
|
+
* renderer.sync.hitBoxOf(cell); // { x: -32, y: -32, width: 64, height: 64 }
|
|
5764
|
+
* ```
|
|
5765
|
+
*/
|
|
5766
|
+
hitBoxOf(entity: Entity): HitBox | undefined;
|
|
5486
5767
|
/**
|
|
5487
5768
|
* The texture registry `assets` drives.
|
|
5488
5769
|
*
|
|
@@ -5603,6 +5884,21 @@ type SyncInternal = {
|
|
|
5603
5884
|
* @returns The views of entities and the objects waiting in the pools.
|
|
5604
5885
|
*/
|
|
5605
5886
|
counts(): SyncCounts;
|
|
5887
|
+
/**
|
|
5888
|
+
* The container of one layer of the scene, so `monitor` can leave a layer out of a picture.
|
|
5889
|
+
*
|
|
5890
|
+
* @param name - Layer name.
|
|
5891
|
+
* @returns The container, or `undefined` for a name the scene does not declare and while inert.
|
|
5892
|
+
*/
|
|
5893
|
+
layerContainer(name: string): PixiContainer | undefined;
|
|
5894
|
+
/**
|
|
5895
|
+
* The layer an entity is drawn in: the `Layer` of the root of its `Parent` chain, since a child
|
|
5896
|
+
* draws with its parent. An entity without a parent names its own.
|
|
5897
|
+
*
|
|
5898
|
+
* @param entity - The entity.
|
|
5899
|
+
* @returns The layer name, or `undefined` when the root of the chain has no `Layer`.
|
|
5900
|
+
*/
|
|
5901
|
+
layerOf(entity: Entity): string | undefined;
|
|
5606
5902
|
};
|
|
5607
5903
|
/**
|
|
5608
5904
|
* The display objects `sync` holds: views of entities and pooled objects.
|
|
@@ -5703,7 +5999,7 @@ type CountingClasses = {
|
|
|
5703
5999
|
};
|
|
5704
6000
|
/**
|
|
5705
6001
|
* monitor module state: the open measuring window, the last closed one, and the captures that
|
|
5706
|
-
* wait for a frame.
|
|
6002
|
+
* wait for a drawn frame.
|
|
5707
6003
|
*/
|
|
5708
6004
|
type MonitorState = {
|
|
5709
6005
|
/** Clock time at the start of the frame being drawn; `undefined` between frames. */frameStart: number | undefined; /** Clock time at the start of the last frame, the far end of the next interval. */
|
|
@@ -5713,8 +6009,8 @@ type MonitorState = {
|
|
|
5713
6009
|
workMs: number; /** Frames finished in the open window. */
|
|
5714
6010
|
frames: number; /** Frames per second of the last closed window. */
|
|
5715
6011
|
fps: number; /** Mean frame work of the last closed window, in milliseconds. */
|
|
5716
|
-
frameMs: number; /**
|
|
5717
|
-
captures:
|
|
6012
|
+
frameMs: number; /** Captures waiting for a drawn frame, served in `end()`. */
|
|
6013
|
+
captures: PictureRequest[]; /** The draws the dev counter saw; stays at 0 in a production build. */
|
|
5718
6014
|
draws: DrawCounter;
|
|
5719
6015
|
};
|
|
5720
6016
|
/**
|
|
@@ -5750,17 +6046,43 @@ type MonitorApi = {
|
|
|
5750
6046
|
* picture shows the state the game just reached; at once while the clock is paused, since no
|
|
5751
6047
|
* frame comes then. Dev builds only: `__MOKU_GAME_DEV__` must be `true`.
|
|
5752
6048
|
*
|
|
5753
|
-
*
|
|
5754
|
-
*
|
|
6049
|
+
* The options draw on the picture after the extract, on an `OffscreenCanvas`; the game never
|
|
6050
|
+
* shows any of it. `layers` hides the other layer containers for the extract alone. `legend`
|
|
6051
|
+
* numbers every keyed view the player can see with a badge at the top-left corner of its rect,
|
|
6052
|
+
* sorted by `rect.y` then `rect.x`, and lists them. `sheet` takes `frames` pictures `everyMs` of
|
|
6053
|
+
* game time apart (while the clock is paused, by one `time.step(everyMs)` each) and lays them out
|
|
6054
|
+
* in `ceil(sqrt(frames))` columns. `against` answers the pixel diff: red where a channel differs
|
|
6055
|
+
* by more than 24, the current picture faded to grey elsewhere. `legend` combines with `against`;
|
|
6056
|
+
* `sheet` combines with `layers` only.
|
|
6057
|
+
*
|
|
6058
|
+
* @param options - What to draw on the picture; a plain picture when left out.
|
|
6059
|
+
* @returns `{ png }`, with `legend` when asked for. `undefined` in a production build, while
|
|
6060
|
+
* inert, lost or unsupported, and when Pixi could not read the frame (logged with
|
|
6061
|
+
* `ctx.log.error`).
|
|
6062
|
+
* @throws {Error} When a layer is not in the scene or the list is empty, when a sheet is out of
|
|
6063
|
+
* 2 to 12 frames every 1 to 5000 ms or comes with `legend` or `against`, when there is no
|
|
6064
|
+
* `OffscreenCanvas` for an option, when the two pictures of `against` differ in size, and when
|
|
6065
|
+
* game time stood still for 600 drawn frames of a sheet.
|
|
5755
6066
|
* @example
|
|
5756
6067
|
* ```ts
|
|
5757
6068
|
* // The editor's capture button, on the dev page that set globalThis.__MOKU_GAME_DEV__ = true.
|
|
5758
|
-
*
|
|
6069
|
+
* await app.renderer.capture(); // { png: "data:image/png;base64,iVBORw0KGgo…" }
|
|
5759
6070
|
* // The same call in a production build.
|
|
5760
6071
|
* await app.renderer.capture(); // undefined
|
|
5761
6072
|
* ```
|
|
6073
|
+
* @example
|
|
6074
|
+
* ```ts
|
|
6075
|
+
* // An agent finds the claim button of the HUD on a 1080 x 1920 canvas drawn at resolution 2.
|
|
6076
|
+
* await app.renderer.capture({ legend: true });
|
|
6077
|
+
* // { png: "data:image/png;base64,…", legend: [{ n: 1, projection: "hud", key: "claim", rect: { x: 1140, y: 2310, w: 567, h: 164 } }] }
|
|
6078
|
+
* ```
|
|
6079
|
+
* @example
|
|
6080
|
+
* ```ts
|
|
6081
|
+
* // The coin flight of a merge, six frames 100 ms apart: 3 columns and 2 rows on one picture.
|
|
6082
|
+
* await app.renderer.capture({ sheet: { frames: 6, everyMs: 100 } }); // { png: "data:image/png;base64,…" }
|
|
6083
|
+
* ```
|
|
5762
6084
|
*/
|
|
5763
|
-
capture(): Promise<
|
|
6085
|
+
capture(options?: CaptureOptions): Promise<Captured | undefined>;
|
|
5764
6086
|
};
|
|
5765
6087
|
/**
|
|
5766
6088
|
* monitor methods the frame drives. Not public.
|
|
@@ -5778,222 +6100,1049 @@ type MonitorInternal = {
|
|
|
5778
6100
|
end(): void;
|
|
5779
6101
|
};
|
|
5780
6102
|
/**
|
|
5781
|
-
* What `monitor` gets injected: the modules it reads its counters from.
|
|
6103
|
+
* What `monitor` gets injected: the modules it reads its counters from and places a legend with.
|
|
6104
|
+
*/
|
|
6105
|
+
type MonitorDeps = {
|
|
6106
|
+
host: HostApi & HostInternal;
|
|
6107
|
+
viewport: ViewportApi & ViewportInternal;
|
|
6108
|
+
sync: SyncApi & SyncInternal;
|
|
6109
|
+
};
|
|
6110
|
+
/**
|
|
6111
|
+
* A rectangle in picture pixels, from the top-left corner of the PNG: CSS pixels of the canvas
|
|
6112
|
+
* times the resolution, so an agent draws on the picture with it.
|
|
6113
|
+
*
|
|
6114
|
+
* @example
|
|
6115
|
+
* ```ts
|
|
6116
|
+
* // The claim button of the HUD on a 1080 x 1920 canvas drawn at resolution 2.
|
|
6117
|
+
* const rect: PictureRect = { x: 1140, y: 2310, w: 567, h: 164 };
|
|
6118
|
+
* ```
|
|
6119
|
+
*/
|
|
6120
|
+
type PictureRect = {
|
|
6121
|
+
x: number;
|
|
6122
|
+
y: number;
|
|
6123
|
+
w: number;
|
|
6124
|
+
h: number;
|
|
6125
|
+
};
|
|
6126
|
+
/**
|
|
6127
|
+
* One numbered view of a legend: the number on its badge, the projection and key that address
|
|
6128
|
+
* it, and its rect on the picture.
|
|
6129
|
+
*
|
|
6130
|
+
* @example
|
|
6131
|
+
* ```ts
|
|
6132
|
+
* const entry: LegendEntry = {
|
|
6133
|
+
* n: 1, projection: "hud", key: "claim", rect: { x: 1140, y: 2310, w: 567, h: 164 }
|
|
6134
|
+
* };
|
|
6135
|
+
* ```
|
|
6136
|
+
*/
|
|
6137
|
+
type LegendEntry = {
|
|
6138
|
+
n: number;
|
|
6139
|
+
projection: string;
|
|
6140
|
+
key: string;
|
|
6141
|
+
rect: PictureRect;
|
|
6142
|
+
};
|
|
6143
|
+
/**
|
|
6144
|
+
* What `capture()` draws on the picture. Every field is optional; none gives a plain picture.
|
|
6145
|
+
*
|
|
6146
|
+
* @example
|
|
6147
|
+
* ```ts
|
|
6148
|
+
* // The numbered board without the HUD over it.
|
|
6149
|
+
* const options: CaptureOptions = { legend: true, layers: ["board", "items"] };
|
|
6150
|
+
* // Six frames of a motion, 100 ms of game time apart.
|
|
6151
|
+
* const sheet: CaptureOptions = { sheet: { frames: 6, everyMs: 100 } };
|
|
6152
|
+
* ```
|
|
6153
|
+
*/
|
|
6154
|
+
type CaptureOptions = {
|
|
6155
|
+
/** Number every keyed view on the picture and list them in `legend` of the answer. */legend?: boolean; /** Draw only these layers of the scene; a `Parent` child follows its parent's layer. */
|
|
6156
|
+
layers?: readonly string[]; /** A contact sheet: 2 to 12 frames, every 1 to 5000 ms of game time. */
|
|
6157
|
+
sheet?: {
|
|
6158
|
+
frames: number;
|
|
6159
|
+
everyMs: number;
|
|
6160
|
+
}; /** A PNG data URL of an earlier capture: the answer is the pixel diff against it. */
|
|
6161
|
+
against?: string;
|
|
6162
|
+
};
|
|
6163
|
+
/**
|
|
6164
|
+
* What `capture()` answers: the picture, and the legend when it was asked for.
|
|
6165
|
+
*
|
|
6166
|
+
* @example
|
|
6167
|
+
* ```ts
|
|
6168
|
+
* const plain: Captured = { png: "data:image/png;base64,iVBORw0KGgo=" };
|
|
6169
|
+
* const numbered: Captured = {
|
|
6170
|
+
* png: "data:image/png;base64,iVBORw0KGgo=",
|
|
6171
|
+
* legend: [{ n: 1, projection: "hud", key: "claim", rect: { x: 1140, y: 2310, w: 567, h: 164 } }]
|
|
6172
|
+
* };
|
|
6173
|
+
* ```
|
|
6174
|
+
*/
|
|
6175
|
+
type Captured = {
|
|
6176
|
+
png: string;
|
|
6177
|
+
legend?: readonly LegendEntry[];
|
|
6178
|
+
};
|
|
6179
|
+
/**
|
|
6180
|
+
* The picture of one drawn frame, the legend measured on that same frame when it was asked for,
|
|
6181
|
+
* and the game time of the frame. Not public.
|
|
6182
|
+
*/
|
|
6183
|
+
type Shot = {
|
|
6184
|
+
png: string;
|
|
6185
|
+
legend: LegendEntry[] | undefined;
|
|
6186
|
+
elapsed: number;
|
|
6187
|
+
};
|
|
6188
|
+
/**
|
|
6189
|
+
* A capture waiting for a drawn frame. Not public.
|
|
6190
|
+
*/
|
|
6191
|
+
type PictureRequest = {
|
|
6192
|
+
/** The layers to draw; every layer when `undefined`. */layers: readonly string[] | undefined; /** True to measure the legend on the frame of the picture. */
|
|
6193
|
+
legend: boolean; /** True when the frame just drawn is the one to take. A plain capture takes the next one. */
|
|
6194
|
+
due: () => boolean; /** Takes the shot instead when the game pauses before that frame came: at once, or stepped. */
|
|
6195
|
+
whilePaused: () => Promise<Shot | undefined>; /** Drawn frames that went by while `due` said no. */
|
|
6196
|
+
waited: number; /** Hands the shot over; nothing when the renderer stopped first. */
|
|
6197
|
+
resolve: (shot?: Shot) => void; /** Hands over why the shot cannot come. */
|
|
6198
|
+
reject: (error: Error) => void;
|
|
6199
|
+
};
|
|
6200
|
+
declare namespace types_d_exports$1 {
|
|
6201
|
+
export { Api$1 as Api, AspectRange, BitmapFontData, CaptureOptions, Captured, Config$1 as Config, CountingClasses, CoverFrame, CreateTextureOptions$1 as CreateTextureOptions, DebugApi, DebugSwitches, Deps$1 as Deps, DisplayAdapter, DisplayEntry, DisplaysApi, DrawCounter, Events$1 as Events, FilterSlot, FiltersApi, FontsApi, HitBox, HostApi, HostInternal, HostModule, HostState, KernelSlice$1 as KernelSlice, LayerEntry, LegendEntry, Modules, MonitorApi, MonitorDeps, MonitorInternal, MonitorModule, MonitorState, NineBorders$1 as NineBorders, Orientation, PictureRect, PixiApplication, PixiBitmapFont, PixiContainer, PixiFilter, PixiGraphics, PixiModule, PixiNineSliceSprite, PixiSprite, PixiTexture, Point, Rect, RenderStats, RendererCtx, RendererKind, SafeArea, SliceFrame, State$1 as State, SyncApi, SyncCounts, SyncInternal, SyncModule, SyncState, TeardownScope, TextureProvider, TextureUsage, TexturesApi, Under, View, ViewKind, ViewportApi, ViewportInternal, ViewportModule, ViewportSize, ViewportState };
|
|
6202
|
+
}
|
|
6203
|
+
/**
|
|
6204
|
+
* The part of Pixi the engine uses: the classes `sync` builds views from (a `Rectangle` frames the
|
|
6205
|
+
* crop of a `"cover"` sprite), the bitmap-font pieces `fonts.install` needs, the `BitmapText` a
|
|
6206
|
+
* plugin above builds through `host.pixi()`, the filter and particle classes `effects` builds
|
|
6207
|
+
* through it (a custom filter carries a `GpuProgram` and a `GlProgram` over `defaultFilterVert`),
|
|
6208
|
+
* and the extension registry with the three WebGPU draw classes the dev draw-call
|
|
6209
|
+
* counter swaps and the bitmap text pipe `host` swaps for one whose SDF shader applies the alpha
|
|
6210
|
+
* once (with the WGSL and GLSL shader pieces it compiles that shader from). The module object
|
|
6211
|
+
* never arrives through a static import: `config.loadPixi()` returns it, so a game without a screen carries no
|
|
6212
|
+
* Pixi in its bundle.
|
|
6213
|
+
*
|
|
6214
|
+
* @example
|
|
6215
|
+
* ```ts
|
|
6216
|
+
* const pixi: PixiModule = await import("pixi.js");
|
|
6217
|
+
* pixi.Texture.WHITE.width; // 1
|
|
6218
|
+
* pixi.GpuBatchAdaptor.extension.name; // "batch"
|
|
6219
|
+
* pixi.BitmapTextPipe.extension.name; // "bitmapText"
|
|
6220
|
+
* pixi.defaultFilterVert.startsWith("in vec2 aPosition;"); // true
|
|
6221
|
+
* ```
|
|
6222
|
+
*/
|
|
6223
|
+
type PixiModule = Pick<typeof import("pixi.js"), "AlphaFilter" | "Application" | "BitmapFont" | "BitmapText" | "BitmapTextPipe" | "BlurFilter" | "Cache" | "ColorMatrixFilter" | "Container" | "DisplacementFilter" | "Filter" | "GlProgram" | "GpuBatchAdaptor" | "GpuEncoderSystem" | "GpuGraphicsAdaptor" | "GpuProgram" | "Graphics" | "Matrix" | "NineSliceSprite" | "NoiseFilter" | "Particle" | "ParticleContainer" | "Rectangle" | "Shader" | "Sprite" | "Texture" | "UniformGroup" | "bitmapFontTextParser" | "bitmapFontXMLStringParser" | "colorBit" | "colorBitGl" | "compileHighShaderGlProgram" | "compileHighShaderGpuProgram" | "defaultFilterVert" | "extensions" | "generateTextureBatchBit" | "generateTextureBatchBitGl" | "getBatchSamplersUniformGroup" | "localUniformMSDFBit" | "localUniformMSDFBitGl" | "mSDFBit" | "mSDFBitGl" | "roundPixelsBit" | "roundPixelsBitGl">;
|
|
6224
|
+
/**
|
|
6225
|
+
* One Pixi application: renderer, canvas and stage.
|
|
6226
|
+
*/
|
|
6227
|
+
type PixiApplication = InstanceType<PixiModule["Application"]>;
|
|
6228
|
+
/**
|
|
6229
|
+
* A Pixi display object. `Sprite` and `NineSliceSprite` are containers too, so every view is one.
|
|
6230
|
+
*/
|
|
6231
|
+
type PixiContainer = InstanceType<PixiModule["Container"]>;
|
|
6232
|
+
/**
|
|
6233
|
+
* A Pixi sprite.
|
|
6234
|
+
*/
|
|
6235
|
+
type PixiSprite = InstanceType<PixiModule["Sprite"]>;
|
|
6236
|
+
/**
|
|
6237
|
+
* A Pixi nine-slice sprite.
|
|
6238
|
+
*/
|
|
6239
|
+
type PixiNineSliceSprite = InstanceType<PixiModule["NineSliceSprite"]>;
|
|
6240
|
+
/**
|
|
6241
|
+
* A Pixi graphics object: what a `Shape` component is drawn with, and what clips its children.
|
|
6242
|
+
*/
|
|
6243
|
+
type PixiGraphics = InstanceType<PixiModule["Graphics"]>;
|
|
6244
|
+
/**
|
|
6245
|
+
* A Pixi texture. `assets` owns its lifetime; the renderer only makes and destroys it on request.
|
|
6246
|
+
*/
|
|
6247
|
+
type PixiTexture = InstanceType<PixiModule["Texture"]>;
|
|
6248
|
+
/**
|
|
6249
|
+
* A Pixi filter: what `effects` builds and `sync.filters.set` hangs on a view. `effects` owns
|
|
6250
|
+
* the instance; the renderer only assigns it.
|
|
6251
|
+
*/
|
|
6252
|
+
type PixiFilter = InstanceType<PixiModule["Filter"]>;
|
|
6253
|
+
/**
|
|
6254
|
+
* A bitmap font the renderer installed for a font asset key. One per key.
|
|
6255
|
+
*/
|
|
6256
|
+
type PixiBitmapFont = InstanceType<PixiModule["BitmapFont"]>;
|
|
6257
|
+
/**
|
|
6258
|
+
* The parsed shape of a `.fnt` file: what `BitmapFont` is built from.
|
|
6259
|
+
*/
|
|
6260
|
+
type BitmapFontData = ConstructorParameters<PixiModule["BitmapFont"]>[0]["data"];
|
|
6261
|
+
/**
|
|
6262
|
+
* Which backend the one application drew with, or `"none"` while it draws nothing.
|
|
6263
|
+
*
|
|
6264
|
+
* @example
|
|
6265
|
+
* ```ts
|
|
6266
|
+
* const kind: RendererKind = "webgpu";
|
|
6267
|
+
* ```
|
|
6268
|
+
*/
|
|
6269
|
+
type RendererKind = "webgpu" | "webgl" | "none";
|
|
6270
|
+
/**
|
|
6271
|
+
* renderer plugin events.
|
|
6272
|
+
*
|
|
6273
|
+
* @example
|
|
6274
|
+
* ```ts
|
|
6275
|
+
* // A game reports a lost GPU to its analytics; the pause itself is handled by `lifecycle`.
|
|
6276
|
+
* createPlugin("gpuReport", {
|
|
6277
|
+
* depends: [rendererPlugin],
|
|
6278
|
+
* hooks: ctx => ({ "renderer:device-lost": ({ kind }) => ctx.log.warn("gpu-lost", { kind }) })
|
|
6279
|
+
* }); // a lost WebGPU device logs { kind: "webgpu" }
|
|
6280
|
+
* ```
|
|
6281
|
+
*/
|
|
6282
|
+
type Events$1 = {
|
|
6283
|
+
/** The GPU device or the WebGL context was lost; the game is paused until the restore lands. */"renderer:device-lost": {
|
|
6284
|
+
kind: "webgpu" | "webgl";
|
|
6285
|
+
reason: string;
|
|
6286
|
+
};
|
|
6287
|
+
};
|
|
6288
|
+
/**
|
|
6289
|
+
* The two numbers that bound the shape of the drawn frame: allowed long side / short side.
|
|
6290
|
+
*
|
|
6291
|
+
* @example
|
|
6292
|
+
* ```ts
|
|
6293
|
+
* const aspect: AspectRange = { min: 4 / 3, max: 21 / 9 };
|
|
6294
|
+
* ```
|
|
6295
|
+
*/
|
|
6296
|
+
type AspectRange = {
|
|
6297
|
+
min: number;
|
|
6298
|
+
max: number;
|
|
6299
|
+
};
|
|
6300
|
+
/**
|
|
6301
|
+
* renderer plugin config.
|
|
6302
|
+
*
|
|
6303
|
+
* @example
|
|
6304
|
+
* ```ts
|
|
6305
|
+
* createApp({
|
|
6306
|
+
* plugins: [...screen],
|
|
6307
|
+
* pluginConfigs: { renderer: { mount: "#game", background: 0x101018, maxResolution: 2 } }
|
|
6308
|
+
* });
|
|
6309
|
+
* // A debug build of the fixture outlines every nine-slice from the first frame.
|
|
6310
|
+
* createApp({ pluginConfigs: { renderer: { mount: "#game", debug: { nineSlice: true } } } });
|
|
6311
|
+
* ```
|
|
6312
|
+
*/
|
|
6313
|
+
type Config$1 = {
|
|
6314
|
+
/** Where the canvas goes: a selector or an element. `undefined` keeps the plugin inert. */mount: string | HTMLElement | undefined; /** Clear colour. Also the colour of the bars around the frame. */
|
|
6315
|
+
background: number; /** Antialias the whole canvas. Off by default: pixel art and sprites do not need it. */
|
|
6316
|
+
antialias: boolean; /** Cap of `devicePixelRatio`. Memory grows with its square. */
|
|
6317
|
+
maxResolution: number; /** Passed to Pixi. Pixi falls back to WebGL by itself. */
|
|
6318
|
+
preference: "webgpu" | "webgl"; /** Allowed long side / short side of the frame. A window outside it gets bars. */
|
|
6319
|
+
aspect: AspectRange; /** Display objects kept in all pools together. */
|
|
6320
|
+
poolLimit: number; /** Text of the unsupported-device screen. */
|
|
6321
|
+
unsupportedMessage: string; /** Loader seam. Tests pass a fake module. */
|
|
6322
|
+
loadPixi: () => Promise<PixiModule>; /** Debug drawing at start. `app.renderer.sync.debug` switches it while the game runs. */
|
|
6323
|
+
debug: DebugSwitches;
|
|
6324
|
+
};
|
|
6325
|
+
/**
|
|
6326
|
+
* renderer plugin state: one branch per module.
|
|
6327
|
+
*/
|
|
6328
|
+
type State$1 = {
|
|
6329
|
+
host: HostState;
|
|
6330
|
+
viewport: ViewportState;
|
|
6331
|
+
sync: SyncState;
|
|
6332
|
+
monitor: MonitorState;
|
|
6333
|
+
};
|
|
6334
|
+
/**
|
|
6335
|
+
* renderer plugin API, `app.renderer`: grouped by module, and the two members of `monitor`,
|
|
6336
|
+
* `stats()` and `capture()`, on the plugin itself.
|
|
6337
|
+
*
|
|
6338
|
+
* @example
|
|
6339
|
+
* ```ts
|
|
6340
|
+
* app.renderer.host.kind(); // "webgpu"
|
|
6341
|
+
* app.renderer.viewport.size().width; // 1080, reference units
|
|
6342
|
+
* app.renderer.sync.hitTest(540, 300, () => true); // 1048576
|
|
6343
|
+
* app.renderer.stats().views; // 180
|
|
6344
|
+
* app.renderer.stats().renderPasses; // 1: no filter on screen
|
|
6345
|
+
* ```
|
|
6346
|
+
*/
|
|
6347
|
+
type Api$1 = {
|
|
6348
|
+
host: HostApi;
|
|
6349
|
+
viewport: ViewportApi;
|
|
6350
|
+
sync: SyncApi;
|
|
6351
|
+
} & MonitorApi;
|
|
6352
|
+
/**
|
|
6353
|
+
* One entity under a point of the page, what the `game.at` door answers: the entity, its owner,
|
|
6354
|
+
* the projection and key that address it when it has them, and the layer its own `Layer` names
|
|
6355
|
+
* (`undefined` for a `Parent` child, which draws in its parent's layer).
|
|
6356
|
+
*
|
|
6357
|
+
* @example
|
|
6358
|
+
* ```ts
|
|
6359
|
+
* // A coin of the board under the pointer.
|
|
6360
|
+
* const under: Under = {
|
|
6361
|
+
* entity: 1_048_581, owner: { kind: "projection", name: "board.items" },
|
|
6362
|
+
* key: { projection: "board.items", key: "c7" }, layer: "items"
|
|
6363
|
+
* };
|
|
6364
|
+
* ```
|
|
6365
|
+
*/
|
|
6366
|
+
type Under = {
|
|
6367
|
+
entity: Entity;
|
|
6368
|
+
owner: Owner;
|
|
6369
|
+
key: ProjectionKey | undefined;
|
|
6370
|
+
layer: string | undefined;
|
|
6371
|
+
};
|
|
6372
|
+
/**
|
|
6373
|
+
* Resolved dependency APIs. `clock` is the time source of the frame counters.
|
|
6374
|
+
*/
|
|
6375
|
+
type Deps$1 = {
|
|
6376
|
+
time: Api$4;
|
|
6377
|
+
lifecycle: Api$6;
|
|
6378
|
+
world: Api$2;
|
|
6379
|
+
clock: Api$7;
|
|
6380
|
+
};
|
|
6381
|
+
/**
|
|
6382
|
+
* What the kernel context offers before the deps are attached.
|
|
6383
|
+
*
|
|
6384
|
+
* `emit` is the kernel's own: `index.ts` writes `events` with an annotated `register`
|
|
6385
|
+
* (core spec `14-EVENT-REGISTRATION.md` row 8), so the plugin's own event reaches the pre-typed
|
|
6386
|
+
* `api` factory and no member of this context is cast.
|
|
6387
|
+
*/
|
|
6388
|
+
type KernelSlice$1 = PluginCtx<Config$1, State$1, Events$1> & {
|
|
6389
|
+
readonly global: Readonly<Config$8>;
|
|
6390
|
+
readonly log: Log.LogApi;
|
|
6391
|
+
readonly require: Require;
|
|
6392
|
+
};
|
|
6393
|
+
/**
|
|
6394
|
+
* Domain context shared by the three modules.
|
|
6395
|
+
*/
|
|
6396
|
+
type RendererCtx = KernelSlice$1 & {
|
|
6397
|
+
readonly deps: Deps$1;
|
|
6398
|
+
};
|
|
6399
|
+
/**
|
|
6400
|
+
* Both halves of the `host` module: what a game calls and what the two modules above get injected.
|
|
6401
|
+
*/
|
|
6402
|
+
type HostModule = HostApi & HostInternal;
|
|
6403
|
+
/**
|
|
6404
|
+
* Both halves of the `viewport` module: what a game calls and what `sync` and the frame drive.
|
|
6405
|
+
*/
|
|
6406
|
+
type ViewportModule = ViewportApi & ViewportInternal;
|
|
6407
|
+
/**
|
|
6408
|
+
* Both halves of the `sync` module: what `input` and `assets` call and what the frame drives.
|
|
6409
|
+
*/
|
|
6410
|
+
type SyncModule = SyncApi & SyncInternal;
|
|
6411
|
+
/**
|
|
6412
|
+
* Both halves of the `monitor` module: what a tool calls and what the frame drives.
|
|
6413
|
+
*/
|
|
6414
|
+
type MonitorModule = MonitorApi & MonitorInternal;
|
|
6415
|
+
/**
|
|
6416
|
+
* The four modules in injection order.
|
|
6417
|
+
*/
|
|
6418
|
+
type Modules = {
|
|
6419
|
+
host: HostModule;
|
|
6420
|
+
viewport: ViewportModule;
|
|
6421
|
+
sync: SyncModule;
|
|
6422
|
+
monitor: MonitorModule;
|
|
6423
|
+
};
|
|
6424
|
+
/**
|
|
6425
|
+
* What `onStop` receives: the frozen config and the plugin state, nothing else.
|
|
6426
|
+
*/
|
|
6427
|
+
type TeardownScope = {
|
|
6428
|
+
readonly config: Readonly<Config$1>;
|
|
6429
|
+
readonly state: State$1;
|
|
6430
|
+
};
|
|
6431
|
+
declare namespace types_d_exports {
|
|
6432
|
+
export { Api, AssetKind, AssetsCtx, AssetsIo, AtlasFrame, AtlasPage, AudioAsset, AudioMime, BundleMap, BundleRecord, BundleSpec, BundleUsage, Config, CreateTextureOptions, DecodedImage, DefineBundles, Deps, Events, FetchResponse, FlowRest, FontAsset, FontPage, Inflight, KernelSlice, LoadBundles, LoadReason, LoadResult, LoadedAssets, LoadedFont, Manifest, ManifestBundle, ManifestFile, NineBorders, NineSlice, PreloadQueue, State, Texture, Tier, Usage };
|
|
6433
|
+
}
|
|
6434
|
+
/**
|
|
6435
|
+
* When a bundle is loaded. `boot` and `core` stay for the whole session, `scene` and `feature`
|
|
6436
|
+
* come and go with the graph, `lazy` is never preloaded.
|
|
6437
|
+
*
|
|
6438
|
+
* @example
|
|
6439
|
+
* ```ts
|
|
6440
|
+
* const tier: Tier = "scene";
|
|
6441
|
+
* ```
|
|
6442
|
+
*/
|
|
6443
|
+
type Tier = "boot" | "core" | "scene" | "feature" | "lazy";
|
|
6444
|
+
/**
|
|
6445
|
+
* What one file of a bundle is. A `.fnt` with its `.png` pages is one `font`, an `.mp3` or an
|
|
6446
|
+
* `.m4a` is `audio`, everything else is a `texture`. A file of an older manifest that names no
|
|
6447
|
+
* kind is a texture.
|
|
6448
|
+
*
|
|
6449
|
+
* @example
|
|
6450
|
+
* ```ts
|
|
6451
|
+
* const kind: AssetKind = "font";
|
|
6452
|
+
* ```
|
|
6453
|
+
*/
|
|
6454
|
+
type AssetKind = "texture" | "font" | "audio";
|
|
6455
|
+
/**
|
|
6456
|
+
* A GPU texture. `assets` owns its lifetime and never imports Pixi: `renderer` makes and destroys
|
|
6457
|
+
* it, this plugin only says when.
|
|
6458
|
+
*/
|
|
6459
|
+
type Texture = PixiTexture;
|
|
6460
|
+
/**
|
|
6461
|
+
* A decoded image, ready for the upload. `createImageBitmap` produces the first shape.
|
|
6462
|
+
*/
|
|
6463
|
+
type DecodedImage = ImageBitmap | HTMLImageElement;
|
|
6464
|
+
/**
|
|
6465
|
+
* Nine-slice borders in pixels, in the order left, top, right, bottom.
|
|
6466
|
+
*
|
|
6467
|
+
* @example
|
|
6468
|
+
* ```ts
|
|
6469
|
+
* const borders: NineBorders = [48, 48, 48, 48];
|
|
6470
|
+
* ```
|
|
6471
|
+
*/
|
|
6472
|
+
type NineBorders = readonly [number, number, number, number];
|
|
6473
|
+
/**
|
|
6474
|
+
* Options of `io.createTexture`. Same shape as `renderer.sync.textures.create`.
|
|
6475
|
+
*
|
|
6476
|
+
* @example
|
|
6477
|
+
* ```ts
|
|
6478
|
+
* const options: CreateTextureOptions = { nine: [48, 48, 48, 48] };
|
|
6479
|
+
* ```
|
|
6480
|
+
*/
|
|
6481
|
+
type CreateTextureOptions = {
|
|
6482
|
+
nine?: NineBorders;
|
|
6483
|
+
};
|
|
6484
|
+
/**
|
|
6485
|
+
* The part of a fetch response the plugin reads: the manifest is `json`, a texture is a `blob`,
|
|
6486
|
+
* a font file is `text` and an audio file stays the raw `arrayBuffer`. The global `Response`
|
|
6487
|
+
* fits it. `headers` gives the `content-type`: on `tauri://` a missing file comes back `200` with
|
|
6488
|
+
* `index.html`, so `text/html` for a path that is not `.html` counts as missing.
|
|
6489
|
+
*/
|
|
6490
|
+
type FetchResponse = {
|
|
6491
|
+
ok: boolean;
|
|
6492
|
+
status: number;
|
|
6493
|
+
headers: {
|
|
6494
|
+
get(name: string): string | null;
|
|
6495
|
+
};
|
|
6496
|
+
json(): Promise<unknown>;
|
|
6497
|
+
blob(): Promise<Blob>;
|
|
6498
|
+
text(): Promise<string>;
|
|
6499
|
+
arrayBuffer(): Promise<ArrayBuffer>;
|
|
6500
|
+
};
|
|
6501
|
+
/**
|
|
6502
|
+
* The I/O seam: everything that leaves the plugin. `undefined` in the config means the browser
|
|
6503
|
+
* pair plus `renderer.sync.textures`; a test passes a fake and nothing touches the network or a GPU.
|
|
6504
|
+
*
|
|
6505
|
+
* @example
|
|
6506
|
+
* ```ts
|
|
6507
|
+
* // A unit test serves two files from memory and counts the textures it handed out.
|
|
6508
|
+
* const io: AssetsIo = {
|
|
6509
|
+
* fetch: async () => ({
|
|
6510
|
+
* ok: true,
|
|
6511
|
+
* status: 200,
|
|
6512
|
+
* headers: { get: () => null },
|
|
6513
|
+
* json: async () => ({}),
|
|
6514
|
+
* blob: async () => blob,
|
|
6515
|
+
* text: async () => 'info face="body"',
|
|
6516
|
+
* arrayBuffer: async () => new ArrayBuffer(8)
|
|
6517
|
+
* }),
|
|
6518
|
+
* decode: async () => bitmap,
|
|
6519
|
+
* createTexture: () => ({ id: "t1" }) as unknown as Texture,
|
|
6520
|
+
* sliceTexture: (page, frame) => ({ id: "s1", page, frame }) as unknown as Texture,
|
|
6521
|
+
* destroyTexture: texture => destroyed.push(texture)
|
|
6522
|
+
* };
|
|
6523
|
+
*
|
|
6524
|
+
* createApp({ plugins: [...screen], pluginConfigs: { assets: { io, manifest } } });
|
|
6525
|
+
* ```
|
|
6526
|
+
*/
|
|
6527
|
+
type AssetsIo = {
|
|
6528
|
+
/**
|
|
6529
|
+
* Fetches one URL: the manifest, or a file of a bundle.
|
|
6530
|
+
*
|
|
6531
|
+
* @param url - Absolute or root-relative URL.
|
|
6532
|
+
* @param init - Carries the abort signal of the running load.
|
|
6533
|
+
* @param init.signal - Aborts the request when the load is cancelled.
|
|
6534
|
+
* @returns The response.
|
|
6535
|
+
*/
|
|
6536
|
+
fetch(url: string, init: {
|
|
6537
|
+
signal: AbortSignal;
|
|
6538
|
+
}): Promise<FetchResponse>;
|
|
6539
|
+
/**
|
|
6540
|
+
* Decodes one image file.
|
|
6541
|
+
*
|
|
6542
|
+
* @param blob - The bytes of the file.
|
|
6543
|
+
* @returns The decoded image.
|
|
6544
|
+
*/
|
|
6545
|
+
decode(blob: Blob): Promise<DecodedImage>;
|
|
6546
|
+
/**
|
|
6547
|
+
* Uploads one decoded image to the GPU.
|
|
6548
|
+
*
|
|
6549
|
+
* @param image - The decoded image.
|
|
6550
|
+
* @param options - `nine` becomes the default nine-slice borders of the texture.
|
|
6551
|
+
* @returns The new texture.
|
|
6552
|
+
*/
|
|
6553
|
+
createTexture(image: DecodedImage, options?: CreateTextureOptions): Texture;
|
|
6554
|
+
/**
|
|
6555
|
+
* Cuts the texture of one packed file out of its atlas page. The slice shares the page's
|
|
6556
|
+
* source: no pixel is copied. Same shape as `renderer.sync.textures.slice`; the manifest's
|
|
6557
|
+
* `atlas` frame is passed as it is.
|
|
6558
|
+
*
|
|
6559
|
+
* @param page - The page texture, made by `createTexture`.
|
|
6560
|
+
* @param frame - Where the file lies on the page, in page pixels.
|
|
6561
|
+
* @param options - `nine` becomes the default nine-slice borders of the slice.
|
|
6562
|
+
* @returns The new texture over the page's source.
|
|
6563
|
+
*/
|
|
6564
|
+
sliceTexture(page: Texture, frame: SliceFrame, options?: CreateTextureOptions): Texture;
|
|
6565
|
+
/**
|
|
6566
|
+
* Frees one texture. A page frees its source too; a slice frees only itself, so the slices of
|
|
6567
|
+
* a bundle go first and its pages after them.
|
|
6568
|
+
*
|
|
6569
|
+
* @param texture - The texture to free.
|
|
6570
|
+
*/
|
|
6571
|
+
destroyTexture(texture: Texture): void;
|
|
6572
|
+
};
|
|
6573
|
+
/**
|
|
6574
|
+
* Nine-slice metadata of one file, as the scanner writes it. Always four numbers: the tags
|
|
6575
|
+
* `{nine=N}`, `{nine=H,V}` and `{nine=L,T,R,B}` all land here.
|
|
6576
|
+
*
|
|
6577
|
+
* @example
|
|
6578
|
+
* ```ts
|
|
6579
|
+
* const nine: NineSlice = { left: 48, top: 48, right: 48, bottom: 48 };
|
|
6580
|
+
* ```
|
|
6581
|
+
*/
|
|
6582
|
+
type NineSlice = {
|
|
6583
|
+
left: number;
|
|
6584
|
+
top: number;
|
|
6585
|
+
right: number;
|
|
6586
|
+
bottom: number;
|
|
6587
|
+
};
|
|
6588
|
+
/**
|
|
6589
|
+
* Where a packed file lies: `page` is the id of a page of the same bundle, never a file name, and
|
|
6590
|
+
* the frame is in page pixels. `width` and `height` equal the file's own, so its `nine` stays
|
|
6591
|
+
* valid as it is. The packer writes it; the loader cuts the file out of the page.
|
|
6592
|
+
*
|
|
6593
|
+
* @example
|
|
6594
|
+
* ```ts
|
|
6595
|
+
* const frame: AtlasFrame = { page: "ui/main-0", x: 583, y: 595, width: 256, height: 128 };
|
|
6596
|
+
* ```
|
|
6597
|
+
*/
|
|
6598
|
+
type AtlasFrame = {
|
|
6599
|
+
page: string;
|
|
6600
|
+
x: number;
|
|
6601
|
+
y: number;
|
|
6602
|
+
width: number;
|
|
6603
|
+
height: number;
|
|
6604
|
+
};
|
|
6605
|
+
/**
|
|
6606
|
+
* One atlas page of a packed bundle. `id` is `<bundle>/<group>-<index>`, the name an `atlas`
|
|
6607
|
+
* frame uses; `path` is the hashed WebP file, read like the `path` of a file. `mb` is
|
|
6608
|
+
* `width × height × 4 / 1 048 576`: the page carries the cost of every file packed on it.
|
|
6609
|
+
*
|
|
6610
|
+
* @example
|
|
6611
|
+
* ```ts
|
|
6612
|
+
* const page: AtlasPage = {
|
|
6613
|
+
* id: "ui/main-0",
|
|
6614
|
+
* path: "ui/main-0-3b1d55a0c9.webp",
|
|
6615
|
+
* width: 966,
|
|
6616
|
+
* height: 1365,
|
|
6617
|
+
* mb: 5.03
|
|
6618
|
+
* };
|
|
6619
|
+
* ```
|
|
6620
|
+
*/
|
|
6621
|
+
type AtlasPage = {
|
|
6622
|
+
id: string;
|
|
6623
|
+
path: string;
|
|
6624
|
+
width: number;
|
|
6625
|
+
height: number;
|
|
6626
|
+
mb: number;
|
|
6627
|
+
};
|
|
6628
|
+
/**
|
|
6629
|
+
* One page image of a font. A page has no key of its own: it belongs to the `.fnt` file that
|
|
6630
|
+
* names it, and is loaded and freed with it.
|
|
6631
|
+
*
|
|
6632
|
+
* @example
|
|
6633
|
+
* ```ts
|
|
6634
|
+
* const page: FontPage = {
|
|
6635
|
+
* path: "features/ui/assets/body_0.png",
|
|
6636
|
+
* width: 256,
|
|
6637
|
+
* height: 128,
|
|
6638
|
+
* mb: 0.125
|
|
6639
|
+
* };
|
|
6640
|
+
* ```
|
|
6641
|
+
*/
|
|
6642
|
+
type FontPage = {
|
|
6643
|
+
path: string;
|
|
6644
|
+
width: number;
|
|
6645
|
+
height: number;
|
|
6646
|
+
mb: number;
|
|
6647
|
+
};
|
|
6648
|
+
/**
|
|
6649
|
+
* One file of a bundle. `mb` is what it costs: `width × height × 4` bytes for a texture, the sum
|
|
6650
|
+
* of the pages for a font, the size of the file for audio, and `0` for a texture packed in an
|
|
6651
|
+
* atlas, whose page carries the cost. `kind` is absent for a texture, which is what every
|
|
6652
|
+
* manifest written before fonts and audio carries. A loose file has `path`; a packed texture has
|
|
6653
|
+
* `atlas` and no `path`.
|
|
6654
|
+
*
|
|
6655
|
+
* @example
|
|
6656
|
+
* ```ts
|
|
6657
|
+
* // The dev manifest of the scanner: a loose file.
|
|
6658
|
+
* const loose: ManifestFile = {
|
|
6659
|
+
* key: "ui.panel",
|
|
6660
|
+
* path: "features/ui/assets/panel{nine=48}.png",
|
|
6661
|
+
* width: 256,
|
|
6662
|
+
* height: 128,
|
|
6663
|
+
* mb: 0.125,
|
|
6664
|
+
* nine: { left: 48, top: 48, right: 48, bottom: 48 }
|
|
6665
|
+
* };
|
|
6666
|
+
*
|
|
6667
|
+
* // The packed manifest: the same key, cut out of the page "ui/main-0".
|
|
6668
|
+
* const packed: ManifestFile = {
|
|
6669
|
+
* key: "ui.panel",
|
|
6670
|
+
* width: 256,
|
|
6671
|
+
* height: 128,
|
|
6672
|
+
* mb: 0,
|
|
6673
|
+
* nine: { left: 48, top: 48, right: 48, bottom: 48 },
|
|
6674
|
+
* atlas: { page: "ui/main-0", x: 583, y: 595, width: 256, height: 128 }
|
|
6675
|
+
* };
|
|
6676
|
+
* ```
|
|
6677
|
+
*/
|
|
6678
|
+
type ManifestFile = {
|
|
6679
|
+
key: string;
|
|
6680
|
+
path?: string;
|
|
6681
|
+
kind?: AssetKind;
|
|
6682
|
+
width: number;
|
|
6683
|
+
height: number;
|
|
6684
|
+
mb: number;
|
|
6685
|
+
pages?: readonly FontPage[];
|
|
6686
|
+
nine?: NineSlice;
|
|
6687
|
+
atlas?: AtlasFrame;
|
|
6688
|
+
};
|
|
6689
|
+
/**
|
|
6690
|
+
* One bundle of the manifest: which feature owns it, when it loads and what it costs. `pages` is
|
|
6691
|
+
* written by the packer only, sorted by id; `mb` is then its pages, its loose textures, its font
|
|
6692
|
+
* pages and its audio bytes together.
|
|
6693
|
+
*
|
|
6694
|
+
* @example
|
|
6695
|
+
* ```ts
|
|
6696
|
+
* const bundle: ManifestBundle = { feature: "ui", tier: "core", mb: 0.125, files: [] };
|
|
6697
|
+
* ```
|
|
5782
6698
|
*/
|
|
5783
|
-
type
|
|
5784
|
-
|
|
5785
|
-
|
|
6699
|
+
type ManifestBundle = {
|
|
6700
|
+
feature: string;
|
|
6701
|
+
tier: Tier;
|
|
6702
|
+
mb: number;
|
|
6703
|
+
pages?: readonly AtlasPage[];
|
|
6704
|
+
files: readonly ManifestFile[];
|
|
5786
6705
|
};
|
|
5787
|
-
declare namespace types_d_exports {
|
|
5788
|
-
export { Api, AspectRange, BitmapFontData, Config, CountingClasses, CoverFrame, CreateTextureOptions, DebugApi, DebugSwitches, Deps, DisplayAdapter, DisplayEntry, DisplaysApi, DrawCounter, Events, FilterSlot, FiltersApi, FontsApi, HitBox, HostApi, HostInternal, HostModule, HostState, KernelSlice, LayerEntry, Modules, MonitorApi, MonitorDeps, MonitorInternal, MonitorModule, MonitorState, NineBorders, Orientation, PixiApplication, PixiBitmapFont, PixiContainer, PixiFilter, PixiGraphics, PixiModule, PixiNineSliceSprite, PixiSprite, PixiTexture, Point, Rect, RenderStats, RendererCtx, RendererKind, SafeArea, SliceFrame, State, SyncApi, SyncCounts, SyncInternal, SyncModule, SyncState, TeardownScope, TextureProvider, TextureUsage, TexturesApi, View, ViewKind, ViewportApi, ViewportInternal, ViewportModule, ViewportSize, ViewportState };
|
|
5789
|
-
}
|
|
5790
6706
|
/**
|
|
5791
|
-
* The
|
|
5792
|
-
*
|
|
5793
|
-
*
|
|
5794
|
-
*
|
|
5795
|
-
* and the extension registry with the three WebGPU draw classes the dev draw-call
|
|
5796
|
-
* counter swaps and the bitmap text pipe `host` swaps for one whose SDF shader applies the alpha
|
|
5797
|
-
* once (with the WGSL and GLSL shader pieces it compiles that shader from). The module object
|
|
5798
|
-
* never arrives through a static import: `config.loadPixi()` returns it, so a game without a screen carries no
|
|
5799
|
-
* Pixi in its bundle.
|
|
6707
|
+
* The manifest: the contract between the scanner, the packer, this plugin and the editor. Version
|
|
6708
|
+
* `1` is the dev manifest of `bun run assets:keys`, version `2` the packed one of
|
|
6709
|
+
* `bun run assets:pack`; the plugin reads both. Bundles are sorted by name, pages by id and files
|
|
6710
|
+
* by key, so two runs on the same tree give the same bytes.
|
|
5800
6711
|
*
|
|
5801
6712
|
* @example
|
|
5802
6713
|
* ```ts
|
|
5803
|
-
*
|
|
5804
|
-
*
|
|
5805
|
-
*
|
|
5806
|
-
*
|
|
5807
|
-
*
|
|
6714
|
+
* // A dev build: loose files, straight from the features.
|
|
6715
|
+
* const manifest: Manifest = {
|
|
6716
|
+
* version: 1,
|
|
6717
|
+
* bundles: { ui: { feature: "ui", tier: "core", mb: 0, files: [] } }
|
|
6718
|
+
* };
|
|
6719
|
+
*
|
|
6720
|
+
* createApp({ plugins: [...screen], pluginConfigs: { assets: { manifest } } });
|
|
6721
|
+
*
|
|
6722
|
+
* // The packed manifest of the same game: the panel is cut out of the page "ui/main-0".
|
|
6723
|
+
* const page = { id: "ui/main-0", path: "ui/main-0-3b1d55a0c9.webp", width: 966, height: 1365, mb: 5.03 };
|
|
6724
|
+
* const atlas = { page: "ui/main-0", x: 583, y: 595, width: 256, height: 128 };
|
|
6725
|
+
* const panel = { key: "ui.panel", width: 256, height: 128, mb: 0, atlas };
|
|
6726
|
+
* const packed: Manifest = {
|
|
6727
|
+
* version: 2,
|
|
6728
|
+
* bundles: { ui: { feature: "ui", tier: "core", mb: 5.03, pages: [page], files: [panel] } }
|
|
6729
|
+
* };
|
|
5808
6730
|
* ```
|
|
5809
6731
|
*/
|
|
5810
|
-
type
|
|
6732
|
+
type Manifest = {
|
|
6733
|
+
version: 1 | 2;
|
|
6734
|
+
bundles: Readonly<Record<string, ManifestBundle>>;
|
|
6735
|
+
};
|
|
5811
6736
|
/**
|
|
5812
|
-
*
|
|
6737
|
+
* What a feature declares about one of its bundles. `files` are globs relative to the feature's
|
|
6738
|
+
* `assets/`; a bundle with `files` takes those files out of the feature's default bundle.
|
|
6739
|
+
*
|
|
6740
|
+
* @example
|
|
6741
|
+
* ```ts
|
|
6742
|
+
* const spec: BundleSpec = { tier: "lazy", files: ["chains/*.png"] };
|
|
6743
|
+
* ```
|
|
5813
6744
|
*/
|
|
5814
|
-
type
|
|
6745
|
+
type BundleSpec = {
|
|
6746
|
+
tier: Tier;
|
|
6747
|
+
files?: readonly string[];
|
|
6748
|
+
};
|
|
5815
6749
|
/**
|
|
5816
|
-
*
|
|
6750
|
+
* What `defineBundles` returns: plain data the scanner and the plugin both read.
|
|
6751
|
+
*
|
|
6752
|
+
* @example
|
|
6753
|
+
* ```ts
|
|
6754
|
+
* const bundles: BundleMap = {
|
|
6755
|
+
* kind: "bundles",
|
|
6756
|
+
* map: { board: { tier: "scene" }, "board.chains": { tier: "lazy", files: ["chains/*.png"] } }
|
|
6757
|
+
* };
|
|
6758
|
+
* ```
|
|
5817
6759
|
*/
|
|
5818
|
-
type
|
|
6760
|
+
type BundleMap<Key extends string = string> = {
|
|
6761
|
+
kind: "bundles";
|
|
6762
|
+
map: Partial<Record<Key, BundleSpec>>;
|
|
6763
|
+
};
|
|
5819
6764
|
/**
|
|
5820
|
-
*
|
|
6765
|
+
* `defineBundles` bound to the bundle keys of one game. `defineGame` returns it, so a key that is
|
|
6766
|
+
* not in `BundleKey` does not compile.
|
|
6767
|
+
*
|
|
6768
|
+
* @example
|
|
6769
|
+
* ```ts
|
|
6770
|
+
* const defineGameBundles: DefineBundles<"board" | "board.chains"> = defineBundles;
|
|
6771
|
+
* ```
|
|
5821
6772
|
*/
|
|
5822
|
-
type
|
|
6773
|
+
type DefineBundles<Key extends string> = (map: Partial<Record<Key, BundleSpec>>) => BundleMap<Key>;
|
|
5823
6774
|
/**
|
|
5824
|
-
*
|
|
6775
|
+
* The `load` descriptor helper bound to the bundle keys of one game.
|
|
6776
|
+
*
|
|
6777
|
+
* @example
|
|
6778
|
+
* ```ts
|
|
6779
|
+
* const loadGameBundle: LoadBundles<"board" | "board.chains"> = load;
|
|
6780
|
+
* ```
|
|
5825
6781
|
*/
|
|
5826
|
-
type
|
|
6782
|
+
type LoadBundles<Key extends string> = (bundle: Key | readonly Key[]) => Descriptor;
|
|
5827
6783
|
/**
|
|
5828
|
-
*
|
|
6784
|
+
* What the `load` effect resolves with, so a loading node can write its own progress.
|
|
6785
|
+
*
|
|
6786
|
+
* @example
|
|
6787
|
+
* ```ts
|
|
6788
|
+
* const result: LoadResult = { loaded: ["board.chains"], mb: 1.25 };
|
|
6789
|
+
* ```
|
|
5829
6790
|
*/
|
|
5830
|
-
type
|
|
6791
|
+
type LoadResult = {
|
|
6792
|
+
loaded: string[];
|
|
6793
|
+
mb: number;
|
|
6794
|
+
};
|
|
5831
6795
|
/**
|
|
5832
|
-
*
|
|
6796
|
+
* Why a bundle was loaded. It is the reason of the caller that started the load; a later waiter
|
|
6797
|
+
* changes nothing.
|
|
6798
|
+
*
|
|
6799
|
+
* @example
|
|
6800
|
+
* ```ts
|
|
6801
|
+
* const reason: LoadReason = "enter";
|
|
6802
|
+
* ```
|
|
5833
6803
|
*/
|
|
5834
|
-
type
|
|
6804
|
+
type LoadReason = "boot" | "enter" | "request" | "preload";
|
|
5835
6805
|
/**
|
|
5836
|
-
*
|
|
5837
|
-
*
|
|
6806
|
+
* The running load of one bundle. Every caller is a waiter; the last one to leave aborts it.
|
|
6807
|
+
* `promise` never rejects: what broke waits in `error`, so a load nobody joined any more cannot
|
|
6808
|
+
* become an unhandled rejection.
|
|
5838
6809
|
*/
|
|
5839
|
-
type
|
|
6810
|
+
type Inflight = {
|
|
6811
|
+
promise: Promise<void>;
|
|
6812
|
+
controller: AbortController;
|
|
6813
|
+
waiters: number;
|
|
6814
|
+
error: unknown;
|
|
6815
|
+
};
|
|
5840
6816
|
/**
|
|
5841
|
-
* A
|
|
6817
|
+
* A loaded font: the `.fnt` file as text and the page textures it names. `text` installs the
|
|
6818
|
+
* first page in the renderer; every page is destroyed with the bundle.
|
|
6819
|
+
*
|
|
6820
|
+
* @example
|
|
6821
|
+
* ```ts
|
|
6822
|
+
* const font: FontAsset = { fnt: 'info face="body" size=32', texture };
|
|
6823
|
+
* ```
|
|
5842
6824
|
*/
|
|
5843
|
-
type
|
|
6825
|
+
type FontAsset = {
|
|
6826
|
+
fnt: string;
|
|
6827
|
+
texture: Texture;
|
|
6828
|
+
};
|
|
5844
6829
|
/**
|
|
5845
|
-
*
|
|
6830
|
+
* What the plugin keeps for a loaded font: the public pair plus every page, because a font of
|
|
6831
|
+
* two pages owns two textures.
|
|
5846
6832
|
*/
|
|
5847
|
-
type
|
|
6833
|
+
type LoadedFont = FontAsset & {
|
|
6834
|
+
pages: readonly Texture[];
|
|
6835
|
+
};
|
|
5848
6836
|
/**
|
|
5849
|
-
*
|
|
6837
|
+
* The container type of a sound, read from the extension of its file: `.mp3` is `"audio/mpeg"`,
|
|
6838
|
+
* `.m4a` (AAC in an MP4 container) is `"audio/mp4"`. These are the two formats the scanner takes,
|
|
6839
|
+
* and the two every browser and WebView decodes.
|
|
5850
6840
|
*
|
|
5851
6841
|
* @example
|
|
5852
6842
|
* ```ts
|
|
5853
|
-
* const
|
|
6843
|
+
* const mime: AudioMime = "audio/mp4"; // the type of theme.m4a
|
|
5854
6844
|
* ```
|
|
5855
6845
|
*/
|
|
5856
|
-
type
|
|
6846
|
+
type AudioMime = "audio/mpeg" | "audio/mp4";
|
|
5857
6847
|
/**
|
|
5858
|
-
*
|
|
6848
|
+
* A loaded sound: the bytes exactly as they were fetched, never decoded here, and the MIME type
|
|
6849
|
+
* of their container. A `Blob` of the bytes needs the type: Safari does not sniff a typeless one.
|
|
5859
6850
|
*
|
|
5860
6851
|
* @example
|
|
5861
6852
|
* ```ts
|
|
5862
|
-
*
|
|
5863
|
-
* createPlugin("gpuReport", {
|
|
5864
|
-
* depends: [rendererPlugin],
|
|
5865
|
-
* hooks: ctx => ({ "renderer:device-lost": ({ kind }) => ctx.log.warn("gpu-lost", { kind }) })
|
|
5866
|
-
* }); // a lost WebGPU device logs { kind: "webgpu" }
|
|
6853
|
+
* const asset: AudioAsset = { bytes: new ArrayBuffer(12_288), mime: "audio/mpeg" }; // click.mp3
|
|
5867
6854
|
* ```
|
|
5868
6855
|
*/
|
|
5869
|
-
type
|
|
5870
|
-
|
|
5871
|
-
|
|
5872
|
-
reason: string;
|
|
5873
|
-
};
|
|
6856
|
+
type AudioAsset = {
|
|
6857
|
+
bytes: ArrayBuffer;
|
|
6858
|
+
mime: AudioMime;
|
|
5874
6859
|
};
|
|
5875
6860
|
/**
|
|
5876
|
-
*
|
|
5877
|
-
*
|
|
5878
|
-
*
|
|
5879
|
-
* ```ts
|
|
5880
|
-
* const aspect: AspectRange = { min: 4 / 3, max: 21 / 9 };
|
|
5881
|
-
* ```
|
|
6861
|
+
* What one bundle brought: textures by asset key (a packed file's is a slice of its page), fonts
|
|
6862
|
+
* with their pages, the undecoded sounds with their MIME types, and the atlas pages by page id. A
|
|
6863
|
+
* running load fills the same four maps before they are published.
|
|
5882
6864
|
*/
|
|
5883
|
-
type
|
|
5884
|
-
|
|
5885
|
-
|
|
6865
|
+
type LoadedAssets = {
|
|
6866
|
+
textures: Map<string, Texture>;
|
|
6867
|
+
fonts: Map<string, LoadedFont>;
|
|
6868
|
+
audio: Map<string, AudioAsset>;
|
|
6869
|
+
pages: Map<string, Texture>;
|
|
5886
6870
|
};
|
|
5887
6871
|
/**
|
|
5888
|
-
*
|
|
6872
|
+
* What the plugin knows about one bundle. `lastUsed` is the value of `useCounter` at the last
|
|
6873
|
+
* touch: a counter, never a clock.
|
|
6874
|
+
*/
|
|
6875
|
+
type BundleRecord = LoadedAssets & {
|
|
6876
|
+
status: "idle" | "loading" | "loaded";
|
|
6877
|
+
inflight: Inflight | undefined;
|
|
6878
|
+
lastUsed: number;
|
|
6879
|
+
};
|
|
6880
|
+
/**
|
|
6881
|
+
* The background preload. A new rest node replaces it and aborts the old controller.
|
|
6882
|
+
*/
|
|
6883
|
+
type PreloadQueue = {
|
|
6884
|
+
bundles: string[];
|
|
6885
|
+
controller: AbortController;
|
|
6886
|
+
};
|
|
6887
|
+
/**
|
|
6888
|
+
* assets plugin config.
|
|
5889
6889
|
*
|
|
5890
6890
|
* @example
|
|
5891
6891
|
* ```ts
|
|
5892
6892
|
* createApp({
|
|
5893
|
-
* plugins: [...screen],
|
|
5894
|
-
* pluginConfigs: {
|
|
6893
|
+
* plugins: [...screen, boardFeature],
|
|
6894
|
+
* pluginConfigs: { assets: { manifest: "/assets/manifest.json", textureBudgetMb: 192 } }
|
|
5895
6895
|
* });
|
|
5896
|
-
* // A debug build of the fixture outlines every nine-slice from the first frame.
|
|
5897
|
-
* createApp({ pluginConfigs: { renderer: { mount: "#game", debug: { nineSlice: true } } } });
|
|
5898
6896
|
* ```
|
|
5899
6897
|
*/
|
|
5900
6898
|
type Config = {
|
|
5901
|
-
/**
|
|
5902
|
-
|
|
5903
|
-
|
|
5904
|
-
|
|
5905
|
-
|
|
5906
|
-
aspect: AspectRange; /** Display objects kept in all pools together. */
|
|
5907
|
-
poolLimit: number; /** Text of the unsupported-device screen. */
|
|
5908
|
-
unsupportedMessage: string; /** Loader seam. Tests pass a fake module. */
|
|
5909
|
-
loadPixi: () => Promise<PixiModule>; /** Debug drawing at start. `app.renderer.sync.debug` switches it while the game runs. */
|
|
5910
|
-
debug: DebugSwitches;
|
|
6899
|
+
/** URL of `manifest.json`, or the parsed manifest itself (tests, headless). `undefined`: an empty manifest. */manifest: string | Manifest | undefined; /** Texture memory budget in MB. */
|
|
6900
|
+
textureBudgetMb: number; /** How many edges from a rest node the background preload looks ahead. `0` turns preload off. */
|
|
6901
|
+
preloadDepth: number; /** Prefix of every file URL. The CDN seam. `undefined`: the folder of the manifest URL, or `"/"`. */
|
|
6902
|
+
baseUrl: string | undefined; /** I/O seam. `undefined`: the browser pair plus `renderer.sync.textures`, or headless. */
|
|
6903
|
+
io: AssetsIo | undefined;
|
|
5911
6904
|
};
|
|
5912
6905
|
/**
|
|
5913
|
-
*
|
|
6906
|
+
* assets plugin state.
|
|
5914
6907
|
*/
|
|
5915
6908
|
type State = {
|
|
5916
|
-
|
|
5917
|
-
|
|
5918
|
-
|
|
5919
|
-
|
|
6909
|
+
/** `undefined` means headless: manifest only, no fetches, no textures. */io: AssetsIo | undefined; /** Empty until `onStart` read it. */
|
|
6910
|
+
manifest: Manifest; /** Asset key to the bundle that carries it. */
|
|
6911
|
+
bundleOfKey: Map<string, string>; /** Scene id to its bundle, read from the `scenes` key of `flow.features`. */
|
|
6912
|
+
bundleOfScene: Map<string, string>; /** Flow id to the feature that owns it, read from the `flows` key of `flow.features`. */
|
|
6913
|
+
featureOfFlow: Map<string, string>;
|
|
6914
|
+
records: Map<string, BundleRecord>; /** Bundles of the node entered last. They are never unloaded. */
|
|
6915
|
+
pinned: Set<string>; /** The bundle of the scene entered last. A node without `scene` keeps it. */
|
|
6916
|
+
sceneBundle: string | undefined;
|
|
6917
|
+
queue: PreloadQueue | undefined;
|
|
6918
|
+
current: NodeInfo | undefined;
|
|
6919
|
+
useCounter: number; /** Keys already reported missing, so one key warns and starts its background load once. */
|
|
6920
|
+
warned: Set<string>; /** `onEnter`, the `load` handler and the texture provider. */
|
|
6921
|
+
removers: Array<() => void>;
|
|
5920
6922
|
};
|
|
5921
6923
|
/**
|
|
5922
|
-
*
|
|
5923
|
-
* `stats()` and `capture()`, on the plugin itself.
|
|
6924
|
+
* What `usage()` reports about one loaded bundle.
|
|
5924
6925
|
*
|
|
5925
6926
|
* @example
|
|
5926
6927
|
* ```ts
|
|
5927
|
-
*
|
|
5928
|
-
* app.renderer.viewport.size().width; // 1080, reference units
|
|
5929
|
-
* app.renderer.sync.hitTest(540, 300, () => true); // 1048576
|
|
5930
|
-
* app.renderer.stats().views; // 180
|
|
5931
|
-
* app.renderer.stats().renderPasses; // 1: no filter on screen
|
|
6928
|
+
* const entry: BundleUsage = { name: "board", tier: "scene", mb: 3.5, lastUsed: 12 };
|
|
5932
6929
|
* ```
|
|
5933
6930
|
*/
|
|
5934
|
-
type
|
|
5935
|
-
|
|
5936
|
-
|
|
5937
|
-
|
|
5938
|
-
|
|
5939
|
-
/**
|
|
5940
|
-
* Resolved dependency APIs. `clock` is the time source of the frame counters.
|
|
5941
|
-
*/
|
|
5942
|
-
type Deps = {
|
|
5943
|
-
time: Api$3;
|
|
5944
|
-
lifecycle: Api$5;
|
|
5945
|
-
world: Api$1;
|
|
5946
|
-
clock: Api$6;
|
|
6931
|
+
type BundleUsage = {
|
|
6932
|
+
name: string;
|
|
6933
|
+
tier: Tier;
|
|
6934
|
+
mb: number;
|
|
6935
|
+
lastUsed: number;
|
|
5947
6936
|
};
|
|
5948
6937
|
/**
|
|
5949
|
-
* What the
|
|
6938
|
+
* What `usage()` reports: the numbers a dev overlay and the tests read.
|
|
5950
6939
|
*
|
|
5951
|
-
*
|
|
5952
|
-
*
|
|
5953
|
-
*
|
|
6940
|
+
* @example
|
|
6941
|
+
* ```ts
|
|
6942
|
+
* const report: Usage = { textureMb: 3.5, budgetMb: 192, bundles: [] };
|
|
6943
|
+
* ```
|
|
5954
6944
|
*/
|
|
5955
|
-
type
|
|
5956
|
-
|
|
5957
|
-
|
|
5958
|
-
readonly
|
|
6945
|
+
type Usage = {
|
|
6946
|
+
textureMb: number;
|
|
6947
|
+
budgetMb: number;
|
|
6948
|
+
bundles: readonly BundleUsage[];
|
|
5959
6949
|
};
|
|
5960
6950
|
/**
|
|
5961
|
-
*
|
|
6951
|
+
* assets plugin events.
|
|
6952
|
+
*
|
|
6953
|
+
* @example
|
|
6954
|
+
* ```ts
|
|
6955
|
+
* // A game reports every loaded bundle to its analytics.
|
|
6956
|
+
* createPlugin("loadReport", {
|
|
6957
|
+
* depends: [assetsPlugin],
|
|
6958
|
+
* hooks: ctx => ({ "assets:bundle-loaded": ({ bundle, mb }) => ctx.log.info("loaded", { bundle, mb }) })
|
|
6959
|
+
* }); // the board bundle logs { bundle: "board", mb: 3.5 }
|
|
6960
|
+
*
|
|
6961
|
+
* // A splash screen fills its loading bar file by file.
|
|
6962
|
+
* createPlugin("loadingBar", {
|
|
6963
|
+
* depends: [assetsPlugin],
|
|
6964
|
+
* createState: () => ({ share: 0 }),
|
|
6965
|
+
* hooks: ctx => ({
|
|
6966
|
+
* "assets:bundle-progress": ({ loaded, total }) => {
|
|
6967
|
+
* ctx.state.share = loaded / total;
|
|
6968
|
+
* }
|
|
6969
|
+
* })
|
|
6970
|
+
* }); // a board bundle of four files sets share to 0.25, 0.5, 0.75 and 1; bundle-loaded follows
|
|
6971
|
+
* ```
|
|
5962
6972
|
*/
|
|
5963
|
-
type
|
|
5964
|
-
|
|
6973
|
+
type Events = {
|
|
6974
|
+
/** Every file of a bundle is a texture now. */"assets:bundle-loaded": {
|
|
6975
|
+
bundle: string;
|
|
6976
|
+
tier: Tier;
|
|
6977
|
+
mb: number;
|
|
6978
|
+
reason: LoadReason;
|
|
6979
|
+
};
|
|
6980
|
+
/**
|
|
6981
|
+
* One more file of a running load settled. `loaded` counts the settled files (a font once, with
|
|
6982
|
+
* its pages), `total` is the file count of the bundle; the last one of a load has
|
|
6983
|
+
* `loaded === total` and comes before `assets:bundle-loaded`. An aborted load sends none.
|
|
6984
|
+
*/
|
|
6985
|
+
"assets:bundle-progress": {
|
|
6986
|
+
bundle: string;
|
|
6987
|
+
loaded: number;
|
|
6988
|
+
total: number;
|
|
6989
|
+
}; /** The textures of a bundle were destroyed. `keys` names every asset that went with it. */
|
|
6990
|
+
"assets:bundle-unloaded": {
|
|
6991
|
+
bundle: string;
|
|
6992
|
+
tier: Tier;
|
|
6993
|
+
mb: number;
|
|
6994
|
+
reason: "budget" | "request";
|
|
6995
|
+
keys: readonly string[];
|
|
6996
|
+
};
|
|
5965
6997
|
};
|
|
5966
6998
|
/**
|
|
5967
|
-
*
|
|
5968
|
-
|
|
5969
|
-
|
|
5970
|
-
|
|
5971
|
-
*
|
|
6999
|
+
* assets plugin API, `app.assets`. Bundles of textures by key: the graph decides when they arrive,
|
|
7000
|
+
* the budget decides when they leave.
|
|
7001
|
+
*
|
|
7002
|
+
* @example
|
|
7003
|
+
* ```ts
|
|
7004
|
+
* // A loading node asks for a bundle and a game system asks for one texture of it.
|
|
7005
|
+
* await app.assets.load("board");
|
|
7006
|
+
* app.assets.texture("board.cell"); // the Pixi texture, once the bundle is there
|
|
7007
|
+
* ```
|
|
5972
7008
|
*/
|
|
5973
|
-
type
|
|
7009
|
+
type Api = {
|
|
7010
|
+
/**
|
|
7011
|
+
* Loads every file of a bundle. An already loaded bundle resolves at once, a loading one joins
|
|
7012
|
+
* the running load. Headless it resolves at once and touches nothing.
|
|
7013
|
+
*
|
|
7014
|
+
* @param bundle - Name of a bundle of the manifest.
|
|
7015
|
+
* @returns A promise that resolves when every texture of the bundle exists.
|
|
7016
|
+
* @throws {Error} When the manifest has no such bundle, and when a file fails to load.
|
|
7017
|
+
* @example
|
|
7018
|
+
* ```ts
|
|
7019
|
+
* // A game plugin warms the bundle of a timed event before its popup can open.
|
|
7020
|
+
* await app.assets.load("event.halloween");
|
|
7021
|
+
* app.assets.isLoaded("event.halloween"); // true
|
|
7022
|
+
* ```
|
|
7023
|
+
*/
|
|
7024
|
+
load(bundle: string): Promise<void>;
|
|
7025
|
+
/**
|
|
7026
|
+
* Destroys the textures of a bundle and tells `renderer` that its keys are gone. A pinned
|
|
7027
|
+
* bundle and the tiers `boot` and `core` are refused with a warning. A running load is aborted.
|
|
7028
|
+
*
|
|
7029
|
+
* @param bundle - Name of a loaded bundle.
|
|
7030
|
+
* @example
|
|
7031
|
+
* ```ts
|
|
7032
|
+
* // The timed event ended: its textures go back to the GPU.
|
|
7033
|
+
* app.assets.unload("event.halloween");
|
|
7034
|
+
* app.assets.isLoaded("event.halloween"); // false
|
|
7035
|
+
* ```
|
|
7036
|
+
*/
|
|
7037
|
+
unload(bundle: string): void;
|
|
7038
|
+
/**
|
|
7039
|
+
* Tells whether every texture of a bundle exists. Headless every bundle of the manifest counts
|
|
7040
|
+
* as loaded, so a headless game never waits for a loading screen.
|
|
7041
|
+
*
|
|
7042
|
+
* @param bundle - Name of a bundle of the manifest.
|
|
7043
|
+
* @returns True when the bundle is loaded.
|
|
7044
|
+
* @example
|
|
7045
|
+
* ```ts
|
|
7046
|
+
* // `scenes` skips its loading path when the bundle of the next scene is already there.
|
|
7047
|
+
* if (!app.assets.isLoaded("board")) await app.assets.load("board");
|
|
7048
|
+
* ```
|
|
7049
|
+
*/
|
|
7050
|
+
isLoaded(bundle: string): boolean;
|
|
7051
|
+
/**
|
|
7052
|
+
* The texture of a loaded asset key. It touches the use counter of the bundle, which is what
|
|
7053
|
+
* the LRU reads. A key of a bundle that is not loaded warns once, starts a background load and
|
|
7054
|
+
* answers `undefined`; the sprites waiting for it are textured when that load lands. A key packed
|
|
7055
|
+
* in an atlas answers its slice of the page, so a game never sees the difference.
|
|
7056
|
+
*
|
|
7057
|
+
* @param key - Asset key, as `generated/assets.ts` types it.
|
|
7058
|
+
* @returns The texture, or `undefined` while the bundle is not loaded.
|
|
7059
|
+
* @example
|
|
7060
|
+
* ```ts
|
|
7061
|
+
* // A game system builds one Pixi object by hand instead of using the Sprite component.
|
|
7062
|
+
* const texture = app.assets.texture("board.cell"); // undefined until the board bundle lands
|
|
7063
|
+
* ```
|
|
7064
|
+
*/
|
|
7065
|
+
texture(key: string): Texture | undefined;
|
|
7066
|
+
/**
|
|
7067
|
+
* The font of a loaded asset key: the `.fnt` file as text and the texture of its first page.
|
|
7068
|
+
* It touches the use counter of the bundle. A font of a bundle that is not loaded, a key of
|
|
7069
|
+
* another kind and every headless run answer `undefined`.
|
|
7070
|
+
*
|
|
7071
|
+
* @param key - Asset key of a `.fnt` file, as `generated/assets.ts` types it in `FontKey`.
|
|
7072
|
+
* @returns The font, or `undefined` while its bundle is not loaded.
|
|
7073
|
+
* @example
|
|
7074
|
+
* ```ts
|
|
7075
|
+
* // `text` installs the font in the renderer when the bundle that carries it arrived.
|
|
7076
|
+
* const font = app.assets.font("ui.body"); // { fnt: 'info face="body" size=32', texture }
|
|
7077
|
+
*
|
|
7078
|
+
* if (font !== undefined) app.renderer.sync.fonts.install("ui.body", font.fnt, font.texture);
|
|
7079
|
+
* ```
|
|
7080
|
+
*/
|
|
7081
|
+
font(key: string): FontAsset | undefined;
|
|
7082
|
+
/**
|
|
7083
|
+
* The sound of a loaded audio key: its bytes exactly as they were fetched (this plugin never
|
|
7084
|
+
* decodes them) and the MIME type of its container, `"audio/mpeg"` for an `.mp3` and
|
|
7085
|
+
* `"audio/mp4"` for an `.m4a`. A packed file keeps its extension, so it keeps its type. It
|
|
7086
|
+
* touches the use counter of the bundle. A bundle that is not loaded, a key of another kind and
|
|
7087
|
+
* every headless run answer `undefined`.
|
|
7088
|
+
*
|
|
7089
|
+
* @param key - Asset key of an `.mp3` or `.m4a` file, as `generated/assets.ts` types it in
|
|
7090
|
+
* `AudioKey`.
|
|
7091
|
+
* @returns The bytes and their type, or `undefined` while its bundle is not loaded.
|
|
7092
|
+
* @example
|
|
7093
|
+
* ```ts
|
|
7094
|
+
* // The audio plugin decodes a sound, or streams a long track from a blob: URL of its bytes.
|
|
7095
|
+
* const asset = ctx.require(assetsPlugin).audio("ui.click");
|
|
7096
|
+
* // { bytes: ArrayBuffer(12 288), mime: "audio/mpeg" }
|
|
7097
|
+
* if (asset !== undefined) URL.createObjectURL(new Blob([asset.bytes], { type: asset.mime }));
|
|
7098
|
+
* ```
|
|
7099
|
+
*/
|
|
7100
|
+
audio(key: string): AudioAsset | undefined;
|
|
7101
|
+
/**
|
|
7102
|
+
* What the loaded bundles cost, sorted by name. `lastUsed` is the use counter, not a clock.
|
|
7103
|
+
*
|
|
7104
|
+
* @returns The used and allowed megabytes and one entry per loaded bundle.
|
|
7105
|
+
* @example
|
|
7106
|
+
* ```ts
|
|
7107
|
+
* // A dev overlay draws the memory bar of the game.
|
|
7108
|
+
* const { textureMb, budgetMb, bundles } = app.assets.usage();
|
|
7109
|
+
* // textureMb: 3.5, budgetMb: 192, bundles: [{ name: "board", tier: "scene", mb: 3.5, lastUsed: 12 }]
|
|
7110
|
+
* ```
|
|
7111
|
+
*/
|
|
7112
|
+
usage(): Usage;
|
|
7113
|
+
};
|
|
5974
7114
|
/**
|
|
5975
|
-
*
|
|
7115
|
+
* Resolved dependency APIs.
|
|
5976
7116
|
*/
|
|
5977
|
-
type
|
|
7117
|
+
type Deps = {
|
|
7118
|
+
flow: Api$3;
|
|
7119
|
+
renderer: Api$1;
|
|
7120
|
+
time: Api$4;
|
|
7121
|
+
};
|
|
5978
7122
|
/**
|
|
5979
|
-
*
|
|
7123
|
+
* What the kernel context offers before the deps are attached.
|
|
7124
|
+
*
|
|
7125
|
+
* `index.ts` writes `events` with an annotated `register` (core spec `14-EVENT-REGISTRATION.md`
|
|
7126
|
+
* row 8), so the two own events reach the context the kernel hands the factories and `emit` is the
|
|
7127
|
+
* kernel's own, assets-typed one. No member of the context is cast.
|
|
5980
7128
|
*/
|
|
5981
|
-
type
|
|
7129
|
+
type KernelSlice = PluginCtx<Config, State, Events> & {
|
|
7130
|
+
readonly global: object;
|
|
7131
|
+
readonly log: Log.LogApi;
|
|
7132
|
+
readonly require: Require;
|
|
7133
|
+
};
|
|
5982
7134
|
/**
|
|
5983
|
-
*
|
|
7135
|
+
* Domain context shared by the files of the plugin.
|
|
5984
7136
|
*/
|
|
5985
|
-
type
|
|
5986
|
-
|
|
5987
|
-
viewport: ViewportModule;
|
|
5988
|
-
sync: SyncModule;
|
|
5989
|
-
monitor: MonitorModule;
|
|
7137
|
+
type AssetsCtx = KernelSlice & {
|
|
7138
|
+
readonly deps: Deps;
|
|
5990
7139
|
};
|
|
5991
7140
|
/**
|
|
5992
|
-
*
|
|
7141
|
+
* Payload of the one event assets listens to.
|
|
5993
7142
|
*/
|
|
5994
|
-
type
|
|
5995
|
-
|
|
5996
|
-
|
|
7143
|
+
type FlowRest = {
|
|
7144
|
+
path: string;
|
|
7145
|
+
checkpoint: boolean;
|
|
5997
7146
|
};
|
|
5998
7147
|
//#endregion
|
|
5999
|
-
export {
|
|
7148
|
+
export { Ease as $, SaveDoc as $t, Config$2 as A, JournalEntry as At, ComponentValue as B, State$4 as Bt, Captured as C, to as Ct, Point as D, DefineNode as Dt, FilterSlot as E, Bookmark as Et, AnyComponentType as F, GuideOptions as Ft, Narrowed as G, Events$4 as Gt, EntitySnapshot as H, types_d_exports$4 as Ht, AnyComponentValue as I, Hint as It, ResourceType as J, State$5 as Jt, Owner as K, Json as Kt, ComponentHandle as L, Answer as Lt, State$2 as M, SceneIdOf as Mt, types_d_exports$2 as N, TypeTag as Nt, ViewportSize as O, FlowGraph as Ot, AnyComponent as P, Descriptor as Pt, DescriptionNode as Q, ProviderCall as Qt, ComponentSchema as R, Api$4 as Rt, types_d_exports$1 as S, slot as St, DebugSwitches as T, FeatureDescription as Tt, FrameDiff as U, Api$5 as Ut, Entity as V, Time as Vt, Mut as W, Config$5 as Wt, TagType as X, Patch as Xt, SystemDefinition as Y, types_d_exports$5 as Yt, ChangeHook as Z, PlayerStateProvider as Zt, PixiModule as _, Events$6 as _n, State$3 as _t, DefineBundles as a, Api$6 as an, ProjectionMotion as at, State$1 as b, defineFlow as bt, LoadReason as c, PauseReason as cn, TrackSegment as ct, Tier as d, Api$7 as dn, BundlesOf as dt, SaveUnreadableError as en, LayerSort as et, Usage as f, Config$7 as fn, Config$3 as ft, PixiFilter as g, Config$8 as gn, Position as gt, Config$1 as h, types_d_exports$7 as hn, GameTypes as ht, Config as i, RngState as in, NumericFields as it, Explained as j, RouteStep as jt, Api$2 as k, FlowState as kt, Manifest as l, State$6 as ln, ViewHandle as lt, Api$1 as m, State$7 as mn, FeaturePlugin as mt, BundleMap as n, StoreApi as nn, Motion as nt, Events as o, Config$6 as on, ProjectionSpec as ot, types_d_exports as p, FakeClock as pn, EmitterIdOf as pt, QueryTerm as q, Root as qt, BundleSpec as r, RngApi as rn, MotionHandle as rt, LoadBundles as s, Events$5 as sn, TrackOptions as st, Api as t, Snapshot as tn, LayerSpec as tt, State as u, types_d_exports$6 as un, Api$3 as ut, PixiSprite as v, Require as vn, TextStylesOf as vt, RenderStats as w, type as wt, Under as x, exit as xt, PixiTexture as y, types_d_exports$3 as yt, ComponentType as z, Config$4 as zt };
|