@moku-labs/game 0.15.3 → 0.15.4

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.
@@ -1,7 +1,7 @@
1
- import { Jn as FeaturePlugin, Sr as Json, Xn as AnyFlow, dr as NodeFx, gr as FakeClock, mr as ClockSource, nr as NodeTable, or as Descriptor, ur as Hint } from "./types-DlC3Ytbj.mjs";
2
- import { t as createApp } from "./index-ByvWj9OX.mjs";
3
- import { b as saveOf, v as WithGameState, y as memory } from "./types-BApuIqwq.mjs";
4
- import { a as createHeadless, i as ReproResult, n as HeadlessGame, o as runRepro, r as Repro, s as stepFrames, t as HeadlessApp } from "./headless-D0U5AXiK.mjs";
1
+ import { Jn as FeaturePlugin, Sr as Json, Xn as AnyFlow, dr as NodeFx, gr as FakeClock, mr as ClockSource, nr as NodeTable, or as Descriptor, ur as Hint } from "./types-vjWgmPqS.mjs";
2
+ import { t as createApp } from "./index-Cv5r0M9Y.mjs";
3
+ import { b as saveOf, v as WithGameState, y as memory } from "./types-VNoG7shY.mjs";
4
+ import { a as createHeadless, i as ReproResult, n as HeadlessGame, o as runRepro, r as Repro, s as stepFrames, t as HeadlessApp } from "./headless-oykCMFBV.mjs";
5
5
  import { AnyPluginInstance } from "@moku-labs/core";
6
6
 
7
7
  //#region src/plugins/clock/fake.d.ts
package/dist/testing.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { an as exit, cn as type, in as refuseStart, m as flattenFeatures, t as createApp } from "./src-xChMXzwH.mjs";
1
+ import { an as exit, cn as type, in as refuseStart, m as flattenFeatures, t as createApp } from "./src-tkKl-oCn.mjs";
2
2
  import { n as saveOf, t as memory } from "./memory-CWlqhS7l.mjs";
3
3
  import { t as fakeClock } from "./fake-6mazmHK3.mjs";
4
4
  import { i as stepFrames, r as runRepro, t as createHeadless } from "./headless-BA6ffzqD.mjs";
@@ -1,4 +1,4 @@
1
- import { Cn as MotionCause, En as ProjectionMotion, Hr as Api, Jt as TransformValue, Mn as Api$1, Mt as ViewportSize, Qr as Require, Sn as Motion, Sr as Json, T as Api$3, Tn as NumericFields, Zt as Api$2, fr as Answer, in as AnyComponentValue, jn as ViewHandle, ln as Entity, nn as AnyComponent, or as Descriptor, pr as Api$5, rn as AnyComponentType, s as Events$1, sn as ComponentType, t as Api$4, ur as Hint, vn as TagType, wn as MotionHandle, yn as ChangeHook } from "./types-DlC3Ytbj.mjs";
1
+ import { Cn as MotionCause, En as ProjectionMotion, Hr as Api, Jt as TransformValue, Mn as Api$1, Mt as ViewportSize, Qr as Require, Sn as Motion, Sr as Json, T as Api$3, Tn as NumericFields, Zt as Api$2, fr as Answer, in as AnyComponentValue, jn as ViewHandle, ln as Entity, nn as AnyComponent, or as Descriptor, pr as Api$5, rn as AnyComponentType, s as Events$1, sn as ComponentType, t as Api$4, ur as Hint, vn as TagType, wn as MotionHandle, yn as ChangeHook } from "./types-vjWgmPqS.mjs";
2
2
  import { Log } from "@moku-labs/common/browser";
3
3
  import { PluginCtx } from "@moku-labs/core";
4
4
 
@@ -2504,12 +2504,6 @@ type Root = {
2504
2504
  h: number;
2505
2505
  } | undefined;
2506
2506
  pending?: boolean;
2507
- /**
2508
- * The release of the hold on the feature the root shows (`world.ecs.hold`): the feature of a
2509
- * popup's component, or of a JSX projection. Called when the sweep drops the root. Optional
2510
- * like `pending`: `mountRoot` always writes it.
2511
- */
2512
- held?: (() => void) | undefined;
2513
2507
  };
2514
2508
  /**
2515
2509
  * Where the focus ring was last drawn: the layer and order of its root, the rect grown by the
@@ -2680,9 +2674,7 @@ type JsxState = {
2680
2674
  written: number; /** The wall times of the last taps on a `DebugTrigger` view, five at the most. */
2681
2675
  debugTaps: number[]; /** Whether `ui:debug-trigger-no-agent` was logged: once per app. */
2682
2676
  debugWarned: boolean; /** Elements that carry `Overlay`, from their enter to their despawn (spec 22 O4.5). */
2683
- overlays: Set<Entity>; /** Component name to the feature whose `ui` key lists it: the feature a popup root holds. */
2684
- featureOfComponent: Map<string, string>; /** Projection name to the feature that registered it: the feature a JSX root holds. */
2685
- featureOfProjection: Map<string, string>;
2677
+ overlays: Set<Entity>;
2686
2678
  };
2687
2679
  /**
2688
2680
  * One node of the snapshot `tree()` answers with. `rect` is natural: under a `fit` ancestor it
@@ -1,5 +1,5 @@
1
- import { A as PixiSprite, An as TrackSegment, Hr as Api$1, Mn as Api$2, Nt as FilterCache, O as PixiFilter, On as ReducedMotion, Ot as FilterSlot, Qr as Require, Rr as Events$5, Sr as Json, T as Api$4, Tn as NumericFields, Zr as Events$2, Zt as Api$3, an as ComponentHandle, ar as TypeTag, in as AnyComponentValue, j as PixiTexture, jt as Point, k as PixiModule, kn as TrackOptions, kt as FilterSlotInputs, ln as Entity, nn as AnyComponent, or as Descriptor, s as Events$1, sn as ComponentType, t as Api$5, v as Events$4, wn as MotionHandle, xn as Ease, xr as Events$3, yr as Api$6 } from "./types-DlC3Ytbj.mjs";
2
- import { I as I18nApi, J as StylesState, S as TextApi, c as UiNode, i as Finding, j as InputApi, n as ElementChange, o as JsxState, r as ElementMotion } from "./types-yLdzHyTl.mjs";
1
+ import { A as PixiSprite, An as TrackSegment, Hr as Api$1, Mn as Api$2, Nt as FilterCache, O as PixiFilter, On as ReducedMotion, Ot as FilterSlot, Qr as Require, Rr as Events$5, Sr as Json, T as Api$4, Tn as NumericFields, Zr as Events$2, Zt as Api$3, an as ComponentHandle, ar as TypeTag, in as AnyComponentValue, j as PixiTexture, jt as Point, k as PixiModule, kn as TrackOptions, kt as FilterSlotInputs, ln as Entity, nn as AnyComponent, or as Descriptor, s as Events$1, sn as ComponentType, t as Api$5, v as Events$4, wn as MotionHandle, xn as Ease, xr as Events$3, yr as Api$6 } from "./types-vjWgmPqS.mjs";
2
+ import { I as I18nApi, J as StylesState, S as TextApi, c as UiNode, i as Finding, j as InputApi, n as ElementChange, o as JsxState, r as ElementMotion } from "./types-B6YgPkY8.mjs";
3
3
  import { Log } from "@moku-labs/common/browser";
4
4
  import { PluginCtx } from "@moku-labs/core";
5
5
  import { Node, Yoga } from "yoga-layout/load";
@@ -1,5 +1,5 @@
1
- import { Sr as Json, jr as Snapshot } from "./types-DlC3Ytbj.mjs";
2
- import { t as HeadlessApp } from "./headless-D0U5AXiK.mjs";
1
+ import { Sr as Json, jr as Snapshot } from "./types-vjWgmPqS.mjs";
2
+ import { t as HeadlessApp } from "./headless-oykCMFBV.mjs";
3
3
  import { Log } from "@moku-labs/common/browser";
4
4
 
5
5
  //#region src/plugins/flow/doors/types.d.ts
@@ -1,7 +1,7 @@
1
- import { At as Orientation, Dr as PlayerStateProvider, E as Config$2, Jn as FeaturePlugin, Mr as SnapshotOf, Or as ProviderCall, Pn as Config$1, Sr as Json, Xn as AnyFlow, a as Config$3, ar as TypeTag, br as Config, d as Manifest, gr as FakeClock, k as PixiModule, kr as SaveDoc, mr as ClockSource, n as AssetsIo } from "./types-DlC3Ytbj.mjs";
2
- import { o as AudioContextLike, s as Config$4 } from "./types-CsMLBPf-.mjs";
3
- import { H as TextKey, I as I18nApi, V as StringTable } from "./types-yLdzHyTl.mjs";
4
- import { ft as platformPlugin, i as screen, mt as PlatformProvider, t as createApp, wt as audioPlugin, xt as effectsPlugin } from "./index-ByvWj9OX.mjs";
1
+ import { At as Orientation, Dr as PlayerStateProvider, E as Config$2, Jn as FeaturePlugin, Mr as SnapshotOf, Or as ProviderCall, Pn as Config$1, Sr as Json, Xn as AnyFlow, a as Config$3, ar as TypeTag, br as Config, d as Manifest, gr as FakeClock, k as PixiModule, kr as SaveDoc, mr as ClockSource, n as AssetsIo } from "./types-vjWgmPqS.mjs";
2
+ import { o as AudioContextLike, s as Config$4 } from "./types-CiTr21gu.mjs";
3
+ import { H as TextKey, I as I18nApi, V as StringTable } from "./types-B6YgPkY8.mjs";
4
+ import { ft as platformPlugin, i as screen, mt as PlatformProvider, t as createApp, wt as audioPlugin, xt as effectsPlugin } from "./index-Cv5r0M9Y.mjs";
5
5
  import { AnyPluginInstance } from "@moku-labs/core";
6
6
 
7
7
  //#region src/plugins/model/store/providers/memory.d.ts
@@ -3365,31 +3365,12 @@ type FeatureTextStyles = {
3365
3365
  readonly kind: "textStyles";
3366
3366
  readonly map: Record<string, object>;
3367
3367
  };
3368
- /**
3369
- * When the systems of a feature run. `"always"` (the default): every frame, as before. `"active"`: only while the
3370
- * feature is active — a node of it is on the flow stack, a scene it declares is mounted, a view of a projection it
3371
- * registered exists, or `ui` holds a popup or a JSX root of it — leaving included, until the last view is gone.
3372
- * Skipping changes nothing the flow sees: systems never write the model. The systems the engine's plugins
3373
- * register always run.
3374
- *
3375
- * @example
3376
- * ```ts
3377
- * // features/board/index.ts: the board's four systems stop on the ward and on the splash.
3378
- * export const boardFeature = defineFeature("board", {
3379
- * flows: [mergeBoard],
3380
- * systems: [looks, fade, leaving, veil],
3381
- * systemsRun: "active"
3382
- * });
3383
- * ```
3384
- */
3385
- type SystemsRun = "always" | "active";
3386
3368
  /**
3387
3369
  * What a feature brings to the game. The logic keys are typed here; the keys of later milestones
3388
3370
  * pass through the index signature until their plugin types them. `flow` stores them untouched,
3389
3371
  * and `logicOnly` drops them. The V2 screen plugins read five of them through the index signature:
3390
3372
  * `projections`, `systems` and `components` (`world`), `scenes` (`scenes`, `assets`, and the
3391
- * scene-id check of `flow`) and `assets` (`assets`). `world` also reads the typed `systemsRun`.
3392
- * The V3 interface keys are typed below.
3373
+ * scene-id check of `flow`) and `assets` (`assets`). The V3 interface keys are typed below.
3393
3374
  *
3394
3375
  * @example
3395
3376
  * ```ts
@@ -3419,8 +3400,13 @@ type FeatureDescription = {
3419
3400
  strings?: Record<string, CompiledMessagesLike | (() => Promise<unknown>)>; /** Text styles the feature owns, read by `text`. */
3420
3401
  textStyles?: FeatureTextStyles; /** Particle emitters the feature owns, read by `effects`. */
3421
3402
  emitters?: readonly FeatureEmitter[]; /** Filters the feature puts on its interface components, read by `effects`. */
3422
- filters?: readonly FeatureFilter[]; /** When the feature's systems run, read by `world`. Absent is `"always"`. */
3423
- systemsRun?: SystemsRun;
3403
+ filters?: readonly FeatureFilter[];
3404
+ /**
3405
+ * The key of 0.15.3, removed: typed `never`, so a game that still writes it is told. A system
3406
+ * now runs while its query has an entity, and `scenes` on `system()` limits it to some scenes:
3407
+ * delete the key (world README, "When a system runs").
3408
+ */
3409
+ systemsRun?: never;
3424
3410
  [later: string]: unknown;
3425
3411
  };
3426
3412
  /**
@@ -3620,7 +3606,7 @@ declare function to<const TargetName extends string, Payload, Output>(target: Ta
3620
3606
  */
3621
3607
  declare function slot(name: string): SlotNode;
3622
3608
  declare namespace types_d_exports$4 {
3623
- export { Allow, Answer, AnyFlow, AnyNode, AnyNodeContext, Api$3 as Api, Bookmark, BundlesOf, CheckedEdges, Config$4 as Config, Contribution, DefineNode, Deps$4 as Deps, Descriptor, Edges, Effect, EffectHandler, EffectOptions, EmitterIdOf, EnterCallback, Events$4 as Events, Exit, FeatureDescription, FeatureEmitter, FeatureFilter, FeaturePlugin, FeatureTree, FeaturesApi, FlowCtx, FlowDefinition, FlowEntry, FlowGraph, FlowKit, FlowSpec, FlowState, FxApi, FxHandler, GameState, GameTypes, GateApi, GraphError, GraphNode, GuideOptions, Hint, HudKeysOf, InboxApi, JournalEntry, KernelSlice$4 as KernelSlice, LifecycleChanged, Mapped, NodeContext, NodeDefinition, NodeFx, NodeInfo, NodeSpec, NodeTable, OutcomeTags, Position, RecoverContext, RegisteredFeature, Result, RouteStep, RunnerApi, SceneIdOf, SlotNode, Stage, State$4 as State, StringTableLike, SystemsRun, Target, TextStylesOf, TypeTag, WorldEvent };
3609
+ export { Allow, Answer, AnyFlow, AnyNode, AnyNodeContext, Api$3 as Api, Bookmark, BundlesOf, CheckedEdges, Config$4 as Config, Contribution, DefineNode, Deps$4 as Deps, Descriptor, Edges, Effect, EffectHandler, EffectOptions, EmitterIdOf, EnterCallback, Events$4 as Events, Exit, FeatureDescription, FeatureEmitter, FeatureFilter, FeaturePlugin, FeatureTree, FeaturesApi, FlowCtx, FlowDefinition, FlowEntry, FlowGraph, FlowKit, FlowSpec, FlowState, FxApi, FxHandler, GameState, GameTypes, GateApi, GraphError, GraphNode, GuideOptions, Hint, HudKeysOf, InboxApi, JournalEntry, KernelSlice$4 as KernelSlice, LifecycleChanged, Mapped, NodeContext, NodeDefinition, NodeFx, NodeInfo, NodeSpec, NodeTable, OutcomeTags, Position, RecoverContext, RegisteredFeature, Result, RouteStep, RunnerApi, SceneIdOf, SlotNode, Stage, State$4 as State, StringTableLike, Target, TextStylesOf, TypeTag, WorldEvent };
3624
3610
  }
3625
3611
  /**
3626
3612
  * flow plugin events.
@@ -5236,15 +5222,18 @@ type SystemContext = {
5236
5222
  readonly snapshot: Snapshot;
5237
5223
  readonly time: Readonly<Time>;
5238
5224
  /**
5239
- * True on the first run after one or more runs of this system were skipped because its feature was not active
5240
- * (`systemsRun: "active"`). Nothing was kept for it while it slept: `world.changed()` holds this frame's changes
5241
- * only, so a system that caches what it drew rebuilds from the components when this is true. Always false for a
5242
- * system that always runs. Read it during the run.
5225
+ * True on the first run after one or more of this system's runs were skipped: its query had no entity, or none of
5226
+ * its `scenes` was mounted or leaving. Nothing was kept for it while it slept: `world.changed()` holds this frame's
5227
+ * changes only, so a system that caches what it drew, or remembers what it saw last, starts over from the
5228
+ * components when this is true. False on every other run. Read it during the run.
5243
5229
  */
5244
5230
  readonly resumed: boolean;
5245
5231
  };
5246
5232
  /**
5247
- * A system definition, typed over its query tuple.
5233
+ * A system definition, typed over its query tuple. A system with a query runs only while its query
5234
+ * can match: before each run the world looks at the store of every term, and when one of them holds
5235
+ * no entity the run is skipped (no walk, no `mut()` mark, no timing). A system without a query
5236
+ * always runs. `scenes` limits either kind to some scenes.
5248
5237
  *
5249
5238
  * @example
5250
5239
  * ```ts
@@ -5263,10 +5252,42 @@ type SystemDefinition<Terms extends readonly QueryTerm[]> = {
5263
5252
  readonly phase: WorldPhase;
5264
5253
  readonly query: Terms;
5265
5254
  /**
5266
- * Runs the system once a frame in its phase. `entities` walks the declared `query` once; every
5267
- * row it hands out is the same array, rewritten for the next row, so a row is valid until the
5268
- * next row. A system that keeps a row copies what it keeps: `const [entity, transform] = row`.
5269
- * `ecs.query()` called inside `run` hands out a new array per row.
5255
+ * The scenes the system runs on: it runs only while one of them is mounted or leaving. A scene
5256
+ * leaves from the switch that replaces it until nothing carries `Exiting` any more, so the exits
5257
+ * of its JSX elements play to the end; any exit counts, also one of the next scene, so a leave may
5258
+ * last longer than the scene's own exits. The next scene's entities already stand during a leave:
5259
+ * a query that matches them too hands them to the system, so it writes only its own scene's
5260
+ * entities. Left out, the system runs on every scene. Read once, when the system registers. A dev
5261
+ * build throws at start for a name no feature declares as a scene. For a system without a query,
5262
+ * or one whose entities stand on every screen.
5263
+ *
5264
+ * @example
5265
+ * ```ts
5266
+ * // features/board/world/systems/idle.ts: no query, so `scenes` alone keeps it to the board.
5267
+ * const BoardIdle = resource("BoardIdle", { ms: 0 });
5268
+ * export const idle = system({
5269
+ * name: "board.idle",
5270
+ * phase: "animate",
5271
+ * query: [],
5272
+ * scenes: ["board"],
5273
+ * run: (_rows, { res, time }) => {
5274
+ * res(BoardIdle).ms += time.delta;
5275
+ * }
5276
+ * });
5277
+ * // On the ward it is not run and BoardIdle.ms stands still. On the board it grows by each
5278
+ * // frame's delta. After the switch to the ward it runs while the board's elements play their
5279
+ * // exit, then not any more.
5280
+ * ```
5281
+ */
5282
+ readonly scenes?: readonly string[];
5283
+ /**
5284
+ * Runs the system once a frame in its phase, unless it is skipped: a term of its `query` has no
5285
+ * entity, or none of its `scenes` is mounted or leaving. `entities` walks the declared `query`
5286
+ * once; every row it hands out is the same array, rewritten for the next row, so a row is valid
5287
+ * until the next row. A system that keeps a row copies what it keeps:
5288
+ * `const [entity, transform] = row`. `ecs.query()` called inside `run` hands out a new array per
5289
+ * row. Work that must happen when the query has no entity (end a press, reset a resource) belongs
5290
+ * in a system without a query.
5270
5291
  */
5271
5292
  run(entities: Iterable<QueryTuple<Terms>>, ctx: SystemContext): void;
5272
5293
  };
@@ -5321,23 +5342,28 @@ type SystemFor<Player, Session> = <const Terms extends readonly QueryTerm[]>(def
5321
5342
  type AnySystem = {
5322
5343
  readonly name: string;
5323
5344
  readonly phase: WorldPhase;
5324
- readonly query: readonly QueryTerm[]; /** As `SystemDefinition.run`: one row array per walk, valid until the next row. */
5345
+ readonly query: readonly QueryTerm[]; /** As `SystemDefinition.scenes`: the scenes it runs on, every scene when left out. */
5346
+ readonly scenes?: readonly string[]; /** As `SystemDefinition.run`: one row array per walk, valid until the next row. */
5325
5347
  run(entities: Iterable<readonly unknown[]>, ctx: SystemContext): void;
5326
5348
  };
5349
+ /** One component store, keyed by entity: a component's value, `true` for a tag. */
5350
+ type ComponentStore = Map<Entity, object | true>;
5327
5351
  /**
5328
5352
  * One registered system, the frame it was registered in, its query resolved once, the walk the
5329
- * runner resets for every run and its scope. A system registered during a frame runs from the
5330
- * next frame on. Module state: no game reads it.
5353
+ * runner resets for every run, the stores its check reads and its scenes. A system registered
5354
+ * during a frame runs from the next frame on. Module state: no game reads it.
5331
5355
  */
5332
5356
  type SystemEntry = {
5333
5357
  readonly definition: AnySystem;
5334
5358
  readonly frame: number;
5335
5359
  readonly plan: QueryPlan;
5336
5360
  /**
5337
- * The index of its feature in `ScopeState` for a system of a feature with `systemsRun: "active"`, -1 for a
5338
- * system that always runs.
5361
+ * The store of each term, in term order, kept once it exists: a store lives as long as the
5362
+ * systems of its world, so the check before a run reads sizes and looks nothing up.
5363
+ * `undefined` for a term no entity carried yet.
5339
5364
  */
5340
- readonly scope: number; /** The system's last chance to run was skipped because its feature was not active. */
5365
+ readonly stores: Array<ComponentStore | undefined>; /** The scenes it runs on, read once at registration; `undefined` for every scene. */
5366
+ readonly scenes: readonly string[] | undefined; /** Its last chance to run was skipped: the next run is told so with `resumed`. */
5341
5367
  skipped: boolean;
5342
5368
  /**
5343
5369
  * The walk of the declared query, made at registration; `undefined` for an empty query, which
@@ -5346,27 +5372,14 @@ type SystemEntry = {
5346
5372
  walk?: SystemWalk | undefined;
5347
5373
  };
5348
5374
  /**
5349
- * Which features are active, kept so a phase reads one byte per scoped system. Indexed by the feature's position in
5350
- * `flow.features.all()`. Filled in `onStart`, sized once; nothing in it grows per frame.
5375
+ * Which scene is mounted and which are still leaving, as `scenes` tells the world with
5376
+ * `ecs.setScene`: what a system with `scenes` runs on. Changed at a scene switch only; a phase
5377
+ * reads it.
5351
5378
  */
5352
- type ScopeState = {
5353
- /** Feature names by index, registration order. */names: string[]; /** Feature name to index. */
5354
- index: Map<string, number>; /** Projection name to the index of the feature that registered it (rule V). */
5355
- projections: Map<string, number>; /** Flow id to node name to the owner's index (rule N); built from one `describe()` when first needed. */
5356
- owners: Map<string, Map<string, number>> | undefined; /** `describe()` threw: every feature counts as on the stack while the stack is not empty (fail-safe). */
5357
- ownersFailed: boolean; /** The `flow.state()` object the stack flags were read from. */
5358
- flow: FlowState | undefined; /** 1 while a frame of the stack addresses a node the feature owns. */
5359
- onStack: Uint8Array; /** The holds `scenes` and `ui` keep on the feature. */
5360
- holds: Int32Array; /** The live and leaving views of the projections the feature registered. */
5361
- views: Int32Array; /** 1 while the feature is active, as of the last evaluation. */
5362
- active: Uint8Array; /** A hold or a view count moved since the last evaluation. */
5363
- dirty: boolean; /** At least one registered system runs only while its feature is active. */
5364
- scoped: boolean;
5365
- /**
5366
- * Moves on at every start and every stop: a hold's release remembers the generation it was
5367
- * taken in, and a release from an older one changes nothing (spec 28 W7.1).
5368
- */
5369
- generation: number;
5379
+ type SceneState$1 = {
5380
+ /** The mounted scene, `undefined` before the first switch. */mounted: string | undefined; /** The scenes a switch replaced while something still carries `Exiting`, oldest first; no repeats. */
5381
+ leaving: string[]; /** The scene ids the features declare, read at start for the dev check of `scenes`; `undefined` before. */
5382
+ declared: ReadonlySet<string> | undefined;
5370
5383
  };
5371
5384
  /**
5372
5385
  * A structural change queued while a phase runs. Applied in call order when the phase ends.
@@ -5420,7 +5433,7 @@ type PhaseContext = {
5420
5433
  /** The ecs API the systems of this world receive. */readonly world: EcsApi; /** The resource reader, one function per world. */
5421
5434
  readonly res: <Value extends object>(resource: ResourceType<Value>) => Value; /** The frozen model snapshot of the running frame. */
5422
5435
  snapshot: Snapshot; /** The running frame's `Time`. */
5423
- time: Readonly<Time>; /** Written before every run: the system's runs before this one were skipped for its scope. */
5436
+ time: Readonly<Time>; /** Written before every run: the system's last chance to run before this one was skipped. */
5424
5437
  resumed: boolean;
5425
5438
  };
5426
5439
  /**
@@ -5623,8 +5636,8 @@ type EcsState = {
5623
5636
  walkReported: boolean; /** The plans of `ecs.query()` by the type of their first term, made once per tuple of types. */
5624
5637
  queryPlans: Map<AnyComponentType, KnownPlan[]>; /** The entity lists of ended `ecs.query()` walks, taken by the next walks: no key copy per call. */
5625
5638
  walkLists: Entity[][]; /** How many entities were despawned since the world began: the sweep looks for dead elements when it moved. */
5626
- despawns: number; /** Which features are active, for the systems of the features with `systemsRun: "active"`. */
5627
- scope: ScopeState;
5639
+ despawns: number; /** The mounted scene and the leaving ones, for the systems with `scenes`. */
5640
+ scene: SceneState$1;
5628
5641
  };
5629
5642
  /**
5630
5643
  * ecs module API, `app.world.ecs`: the screen as data. Plain-object components in one store per
@@ -5641,55 +5654,23 @@ type EcsState = {
5641
5654
  type EcsApi = {
5642
5655
  /**
5643
5656
  * Registers a system at the end of its phase list. A system registered during a frame runs
5644
- * from the next frame on.
5657
+ * from the next frame on. In every frame the system is skipped while a term of its query has no
5658
+ * entity, or while none of its `scenes` is mounted or leaving.
5645
5659
  *
5646
5660
  * @param definition - The system, built with `system()`.
5647
5661
  * @returns The remover; calling it twice is a no-op.
5648
- * @throws {Error} When a system of that name is already registered.
5662
+ * @throws {Error} When a system of that name is already registered, and in a dev build after
5663
+ * start for a name in `scenes` that no feature declares as a scene.
5649
5664
  * @example
5650
5665
  * ```ts
5651
5666
  * // `text` registers its layout system when the plugin starts.
5652
5667
  * const world = ctx.require(worldPlugin);
5653
- * const off = world.ecs.system(measureLabels); // runs from the next frame
5668
+ * const off = world.ecs.system(measureLabels); // runs from the next frame on which a label exists
5654
5669
  *
5655
5670
  * off(); // the plugin stops
5656
5671
  * ```
5657
5672
  */
5658
5673
  system(definition: AnySystem): () => void;
5659
- /**
5660
- * Keeps a feature active while something of it is shown that the world cannot see: `scenes` holds the feature that
5661
- * declares the mounted scene, `ui` the feature of each popup or JSX root it holds. Holds are counted; the returned
5662
- * release is idempotent. A name that is not a registered feature holds nothing (a dev warning
5663
- * `world:hold-unknown-feature`). Takes effect at the next phase that runs systems.
5664
- *
5665
- * @param feature - The feature to keep active.
5666
- * @returns The release.
5667
- * @example
5668
- * ```ts
5669
- * // scenes, as it mounts the scene "ward" that the feature "screens" declares:
5670
- * const release = ctx.require(worldPlugin).ecs.hold("screens");
5671
- * app.world.ecs.isActive("screens"); // true
5672
- * release(); // the next scene replaced the ward
5673
- * app.world.ecs.isActive("screens"); // false: no node, view or hold of "screens" is left
5674
- * ```
5675
- */
5676
- hold(feature: string): () => void;
5677
- /**
5678
- * Whether a feature is active now: a node of it is on the flow stack, a scene it declares is mounted, a view of a
5679
- * projection it registered exists (leaving included), or `ui` holds a popup or JSX root of it. Answers for any
5680
- * feature, whatever its `systemsRun`; the systems of a feature with `systemsRun: "active"` run exactly while this
5681
- * is true. False for a name that is not a feature.
5682
- *
5683
- * @param feature - The feature name.
5684
- * @returns True while it is active.
5685
- * @example
5686
- * ```ts
5687
- * // A test of the board: the graph rests at the board's awaitIntent and the shop sheet is closed.
5688
- * app.world.ecs.isActive("board"); // true: a node of the board is on the stack, its views are drawn
5689
- * app.world.ecs.isActive("shop"); // false: no view of the sheet, no node of the shop on the stack
5690
- * ```
5691
- */
5692
- isActive(feature: string): boolean;
5693
5674
  /**
5694
5675
  * Creates an entity. The id is reserved and returned at once, also inside a phase, where the
5695
5676
  * components are attached when the phase ends.
@@ -5990,6 +5971,24 @@ type EcsApi = {
5990
5971
  * ```
5991
5972
  */
5992
5973
  setMode(mode: WorldMode): void;
5974
+ /**
5975
+ * Tells the world which scene is mounted now, for the systems that list `scenes`. The scene
5976
+ * mounted before it, and any scene still leaving, keep their systems running until nothing
5977
+ * carries `Exiting` any more: the elements of their JSX views played their exit (the views themselves
5978
+ * go at the switch). Each system reads it
5979
+ * before its next run. The same scene again changes nothing. `scenes` calls it in the block that
5980
+ * mounts a scene; a game never needs to.
5981
+ *
5982
+ * @param scene - The id of the scene now mounted.
5983
+ * @example
5984
+ * ```ts
5985
+ * // scenes, in the block that replaces the ward with the board:
5986
+ * ctx.require(worldPlugin).ecs.setScene("board");
5987
+ * // In the next frame a system with scenes: ["board"] runs. One with scenes: ["ward"] runs
5988
+ * // until the ward's elements played their exit, then sleeps.
5989
+ * ```
5990
+ */
5991
+ setScene(scene: string): void;
5993
5992
  /**
5994
5993
  * The component type behind a name. A type registers itself on first use, so a name nobody
5995
5994
  * wrote, read or queried yet is unknown.
@@ -6130,22 +6129,6 @@ type EcsInternal = {
6130
6129
  * Drops every entity, component, resource, system and hook.
6131
6130
  */
6132
6131
  clear(): void;
6133
- /**
6134
- * Counts a new view of a projection for the feature that registered it (rule V of `systemsRun`).
6135
- *
6136
- * @param projection - The projection name.
6137
- */
6138
- viewCame(projection: string): void;
6139
- /**
6140
- * Takes back the count of a despawned view; never below 0.
6141
- *
6142
- * @param projection - The projection name.
6143
- */
6144
- viewLeft(projection: string): void;
6145
- /**
6146
- * Drops every view count: the projections were cleared.
6147
- */
6148
- viewsCleared(): void;
6149
6132
  };
6150
6133
  //#endregion
6151
6134
  //#region src/plugins/world/ecs/systems.d.ts
@@ -9321,9 +9304,9 @@ type HostState = {
9321
9304
  */
9322
9305
  uninstallPasses: (() => void) | undefined;
9323
9306
  /**
9324
- * Swaps Pixi's own bitmap text pipe back in. Set once per host state when the renderer's pipe,
9325
- * whose SDF shader applies the alpha once, went into Pixi's registry, so a restore reuses it;
9326
- * `undefined` while inert and when the module lacked the pieces.
9307
+ * Swaps Pixi's own bitmap text and batch pipes back in and drops the SDF batcher. Set once per host state when the
9308
+ * renderer's batched distance-field text went into Pixi's registry, so a restore reuses it; `undefined` while inert
9309
+ * and when the module lacked the pieces.
9327
9310
  */
9328
9311
  uninstallSdfText: (() => void) | undefined;
9329
9312
  /**
@@ -10536,9 +10519,9 @@ declare namespace types_d_exports$2 {
10536
10519
  * the render textures and matrices the kept pictures are drawn with (an `AlphaFilter` runs their pre-pass), and the
10537
10520
  * extension registry with the seven classes the counting of a dev or debug build swaps
10538
10521
  * (the three WebGPU draw classes, the filter system, both texture systems and the batcher pipe whose `buildStart`
10539
- * begins every instruction rebuild; production swaps the encoder alone, to count the passes) and the bitmap text
10540
- * pipe `host` swaps for one whose SDF shader applies the alpha once (with the WGSL and GLSL shader
10541
- * pieces it compiles that shader from). The module object
10522
+ * begins every instruction rebuild; production swaps the encoder alone, to count the passes), and the pieces of
10523
+ * batched distance-field text `host` installs: Pixi's `Batcher` with its geometry and buffers, the bitmap text pipe
10524
+ * and the batcher pipe it subclasses, and the WGSL and GLSL shader pieces of the batch's SDF shader. The module object
10542
10525
  * never arrives through a static import: `config.loadPixi()` returns it, so a game without a screen carries no
10543
10526
  * Pixi in its bundle.
10544
10527
  *
@@ -10550,10 +10533,11 @@ declare namespace types_d_exports$2 {
10550
10533
  * pixi.FilterSystem.extension.name; // "filter"
10551
10534
  * pixi.BatcherPipe.extension.name; // "batch"
10552
10535
  * pixi.BitmapTextPipe.extension.name; // "bitmapText"
10536
+ * pixi.ExtensionType.Batcher; // "batcher"
10553
10537
  * pixi.defaultFilterVert.startsWith("in vec2 aPosition;"); // true
10554
10538
  * ```
10555
10539
  */
10556
- type PixiModule = Pick<typeof import("pixi.js"), "AlphaFilter" | "Application" | "BatcherPipe" | "BitmapFont" | "BitmapText" | "BitmapTextPipe" | "BlurFilter" | "Bounds" | "Cache" | "ColorMatrixFilter" | "Container" | "DisplacementFilter" | "Filter" | "FilterSystem" | "GlProgram" | "GlTextureSystem" | "GpuBatchAdaptor" | "GpuEncoderSystem" | "GpuGraphicsAdaptor" | "GpuProgram" | "GpuTextureSystem" | "Graphics" | "Matrix" | "NineSliceSprite" | "NoiseFilter" | "Particle" | "ParticleContainer" | "Rectangle" | "RenderGroupPipe" | "RenderTexture" | "Shader" | "Sprite" | "Texture" | "TexturePool" | "UniformGroup" | "bitmapFontTextParser" | "bitmapFontXMLStringParser" | "colorBit" | "colorBitGl" | "compileHighShaderGlProgram" | "compileHighShaderGpuProgram" | "defaultFilterVert" | "extensions" | "generateTextureBatchBit" | "generateTextureBatchBitGl" | "getBatchSamplersUniformGroup" | "localUniformMSDFBit" | "localUniformMSDFBitGl" | "mSDFBit" | "mSDFBitGl" | "roundPixelsBit" | "roundPixelsBitGl">;
10540
+ type PixiModule = Pick<typeof import("pixi.js"), "AlphaFilter" | "Application" | "Batcher" | "BatcherPipe" | "BitmapFont" | "BitmapText" | "BitmapTextPipe" | "BlurFilter" | "Bounds" | "Buffer" | "BufferUsage" | "Cache" | "ColorMatrixFilter" | "Container" | "DefaultBatcher" | "DisplacementFilter" | "ExtensionType" | "Filter" | "FilterSystem" | "Geometry" | "GlProgram" | "GlTextureSystem" | "GpuBatchAdaptor" | "GpuEncoderSystem" | "GpuGraphicsAdaptor" | "GpuProgram" | "GpuTextureSystem" | "Graphics" | "Matrix" | "NineSliceSprite" | "NoiseFilter" | "Particle" | "ParticleContainer" | "Rectangle" | "RenderGroupPipe" | "RenderTexture" | "Shader" | "Sprite" | "Texture" | "TexturePool" | "UniformGroup" | "bitmapFontTextParser" | "bitmapFontXMLStringParser" | "colorBit" | "colorBitGl" | "compileHighShaderGlProgram" | "compileHighShaderGpuProgram" | "defaultFilterVert" | "extensions" | "generateTextureBatchBit" | "generateTextureBatchBitGl" | "getBatchSamplersUniformGroup" | "mSDFBit" | "mSDFBitGl" | "roundPixelsBit" | "roundPixelsBitGl">;
10557
10541
  /**
10558
10542
  * One Pixi application: renderer, canvas and stage.
10559
10543
  */
@@ -11184,13 +11168,7 @@ type State$1 = {
11184
11168
  */
11185
11169
  named: readonly string[];
11186
11170
  owner: SceneOwner; /** The remover of the `onEnter` callback: set in `onStart`, run in `onStop`. */
11187
- teardown: (() => void) | undefined; /** Scene id to the feature that declares it, filled in `onStart`. */
11188
- owners: Map<string, string>;
11189
- /**
11190
- * The release of the hold on the mounted scene's feature (`world.ecs.hold`), so the systems of a
11191
- * feature with `systemsRun: "active"` run while its scene is mounted.
11192
- */
11193
- held: (() => void) | undefined;
11171
+ teardown: (() => void) | undefined;
11194
11172
  };
11195
11173
  /**
11196
11174
  * scenes plugin events.
@@ -1,9 +1,9 @@
1
- import { Ir as Api$3, T as Api, Zt as Api$1, yr as Api$2 } from "./types-DlC3Ytbj.mjs";
2
- import { P as AnimApi, r as UiApi } from "./types-CsMLBPf-.mjs";
3
- import { j as InputApi } from "./types-yLdzHyTl.mjs";
4
- import { r as Repro } from "./headless-D0U5AXiK.mjs";
5
- import { a as InputOf, r as ControlApp } from "./types-B0bmoyRT.mjs";
6
- import { t as commands } from "./commands-31d2BEwc.mjs";
1
+ import { Ir as Api$3, T as Api, Zt as Api$1, yr as Api$2 } from "./types-vjWgmPqS.mjs";
2
+ import { P as AnimApi, r as UiApi } from "./types-CiTr21gu.mjs";
3
+ import { j as InputApi } from "./types-B6YgPkY8.mjs";
4
+ import { r as Repro } from "./headless-oykCMFBV.mjs";
5
+ import { a as InputOf, r as ControlApp } from "./types-DY_wOvUF.mjs";
6
+ import { t as commands } from "./commands-CvWBaoEi.mjs";
7
7
 
8
8
  //#region src/visual/types.d.ts
9
9
  /** The base commands of the `/control` door, keyed by their short names. */
package/dist/visual.d.mts CHANGED
@@ -1,2 +1,2 @@
1
- import { a as VisualApp, c as VisualRenderer, d as VisualStart, f as VisualStep, h as VisualTolerance, i as CheckpointResult, l as VisualReport, m as VisualTestResult, n as runVisualTests, o as VisualOptions, p as VisualTest, r as defineVisualTest, s as VisualPage, t as parseVisualArgv, u as VisualSetup } from "./visual-G6F0Mi9H.mjs";
1
+ import { a as VisualApp, c as VisualRenderer, d as VisualStart, f as VisualStep, h as VisualTolerance, i as CheckpointResult, l as VisualReport, m as VisualTestResult, n as runVisualTests, o as VisualOptions, p as VisualTest, r as defineVisualTest, s as VisualPage, t as parseVisualArgv, u as VisualSetup } from "./visual-B8ve4Qll.mjs";
2
2
  export { type CheckpointResult, type VisualApp, type VisualOptions, type VisualPage, type VisualRenderer, type VisualReport, type VisualSetup, type VisualStart, type VisualStep, type VisualTest, type VisualTestResult, type VisualTolerance, defineVisualTest, parseVisualArgv, runVisualTests };
package/llms.txt CHANGED
@@ -101,8 +101,7 @@ function defineFeature(name: string, description: FeatureDescription): FeaturePl
101
101
  // contribute?: Record<string, { flow: AnyFlow; order: number; when?: (snapshot: Snapshot) => boolean }>;
102
102
  // features?: readonly FeaturePlugin[]; // sub-features, composed right after this one
103
103
  // projections?, systems?, components?, scenes?, assets?; // V2 keys, read by world, scenes and assets
104
- // systemsRun?: "always" | "active"; // read by world. "always" (default) or "active":
105
- // // the feature's systems run only while a node, scene, view or ui root of it is there
104
+ // systemsRun?: never; // the 0.15.3 key, removed: a system runs while its query has an entity
106
105
  // animations?: readonly AnimationDefinition[]; // V3, read by anim
107
106
  // ui?: readonly ComponentDefinition[]; // V3, read by ui (defineComponent results)
108
107
  // strings?: Record<locale, CompiledMessages | (() => Promise<unknown>)>; // V3, read by i18n
@@ -529,7 +528,7 @@ The graph. Modules `runner`, `gate`, `inbox`, `fx`, `features`. Runner methods s
529
528
  - `app.assets`: `load(bundle)`, `unload(bundle)`, `isLoaded(bundle)`, `has(bundle)` (the manifest carries it, loaded or not), `texture(key)`, `font(key)`, `audio(key)`, `usage()`. Per bundle in a feature's `assets.ts`: `defineBundles({ ep01: { tier: "feature", release: "leave", mipmaps: ["items/**"] } })`. `release: "leave"` also unloads the bundle, with reason `"leave"`, at the next rest once nothing holds it: not pinned by the node entered, not named by the mounted scene, no node of its feature on the stack; a bundle the outgoing scene named leaves one rest later. The default `"budget"` leaves only under the texture budget. `mipmaps` (`true` or globs relative to `assets/`) uploads the textures with their full chain; such a texture stays loose in a pack. A sub-feature with resources gets its own bundles, named like its folder, with `dir` and `parent` in the manifest; a code-only one has none. `companions.add({ load(bundle), unload(bundle) })` adds what loads and unloads with every bundle; `i18n` adds the strings of a sub-feature that way, only when `assets` is composed. Events `assets:bundle-loaded`, `assets:bundle-progress`, `assets:bundle-unloaded` (`reason: "budget" | "request" | "leave"`), `assets:replaced` (dev). Config: `manifest`, `textureBudgetMb: 192`, `preloadDepth: 2`, `baseUrl`, `io`.
530
529
  - `resource(name, defaults)` clones plain JSON defaults into every world. `resource(name, () => value)` calls the factory once per world on first read, so the value may hold a `Map` or a function: `const Looks = resource("boardLooks", () => ({ shown: new Map<World.Entity, { look: string }>() }))`. A system keeps its own state there; `snapshot()` leaves a value that is not JSON out.
531
530
  - `Now` is the engine resource of the frame's moment: `world` writes `clock.now()` into it at start and first thing in every frame, headless too, so every system of a frame reads one moment: `const { now } = res(Now)`; a fake clock makes it exact. A system that shows time writes its component only when its picture changes. `snapshot().resources` holds `Now: { now }`. Reserved resource names: `Now`, `Pointer`, `AnimPlayer`, `UiCounters`; a game resource of one of them throws `[game] world: the resource name "Now" is the engine's (the frame's moment).`
532
- - Systems that run where their feature is: `defineFeature("board", { systems, systemsRun: "active" })` runs the feature's systems only while it is active: a node of it on the flow stack (outer frames of sub-flows included), a scene it declares mounted (`scenes` holds it), a view of a projection it registered, leaving included, or a popup or JSX root of it that `ui` holds until its exit ended. `"always"` (default) and the systems the engine's plugins or `ecs.system` register run every frame. A skipped system keeps its slot, walks no query, marks nothing and allocates nothing; `ctx.resumed` is true on its first run after a skip (a cache it drew is rebuilt then). `app.world.ecs.isActive(feature)` answers for any feature; `ecs.hold(feature)` returns a release (`scenes` and `ui` call it). The active set is read at the start of each phase that runs systems. Dev warnings `world:feature-never-active`, `world:hold-unknown-feature`, `world:bad-feature-entry` with `key: "systemsRun"`.
531
+ - When a system runs: before each run the world checks it. A system with a `query` is skipped while a term of its query has no entity (the smallest store among the terms is empty): no walk, no `mut()` mark, no timing. A system without a query always runs. `system({ ..., scenes: ["board"] })` runs only while one of those scenes is mounted or leaving: `scenes` calls `app.world.ecs.setScene(id)` at every switch, and the scene before leaves until nothing carries `Exiting` (the elements of its JSX views played their exit; the next scene's entities already stand then, so a `scenes` system writes only its own scene's entities). A dev build throws at start for a name in `scenes` no feature declares: `[game] System "board.idle" runs on the scene "bord", which no feature declares.` `ctx.resumed` is true on the first run after a skip (a cache or a remembered cue starts over then). The same for every system, the engine's included; 0 B a frame. A run with no rows does not happen any more: work a system did besides its rows (a hover, a drop or a press that ends, a resource reset, something that outlives its entities) goes to a system with `query: []` and `scenes`, reading its rows with `world.query()`. From 0.15.3: delete `systemsRun` from every feature (the type refuses it), rewrite the systems that work outside their rows, add `scenes` only to a system whose entities stand on every screen; `ecs.hold` and `ecs.isActive` are gone (a test asks `app.scenes.current()` or whether the system ran). World README, "When a system runs".
533
532
  - Motion hooks are told why they play: `enter(view, item, hint, cause)` and `exit(view, item, hint, cause)` get `World.MotionCause`, `"mount"` (the view comes or goes with its screen: a mount, an unmount, a load, a restore, a paused or fast world) or `"change"` (a commit). A world projection plays them only for `"change"`; `ui` plays them for both. `app.world.projection.causeOf(entity)` answers how a view came or how it is leaving, also in a removal hook of its despawn, until the next frame.
534
533
  - `app.world.projection.replace(spec)` swaps the spec of a registered projection; a mounted one runs its new `view` on the next frame. An unknown name throws. The caller is the dev hot swap.
535
534
  - `input`: one followed pointer. A finger that has not started a gesture (pressed, inside the tap slop, no long press, no trace) gives way to the next finger that goes down, wherever it rests, and its press is cancelled; a finger that started a gesture keeps it. `Pointer.id` is the followed `pointerId` (`-1` at rest): a system that measures the finger's travel starts over when it changes.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moku-labs/game",
3
- "version": "0.15.3",
3
+ "version": "0.15.4",
4
4
  "description": "2D puzzle game engine on TypeScript and PixiJS, built on @moku-labs/core.",
5
5
  "type": "module",
6
6
  "sideEffects": [