@moku-labs/game 0.15.26 → 0.15.28
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/dist/app/page.d.mts +1 -1
- package/dist/app/page.mjs +1 -1
- package/dist/app/system.d.mts +3 -3
- package/dist/app.d.mts +3 -3
- package/dist/app.mjs +1 -1
- package/dist/assets.d.mts +1 -1
- package/dist/{commands-KWKfdywA.d.mts → commands-BJ4gG5WZ.d.mts} +5 -5
- package/dist/control.d.mts +4 -4
- package/dist/{define-uiyy88hF.d.mts → define-CVYadV_I.d.mts} +2 -2
- package/dist/{headless-PssyKRFN.d.mts → headless-DBYmnluy.d.mts} +1 -1
- package/dist/{index-CM1goGHf.d.mts → index-Dn_v0Fqs.d.mts} +7 -5
- package/dist/index.d.mts +5 -5
- package/dist/index.mjs +3 -3
- package/dist/inspect.d.mts +6 -6
- package/dist/inspect.mjs +1 -1
- package/dist/jsx-dev-runtime.d.mts +1 -1
- package/dist/jsx-runtime.d.mts +1 -1
- package/dist/{runtime-C0YaA9fC.d.mts → runtime-DnjJ4Zo9.d.mts} +1 -1
- package/dist/{sources-Dpc4NXvG.mjs → sources-ncFMQIus.mjs} +1 -1
- package/dist/{src-DWYFupNb.mjs → src-DPvSTM8O.mjs} +485 -78
- package/dist/testing.d.mts +4 -4
- package/dist/testing.mjs +1 -1
- package/dist/{types-BdLJ4lTA.d.mts → types-Bsa1nFyU.d.mts} +4 -4
- package/dist/{types-C-wAHgpD.d.mts → types-C76NQm-h.d.mts} +119 -20
- package/dist/{types-ChZ4Ej7O.d.mts → types-C_nC_M4p.d.mts} +73 -11
- package/dist/{types-sfaYGg9l.d.mts → types-DTetCcBC.d.mts} +2 -2
- package/dist/{types-_OQ6H8WL.d.mts → types-bEJEga5m.d.mts} +21 -3
- package/dist/{visual-DmDWJEzs.d.mts → visual-BMRHt4IB.d.mts} +6 -6
- package/dist/{visual-kEC9nb-o.mjs → visual-ClVRMUAi.mjs} +35 -5
- package/dist/visual.d.mts +1 -1
- package/dist/visual.mjs +1 -1
- package/package.json +1 -1
|
@@ -2,7 +2,7 @@ import { t as __exportAll } from "./rolldown-runtime-8H4AJuhK.mjs";
|
|
|
2
2
|
import { r as systemSource, t as memory } from "./memory-CWlqhS7l.mjs";
|
|
3
3
|
import { a as finishDrafts, i as enableDraftPatches, n as deepFreeze$1, o as openDrafts, r as dropDrafts } from "./drafts-BawpSaVp.mjs";
|
|
4
4
|
import { _ as pushEntry, c as collectFlows, d as findNode, f as framePath, g as compact, h as slotNames, i as servePaused, l as describeGraph, m as isRestCheckpoint, n as captureFrame, o as epochOf, p as graphHash, r as serveCaptures, s as pageNow$1, t as cancelCaptures } from "./capture-PTJXDpxK.mjs";
|
|
5
|
-
import { $ as component, A as localPoseOf, B as OverlayLevel, C as builtInStyles, D as textFor, E as readStyle, F as rootPoseOf, G as arcDefaults, H as Shape, I as Display, J as Exiting, K as componentsFor, L as HudPart, M as poseUnder, N as rootPointOf, O as applyPose, P as rootPoseInto, Q as Tree, R as NineSlice, U as Sprite, V as Parent, W as Transform, X as Now, Y as Layer, Z as Order, _ as uiFor, a as visualOf$1, b as Countdown, c as Covered, d as LocalWrite, f as Scroll, h as UiCounters, i as samePose, it as tag, j as parentOf$1, k as isLeaving, l as DebugTrigger, m as UI_OWNER, n as drawnRect, nt as system, o as visualRectOf, p as ScrollState, r as fitScaleOf, rt as systemsFor, s as Box, t as CONTENT$1, tt as resource, u as Escapable, x as Text, z as Overlay } from "./visual-
|
|
5
|
+
import { $ as component, A as localPoseOf, B as OverlayLevel, C as builtInStyles, D as textFor, E as readStyle, F as rootPoseOf, G as arcDefaults, H as Shape, I as Display, J as Exiting, K as componentsFor, L as HudPart, M as poseUnder, N as rootPointOf, O as applyPose, P as rootPoseInto, Q as Tree, R as NineSlice, U as Sprite, V as Parent, W as Transform, X as Now, Y as Layer, Z as Order, _ as uiFor, a as visualOf$1, b as Countdown, c as Covered, d as LocalWrite, f as Scroll, h as UiCounters, i as samePose, it as tag, j as parentOf$1, k as isLeaving, l as DebugTrigger, m as UI_OWNER, n as drawnRect, nt as system, o as visualRectOf, p as ScrollState, r as fitScaleOf, rt as systemsFor, s as Box, t as CONTENT$1, tt as resource, u as Escapable, x as Text, z as Overlay } from "./visual-ClVRMUAi.mjs";
|
|
6
6
|
import { a as isDev } from "./dev-BNndV-hU.mjs";
|
|
7
7
|
import { n as isComponentDefinition } from "./component-DGg5DqKK.mjs";
|
|
8
8
|
import { n as flatten } from "./flatten-nUX9PpGo.mjs";
|
|
@@ -5872,7 +5872,7 @@ function restorePosition(ctx, _modules, bookmark) {
|
|
|
5872
5872
|
* @param view - The last view.
|
|
5873
5873
|
* @returns True while nothing the state shows moved.
|
|
5874
5874
|
*/
|
|
5875
|
-
function isCurrent$
|
|
5875
|
+
function isCurrent$2(ctx, view) {
|
|
5876
5876
|
const runner = ctx.state.runner;
|
|
5877
5877
|
return view.running === (runner.running !== void 0) && view.depth === runner.stack.length && view.top === runner.stack.at(-1) && view.open === ctx.state.gate.open && view.mode === ctx.state.fx.mode && view.journalIndex === runner.journalIndex;
|
|
5878
5878
|
}
|
|
@@ -5913,7 +5913,7 @@ function viewOf(ctx) {
|
|
|
5913
5913
|
function readState(ctx) {
|
|
5914
5914
|
const runner = ctx.state.runner;
|
|
5915
5915
|
const view = runner.view;
|
|
5916
|
-
if (view !== void 0 && isCurrent$
|
|
5916
|
+
if (view !== void 0 && isCurrent$2(ctx, view)) return view.state;
|
|
5917
5917
|
const next = viewOf(ctx);
|
|
5918
5918
|
runner.view = next;
|
|
5919
5919
|
return next.state;
|
|
@@ -11262,6 +11262,7 @@ function reconcileOne(pctx, spec, mounted, snapshot, options, counts, used) {
|
|
|
11262
11262
|
return;
|
|
11263
11263
|
}
|
|
11264
11264
|
mounted.items = next;
|
|
11265
|
+
mounted.seen = snapshot;
|
|
11265
11266
|
if (options.direct) flushMounted(pctx, mounted);
|
|
11266
11267
|
if (options.converge) convergeMounted(pctx, mounted);
|
|
11267
11268
|
else {
|
|
@@ -11271,6 +11272,20 @@ function reconcileOne(pctx, spec, mounted, snapshot, options, counts, used) {
|
|
|
11271
11272
|
counts.queued += mounted.queue.length;
|
|
11272
11273
|
}
|
|
11273
11274
|
/**
|
|
11275
|
+
* Tells whether a play reconcile would change nothing in a projection: its last reconcile read the same `player` and
|
|
11276
|
+
* `session` trees, and nothing forces, converges or settles it. A scene mounts its projections direct after the commit
|
|
11277
|
+
* of its node; the next frame's reconcile of that commit then skips them (≈2 ms a switch on a Galaxy A26, F61).
|
|
11278
|
+
*
|
|
11279
|
+
* @param mounted - The mounted projection.
|
|
11280
|
+
* @param snapshot - The state of this reconcile.
|
|
11281
|
+
* @param options - How this reconcile runs.
|
|
11282
|
+
* @returns True when the projection is passed over.
|
|
11283
|
+
*/
|
|
11284
|
+
function isCurrent$1(mounted, snapshot, options) {
|
|
11285
|
+
const seen = mounted.seen;
|
|
11286
|
+
return seen !== void 0 && !options.direct && !options.force && !options.converge && !options.settle && seen.player === snapshot.player && seen.session === snapshot.session;
|
|
11287
|
+
}
|
|
11288
|
+
/**
|
|
11274
11289
|
* Runs one reconcile over every mounted projection, or over the named ones only. A converging
|
|
11275
11290
|
* one then brings the elements other plugins own home and tells their owners.
|
|
11276
11291
|
*
|
|
@@ -11287,7 +11302,7 @@ function reconcile(pctx, options) {
|
|
|
11287
11302
|
for (const [name, mounted] of state.mounted) {
|
|
11288
11303
|
if (options.names !== void 0 && !options.names.includes(name)) continue;
|
|
11289
11304
|
const spec = state.specs.get(name);
|
|
11290
|
-
if (spec === void 0) continue;
|
|
11305
|
+
if (spec === void 0 || isCurrent$1(mounted, snapshot, options)) continue;
|
|
11291
11306
|
counts.projections += 1;
|
|
11292
11307
|
reconcileOne(pctx, spec, mounted, snapshot, options, counts, used);
|
|
11293
11308
|
}
|
|
@@ -16195,6 +16210,14 @@ function emptyMap$8() {
|
|
|
16195
16210
|
return /* @__PURE__ */ new Map();
|
|
16196
16211
|
}
|
|
16197
16212
|
/**
|
|
16213
|
+
* Creates an empty set. Same reason as `emptyRecords`.
|
|
16214
|
+
*
|
|
16215
|
+
* @returns An empty set.
|
|
16216
|
+
*/
|
|
16217
|
+
function emptySet$7() {
|
|
16218
|
+
return /* @__PURE__ */ new Set();
|
|
16219
|
+
}
|
|
16220
|
+
/**
|
|
16198
16221
|
* Creates an empty weak set. Same reason as `emptyRecords`.
|
|
16199
16222
|
*
|
|
16200
16223
|
* @returns An empty weak set.
|
|
@@ -16229,6 +16252,7 @@ function createKeptState(config) {
|
|
|
16229
16252
|
on: allowed && config.debug.kept !== false,
|
|
16230
16253
|
allowed,
|
|
16231
16254
|
warnedBudget: false,
|
|
16255
|
+
warnedBlend: emptySet$7(),
|
|
16232
16256
|
verifySkipped: 0,
|
|
16233
16257
|
bypassed: false,
|
|
16234
16258
|
flushPasses: 0,
|
|
@@ -16317,6 +16341,7 @@ function stopKept(state) {
|
|
|
16317
16341
|
state.flushPasses = 0;
|
|
16318
16342
|
state.drawnPictures = 0;
|
|
16319
16343
|
state.warnedBudget = false;
|
|
16344
|
+
state.warnedBlend.clear();
|
|
16320
16345
|
state.verifySkipped = 0;
|
|
16321
16346
|
state.bypassed = false;
|
|
16322
16347
|
}
|
|
@@ -16514,7 +16539,64 @@ function ancestorsPlain(kctx, record, stage) {
|
|
|
16514
16539
|
function eligible(kctx, record, view, stack) {
|
|
16515
16540
|
const { top } = record;
|
|
16516
16541
|
const stage = kctx.host.stage();
|
|
16517
|
-
return kctx
|
|
16542
|
+
return keepingOn(kctx) && stage !== void 0 && stack.enabled > 0 && plainFilters(record.slots) && drawsAsPicture(top, view) && topDrawn(kctx.sync, view) && ancestorsPlain(kctx, record, stage);
|
|
16543
|
+
}
|
|
16544
|
+
/**
|
|
16545
|
+
* Tells whether the kept cache may keep anything this frame: switched on and not bypassed.
|
|
16546
|
+
*
|
|
16547
|
+
* @param kctx - The kept context.
|
|
16548
|
+
* @returns True when pictures may be kept.
|
|
16549
|
+
*/
|
|
16550
|
+
function keepingOn(kctx) {
|
|
16551
|
+
return kctx.state.on && !kctx.state.bypassed;
|
|
16552
|
+
}
|
|
16553
|
+
/**
|
|
16554
|
+
* Tells whether a top and its visual draw as a picture does: the top visible and not faded out, both blending
|
|
16555
|
+
* normally (the visual sits inside the wrapper of a kept bare view, D4).
|
|
16556
|
+
*
|
|
16557
|
+
* @param top - The record's top.
|
|
16558
|
+
* @param view - Its view.
|
|
16559
|
+
* @returns True when they do.
|
|
16560
|
+
*/
|
|
16561
|
+
function drawsAsPicture(top, view) {
|
|
16562
|
+
return top.visible && top.alpha > 0 && plainBlend(top.blendMode) && plainBlend(view.object.blendMode);
|
|
16563
|
+
}
|
|
16564
|
+
/**
|
|
16565
|
+
* Tells whether a node under a top blends otherwise than normally, at any depth (F68): drawn into a picture, it would
|
|
16566
|
+
* blend with the picture's empty texture, not with what is drawn under the view, and a glow would cover the board under
|
|
16567
|
+
* it instead of lighting it. Recursive, by index over `children`: allocates nothing.
|
|
16568
|
+
*
|
|
16569
|
+
* @param node - The top, or a node under it; its own blend is not read (`eligible` reads the top's and the visual's).
|
|
16570
|
+
* @returns True when a node under it blends otherwise.
|
|
16571
|
+
*/
|
|
16572
|
+
function blendsBelow(node) {
|
|
16573
|
+
const { children } = node;
|
|
16574
|
+
for (let index = 0; index < children.length; index += 1) {
|
|
16575
|
+
const child = children[index];
|
|
16576
|
+
if (child !== void 0 && (!plainBlend(child.blendMode) || blendsBelow(child))) return true;
|
|
16577
|
+
}
|
|
16578
|
+
return false;
|
|
16579
|
+
}
|
|
16580
|
+
/**
|
|
16581
|
+
* Tells whether a record's subtree holds a node that blends otherwise than normally, and remembers a yes (`blended`):
|
|
16582
|
+
* a refused record is not walked again until a change under its top clears it (`stepRecord`). The first refusal of a
|
|
16583
|
+
* top is warned once per run, by its label.
|
|
16584
|
+
*
|
|
16585
|
+
* @param kctx - The kept context.
|
|
16586
|
+
* @param record - The record.
|
|
16587
|
+
* @returns True when the record may not be taken.
|
|
16588
|
+
*/
|
|
16589
|
+
function blendedBelow(kctx, record) {
|
|
16590
|
+
if (record.blended) return true;
|
|
16591
|
+
if (!blendsBelow(record.top)) return false;
|
|
16592
|
+
record.blended = true;
|
|
16593
|
+
const { warnedBlend } = kctx.state;
|
|
16594
|
+
const label = labelOf$3(record.top);
|
|
16595
|
+
if (!warnedBlend.has(label)) {
|
|
16596
|
+
warnedBlend.add(label);
|
|
16597
|
+
kctx.ctx.log.warn("renderer:kept-blend", { picture: label });
|
|
16598
|
+
}
|
|
16599
|
+
return true;
|
|
16518
16600
|
}
|
|
16519
16601
|
/**
|
|
16520
16602
|
* Tells whether a view of kind `"Display"` hangs in a subtree, the top included: a game's own object it may write
|
|
@@ -16582,7 +16664,8 @@ function createRecord$1(kctx, pixi, entity, view, slots, category) {
|
|
|
16582
16664
|
refused: 0,
|
|
16583
16665
|
cut: false,
|
|
16584
16666
|
direct: false,
|
|
16585
|
-
displays: void 0
|
|
16667
|
+
displays: void 0,
|
|
16668
|
+
blended: false
|
|
16586
16669
|
};
|
|
16587
16670
|
const { state } = kctx;
|
|
16588
16671
|
state.records.push(record);
|
|
@@ -16761,8 +16844,8 @@ function writeTakeKeys(record, now, box) {
|
|
|
16761
16844
|
/**
|
|
16762
16845
|
* Takes or retakes the picture of a record: stages 1, 2 (when its first filter has inputs) and 3, at the phase of this
|
|
16763
16846
|
* frame. The bounds are cut to the filter area of the top when it holds one (F43). Refused, and the view drawn live,
|
|
16764
|
-
* for a `Display` in the subtree without `onDemand`,
|
|
16765
|
-
* has no room (P6.3, warned once).
|
|
16847
|
+
* for a `Display` in the subtree without `onDemand`, a node under the top that blends otherwise (F68, warned once per
|
|
16848
|
+
* top), empty bounds, a picture over 4096 device pixels, or a budget that has no room (P6.3, warned once).
|
|
16766
16849
|
*
|
|
16767
16850
|
* @param kctx - The kept context.
|
|
16768
16851
|
* @param pixi - The module that draws.
|
|
@@ -16775,6 +16858,7 @@ function writeTakeKeys(record, now, box) {
|
|
|
16775
16858
|
function takePicture(kctx, pixi, matrices, record, view, now) {
|
|
16776
16859
|
const { top } = record;
|
|
16777
16860
|
if (!kctx.ctx.config.onDemand && holdsDisplay(kctx, top)) return false;
|
|
16861
|
+
if (blendedBelow(kctx, record)) return false;
|
|
16778
16862
|
const drawn = drawnBounds(pixi, top);
|
|
16779
16863
|
if (!drawn.isValid) return false;
|
|
16780
16864
|
const local = clippedToArea(drawn, top.filterArea);
|
|
@@ -17114,7 +17198,8 @@ function wrapBare(kctx, record, view) {
|
|
|
17114
17198
|
}
|
|
17115
17199
|
/**
|
|
17116
17200
|
* A live record's frame (P4.2): still frames are counted; at two (eight after churning) the record is taken, a bare
|
|
17117
|
-
* view in a wrapper of its own (D4).
|
|
17201
|
+
* view in a wrapper of its own (D4). A record with a node under its top that blends otherwise stays live, before any
|
|
17202
|
+
* wrap (F68).
|
|
17118
17203
|
*
|
|
17119
17204
|
* @param kctx - The kept context.
|
|
17120
17205
|
* @param pixi - The module that draws.
|
|
@@ -17129,6 +17214,7 @@ function stepLive(kctx, pixi, matrices, record, view) {
|
|
|
17129
17214
|
record.still = changed ? 0 : record.still + 1;
|
|
17130
17215
|
if (record.still < (record.calm ? 8 : 2)) return;
|
|
17131
17216
|
if (record.refused > 0 && state.bytes + record.refused - record.bytes > state.budget) return;
|
|
17217
|
+
if (blendedBelow(kctx, record)) return;
|
|
17132
17218
|
wrapBare(kctx, record, view);
|
|
17133
17219
|
if (takePicture(kctx, pixi, matrices, record, view, NOW)) toKept(kctx, record, view);
|
|
17134
17220
|
else record.still = 0;
|
|
@@ -17311,7 +17397,8 @@ function stepCalm(kctx, record) {
|
|
|
17311
17397
|
}
|
|
17312
17398
|
/**
|
|
17313
17399
|
* One drawn frame of a record, inside the flush: eligibility, the keys of this frame, the policy of its life, the quad
|
|
17314
|
-
* of a kept one; the dirty flags are cleared.
|
|
17400
|
+
* of a kept one; the dirty flags are cleared. A change under the top forgets a blended node found before: the next
|
|
17401
|
+
* take walks the subtree again (F68).
|
|
17315
17402
|
*
|
|
17316
17403
|
* @param kctx - The kept context.
|
|
17317
17404
|
* @param pixi - The module that draws.
|
|
@@ -17321,6 +17408,7 @@ function stepCalm(kctx, record) {
|
|
|
17321
17408
|
*/
|
|
17322
17409
|
function stepRecord(kctx, pixi, matrices, record, view) {
|
|
17323
17410
|
noteDrawing(kctx, record, BEFORE);
|
|
17411
|
+
if (record.dirtyS1) record.blended = false;
|
|
17324
17412
|
NOW.r = kctx.host.resolution();
|
|
17325
17413
|
readStack(record.slots, NOW.r, pixi.AlphaFilter, NOW);
|
|
17326
17414
|
if (eligible(kctx, record, view, NOW)) stepEligible(kctx, pixi, matrices, record, view);
|
|
@@ -23947,10 +24035,21 @@ const PROBE_SIZE = 4;
|
|
|
23947
24035
|
/** The content subjects the renderer warms itself, in queue order; the text follows once a font is installed. */
|
|
23948
24036
|
const CONTENT = Object.freeze([
|
|
23949
24037
|
"sprite",
|
|
24038
|
+
"sprite-add",
|
|
24039
|
+
"sprite-screen",
|
|
23950
24040
|
"graphics",
|
|
23951
24041
|
"particles-normal",
|
|
23952
24042
|
"particles-add"
|
|
23953
24043
|
]);
|
|
24044
|
+
/** The probe object of each content subject the tree holds from its first warm frame on. */
|
|
24045
|
+
const FIXED_NODES = Object.freeze({
|
|
24046
|
+
sprite: "sprite",
|
|
24047
|
+
"sprite-add": "spriteAdd",
|
|
24048
|
+
"sprite-screen": "spriteScreen",
|
|
24049
|
+
graphics: "graphics",
|
|
24050
|
+
"particles-normal": "particlesNormal",
|
|
24051
|
+
"particles-add": "particlesAdd"
|
|
24052
|
+
});
|
|
23954
24053
|
/** Every target a WebGPU pipeline is keyed by. */
|
|
23955
24054
|
const ALL_VARIANTS = Object.freeze([
|
|
23956
24055
|
"canvas",
|
|
@@ -23980,16 +24079,18 @@ function particleProperties() {
|
|
|
23980
24079
|
}
|
|
23981
24080
|
/**
|
|
23982
24081
|
* A 4 × 4 sprite of Pixi's white texture: the batch pipeline of sprites, nine-slices, plain bitmap text and batched
|
|
23983
|
-
* graphics.
|
|
24082
|
+
* graphics, with the blend of a `Sprite` or a `NineSlice` glow (part of a WebGPU pipeline's key).
|
|
23984
24083
|
*
|
|
23985
24084
|
* @param pixi - The module that draws.
|
|
23986
|
-
* @
|
|
24085
|
+
* @param blend - `"normal"` (Pixi's own mode left as it is), `"add"` or `"screen"`.
|
|
24086
|
+
* @returns The sprite, labelled `renderer.warm.sprite`, or `renderer.warm.sprite-<blend>` for a glow.
|
|
23987
24087
|
*/
|
|
23988
|
-
function whiteSprite(pixi) {
|
|
24088
|
+
function whiteSprite(pixi, blend = "normal") {
|
|
23989
24089
|
const sprite = new pixi.Sprite(pixi.Texture.WHITE);
|
|
23990
|
-
sprite.label = "renderer.warm.sprite"
|
|
24090
|
+
sprite.label = blend === "normal" ? "renderer.warm.sprite" : `renderer.warm.sprite-${blend}`;
|
|
23991
24091
|
sprite.width = PROBE_SIZE;
|
|
23992
24092
|
sprite.height = PROBE_SIZE;
|
|
24093
|
+
if (blend !== "normal") sprite.blendMode = blend;
|
|
23993
24094
|
return sprite;
|
|
23994
24095
|
}
|
|
23995
24096
|
/**
|
|
@@ -24052,6 +24153,8 @@ function buildObjects(pixi) {
|
|
|
24052
24153
|
holder,
|
|
24053
24154
|
list: [],
|
|
24054
24155
|
sprite: whiteSprite(pixi),
|
|
24156
|
+
spriteAdd: whiteSprite(pixi, "add"),
|
|
24157
|
+
spriteScreen: whiteSprite(pixi, "screen"),
|
|
24055
24158
|
graphics,
|
|
24056
24159
|
particlesNormal: particlesOf(pixi, "normal"),
|
|
24057
24160
|
particlesAdd: particlesOf(pixi, "add"),
|
|
@@ -24075,6 +24178,8 @@ function dropObjects(warm) {
|
|
|
24075
24178
|
objects.clipFrame,
|
|
24076
24179
|
objects.holder,
|
|
24077
24180
|
objects.sprite,
|
|
24181
|
+
objects.spriteAdd,
|
|
24182
|
+
objects.spriteScreen,
|
|
24078
24183
|
objects.graphics,
|
|
24079
24184
|
objects.particlesNormal,
|
|
24080
24185
|
objects.particlesAdd,
|
|
@@ -24251,27 +24356,22 @@ function warmFilter(sctx, filter) {
|
|
|
24251
24356
|
* @returns The node, or `undefined` for the text without a font.
|
|
24252
24357
|
*/
|
|
24253
24358
|
function nodeOf$1(pixi, warm, objects, subject) {
|
|
24254
|
-
|
|
24255
|
-
|
|
24256
|
-
|
|
24257
|
-
|
|
24258
|
-
|
|
24259
|
-
|
|
24260
|
-
|
|
24261
|
-
|
|
24262
|
-
|
|
24263
|
-
|
|
24264
|
-
|
|
24265
|
-
|
|
24266
|
-
|
|
24267
|
-
|
|
24268
|
-
|
|
24269
|
-
|
|
24270
|
-
default:
|
|
24271
|
-
objects.list[0] = subject;
|
|
24272
|
-
objects.holder.filters = objects.list;
|
|
24273
|
-
return objects.holder;
|
|
24274
|
-
}
|
|
24359
|
+
if (typeof subject !== "string") {
|
|
24360
|
+
objects.list[0] = subject;
|
|
24361
|
+
objects.holder.filters = objects.list;
|
|
24362
|
+
return objects.holder;
|
|
24363
|
+
}
|
|
24364
|
+
if (subject !== "text") return objects[FIXED_NODES[subject]];
|
|
24365
|
+
if (warm.textFont === void 0) return void 0;
|
|
24366
|
+
objects.text ??= new pixi.BitmapText({
|
|
24367
|
+
label: "renderer.warm.text",
|
|
24368
|
+
text: "0",
|
|
24369
|
+
style: {
|
|
24370
|
+
fontFamily: warm.textFont,
|
|
24371
|
+
fontSize: 16
|
|
24372
|
+
}
|
|
24373
|
+
});
|
|
24374
|
+
return objects.text;
|
|
24275
24375
|
}
|
|
24276
24376
|
/**
|
|
24277
24377
|
* The name of a probe's filter in the log: the name of its WebGPU program (the definition id of one of `effects`'
|
|
@@ -25077,7 +25177,8 @@ function poolKeyOf(kind, textureKey, hollow) {
|
|
|
25077
25177
|
return hollow ? `${kind}.hollow:${textureKey}` : `${kind}:${textureKey}`;
|
|
25078
25178
|
}
|
|
25079
25179
|
/**
|
|
25080
|
-
* Brings the object of a view that left back to the state a fresh one has: detached, drawn, opaque, unmoved,
|
|
25180
|
+
* Brings the object of a view that left back to the state a fresh one has: detached, drawn, opaque, unmoved, blending
|
|
25181
|
+
* normally, white.
|
|
25081
25182
|
* Its hidden reasons go with `clearHidden`, so a pooled object never brings the reason of the view it belonged to; a
|
|
25082
25183
|
* sprite and a graphics lose their tint (a fill-only shape is coloured by it, spec 26 R2.1).
|
|
25083
25184
|
*
|
|
@@ -25089,20 +25190,25 @@ function resetObject(view) {
|
|
|
25089
25190
|
resetNode(view.object);
|
|
25090
25191
|
}
|
|
25091
25192
|
/**
|
|
25092
|
-
* Brings a detached object back to a fresh one's pose and look: drawn, opaque, unmoved,
|
|
25093
|
-
* graphics.
|
|
25193
|
+
* Brings a detached object back to a fresh one's pose and look: drawn, opaque, unmoved, blending as its parent (no
|
|
25194
|
+
* glow of the view it drew for), white for a sprite and a graphics, and a graphics without a path.
|
|
25094
25195
|
*
|
|
25095
25196
|
* @param object - The object, out of the tree.
|
|
25096
25197
|
*/
|
|
25097
25198
|
function resetNode(object) {
|
|
25098
25199
|
object.visible = true;
|
|
25099
25200
|
object.alpha = 1;
|
|
25201
|
+
object.blendMode = "inherit";
|
|
25100
25202
|
object.rotation = 0;
|
|
25101
25203
|
object.zIndex = 0;
|
|
25102
25204
|
object.position.set(0, 0);
|
|
25103
25205
|
object.pivot.set(0, 0);
|
|
25104
25206
|
object.scale.set(1);
|
|
25105
|
-
if (isSprite(object)
|
|
25207
|
+
if (isSprite(object)) object.tint = 16777215;
|
|
25208
|
+
if (isGraphics(object)) {
|
|
25209
|
+
object.tint = 16777215;
|
|
25210
|
+
object.clear();
|
|
25211
|
+
}
|
|
25106
25212
|
}
|
|
25107
25213
|
/**
|
|
25108
25214
|
* Takes an object out of its pool, if one is waiting.
|
|
@@ -27197,6 +27303,42 @@ function retargetKey(state, entity, view, key) {
|
|
|
27197
27303
|
if (key !== "") trackKey(state, entity, key);
|
|
27198
27304
|
}
|
|
27199
27305
|
/**
|
|
27306
|
+
* The Pixi blend mode a `blend` of a `Sprite` or a `NineSlice` draws with: `"add"` and `"screen"` as they are, Pixi's
|
|
27307
|
+
* own blends of the sprite batch; `"inherit"`, what a fresh object holds, for `"normal"` and a value without the field.
|
|
27308
|
+
* Any other string (Pixi's advanced modes, `"multiply"`, a typo) is never handed to Pixi, which would draw an advanced
|
|
27309
|
+
* mode through a filter pass of its own: it draws normally, and each such value is warned once per run.
|
|
27310
|
+
*
|
|
27311
|
+
* @param sctx - Domain context of the sync module.
|
|
27312
|
+
* @param blend - The component's `blend`, as a game wrote it.
|
|
27313
|
+
* @returns The Pixi blend mode.
|
|
27314
|
+
*/
|
|
27315
|
+
function blendModeOf(sctx, blend) {
|
|
27316
|
+
if (blend === void 0 || blend === "normal") return "inherit";
|
|
27317
|
+
if (blend === "add" || blend === "screen") return blend;
|
|
27318
|
+
const warned = sctx.ctx.state.sync.blendWarned;
|
|
27319
|
+
if (!warned.has(blend)) {
|
|
27320
|
+
warned.add(blend);
|
|
27321
|
+
sctx.ctx.log.warn("renderer:blend-unknown", { blend });
|
|
27322
|
+
}
|
|
27323
|
+
return "inherit";
|
|
27324
|
+
}
|
|
27325
|
+
/**
|
|
27326
|
+
* Writes the blend of a sprite or a nine-slice on its own object, never on the wrapper: a glow blends its own pixels,
|
|
27327
|
+
* not its children's. Only a mode that differs from what Pixi holds is written, the entity marked before: Pixi rebuilds
|
|
27328
|
+
* the instructions of the render group on a new mode.
|
|
27329
|
+
*
|
|
27330
|
+
* @param sctx - Domain context of the sync module.
|
|
27331
|
+
* @param entity - The entity.
|
|
27332
|
+
* @param object - The view's own object: the sprite, or the nine-slice, filled or hollow.
|
|
27333
|
+
* @param blend - The component's `blend`.
|
|
27334
|
+
*/
|
|
27335
|
+
function writeBlend(sctx, entity, object, blend) {
|
|
27336
|
+
const mode = blendModeOf(sctx, blend);
|
|
27337
|
+
if (object.blendMode === mode) return;
|
|
27338
|
+
markView(sctx, entity);
|
|
27339
|
+
object.blendMode = mode;
|
|
27340
|
+
}
|
|
27341
|
+
/**
|
|
27200
27342
|
* Writes the texture and the tint of a sprite, each only when it differs from what Pixi holds.
|
|
27201
27343
|
*
|
|
27202
27344
|
* @param sctx - Domain context of the sync module.
|
|
@@ -27325,7 +27467,7 @@ function applyHud(sctx, entity, view, factor) {
|
|
|
27325
27467
|
}
|
|
27326
27468
|
/**
|
|
27327
27469
|
* Writes the `Sprite` component onto its display object: the texture, or its crop for a `"cover"`
|
|
27328
|
-
* sprite, the stretch into the box, and the box as the hit box, the anchor applied. The stretch and
|
|
27470
|
+
* sprite, the blend, the stretch into the box, and the box as the hit box, the anchor applied. The stretch and
|
|
27329
27471
|
* the hit box are written into the view's own objects. A new stretch of a view without a wrapper
|
|
27330
27472
|
* changes the object's own units, so its filter area is written again (B4); a write that keeps it (an
|
|
27331
27473
|
* alpha tween, a tint) writes no area.
|
|
@@ -27345,7 +27487,7 @@ function applySprite(sctx, entity, view) {
|
|
|
27345
27487
|
return stretched;
|
|
27346
27488
|
}
|
|
27347
27489
|
/**
|
|
27348
|
-
* The body of `applySprite`: the texture or its crop, the stretch, the anchor and the hit box.
|
|
27490
|
+
* The body of `applySprite`: the texture or its crop, the blend, the stretch, the anchor and the hit box.
|
|
27349
27491
|
*
|
|
27350
27492
|
* @param sctx - Domain context of the sync module.
|
|
27351
27493
|
* @param entity - The entity.
|
|
@@ -27360,6 +27502,7 @@ function writeSprite(sctx, entity, view) {
|
|
|
27360
27502
|
if (texture === void 0) warnMissing(sctx, value.texture);
|
|
27361
27503
|
view.placeholder = texture === void 0;
|
|
27362
27504
|
applyAlpha(sctx, entity, view, value);
|
|
27505
|
+
writeBlend(sctx, entity, object, value.blend);
|
|
27363
27506
|
if (texture === void 0) {
|
|
27364
27507
|
showCover(sctx, entity, view, "");
|
|
27365
27508
|
drawPlaceholder(sctx, entity, object, view, value, placeholderTexture(sctx));
|
|
@@ -27425,8 +27568,9 @@ function writeSlice(sctx, entity, object, field, value) {
|
|
|
27425
27568
|
* Writes the `NineSlice` component onto its display object and recomputes the hit box. Pixi reads
|
|
27426
27569
|
* a texture's borders only in the constructor, so they are copied here on every write: a pooled
|
|
27427
27570
|
* object, a new key and a reloaded bundle all get the insets of the texture they draw now. A `hollow` that flipped
|
|
27428
|
-
* swaps the view's object first (F59)
|
|
27429
|
-
* or only the size, allocates nothing: the borders, the outline's
|
|
27571
|
+
* swaps the view's object first (F59), and the blend is written on the object drawn now. The hit box is the whole
|
|
27572
|
+
* panel, hollow or not. A write that changes nothing, or only the size, allocates nothing: the borders, the outline's
|
|
27573
|
+
* shape and the clip's box are written in place.
|
|
27430
27574
|
*
|
|
27431
27575
|
* @param sctx - Domain context of the sync module.
|
|
27432
27576
|
* @param entity - The entity.
|
|
@@ -27457,6 +27601,7 @@ function applyNineSlice(sctx, entity, view) {
|
|
|
27457
27601
|
changed = writeSlice(sctx, entity, object, "bottomHeight", borders.bottom) || changed;
|
|
27458
27602
|
changed = writeSlice(sctx, entity, object, "tint", missing ? 16711935 : value.tint) || changed;
|
|
27459
27603
|
applyAlpha(sctx, entity, view, value);
|
|
27604
|
+
writeBlend(sctx, entity, object, value.blend);
|
|
27460
27605
|
changed = writeSlice(sctx, entity, object, "width", value.width) || changed;
|
|
27461
27606
|
changed = writeSlice(sctx, entity, object, "height", value.height) || changed;
|
|
27462
27607
|
writeHitBox(view.hitBox, value.width, value.height);
|
|
@@ -28196,6 +28341,7 @@ function stopSync(state) {
|
|
|
28196
28341
|
state.byKey.clear();
|
|
28197
28342
|
state.invalidated.clear();
|
|
28198
28343
|
state.warned.clear();
|
|
28344
|
+
state.blendWarned.clear();
|
|
28199
28345
|
state.added.clear();
|
|
28200
28346
|
state.removed.clear();
|
|
28201
28347
|
state.reparented.clear();
|
|
@@ -31547,6 +31693,7 @@ function createSyncState(debug) {
|
|
|
31547
31693
|
slices: emptyWeakSet(),
|
|
31548
31694
|
invalidated: emptySet$4(),
|
|
31549
31695
|
warned: emptySet$4(),
|
|
31696
|
+
blendWarned: emptySet$4(),
|
|
31550
31697
|
added: emptySet$4(),
|
|
31551
31698
|
removed: emptySet$4(),
|
|
31552
31699
|
reparented: emptySet$4(),
|
|
@@ -38180,11 +38327,121 @@ function applyAllGains(state) {
|
|
|
38180
38327
|
applyGain(state, "sfx");
|
|
38181
38328
|
}
|
|
38182
38329
|
//#endregion
|
|
38330
|
+
//#region src/plugins/audio/named.ts
|
|
38331
|
+
/**
|
|
38332
|
+
* @file audio plugin — named sounds: an `sfx` with an `id` plays through a gain of its own into its
|
|
38333
|
+
* bus, so the next sound with that id, `stopSfx` and `app.audio.stop` can fade it out. One entry
|
|
38334
|
+
* per id in `state.named`; the entry object is the token a sound still decoding checks before it
|
|
38335
|
+
* starts. A sound without an id never comes here.
|
|
38336
|
+
*/
|
|
38337
|
+
/**
|
|
38338
|
+
* The key a sound with an id waits under in `pendingSfx` while the unlock resumes the context.
|
|
38339
|
+
* The `#` keeps the ids apart from the asset keys, so the latest sound per id waits.
|
|
38340
|
+
*
|
|
38341
|
+
* @param id - The id of the sound.
|
|
38342
|
+
* @returns The key of the waiting sound.
|
|
38343
|
+
* @example
|
|
38344
|
+
* ```ts
|
|
38345
|
+
* waitingKey("voice"); // "#voice"
|
|
38346
|
+
* ```
|
|
38347
|
+
*/
|
|
38348
|
+
function waitingKey(id) {
|
|
38349
|
+
return `#${id}`;
|
|
38350
|
+
}
|
|
38351
|
+
/**
|
|
38352
|
+
* Fades one named sound out on the context clock and stops its source at the end of the fade. A
|
|
38353
|
+
* sound still decoding has neither a gain nor a source, so nothing is scheduled.
|
|
38354
|
+
*
|
|
38355
|
+
* @param state - The plugin state.
|
|
38356
|
+
* @param entry - The sound to fade.
|
|
38357
|
+
* @param seconds - Length of the fade, in seconds of the context clock.
|
|
38358
|
+
*/
|
|
38359
|
+
function fadeNamed(state, entry, seconds) {
|
|
38360
|
+
const context = state.context;
|
|
38361
|
+
if (context === void 0) return;
|
|
38362
|
+
const now = context.currentTime;
|
|
38363
|
+
if (entry.gain !== void 0) ramp(entry.gain, 0, seconds, now);
|
|
38364
|
+
entry.source?.stop(now + seconds);
|
|
38365
|
+
}
|
|
38366
|
+
/**
|
|
38367
|
+
* Takes an id for a new sound: the sound that plays, or decodes, under it fades out over the fade
|
|
38368
|
+
* of the new one, and a fresh entry takes its place.
|
|
38369
|
+
*
|
|
38370
|
+
* @param state - The plugin state.
|
|
38371
|
+
* @param id - The id of the new sound.
|
|
38372
|
+
* @param seconds - Length of the fade of the replaced sound, in seconds.
|
|
38373
|
+
* @returns The entry of the new sound, its token.
|
|
38374
|
+
*/
|
|
38375
|
+
function claimNamed(state, id, seconds) {
|
|
38376
|
+
const playing = state.named.get(id);
|
|
38377
|
+
if (playing !== void 0) fadeNamed(state, playing, seconds);
|
|
38378
|
+
const entry = {
|
|
38379
|
+
gain: void 0,
|
|
38380
|
+
source: void 0
|
|
38381
|
+
};
|
|
38382
|
+
state.named.set(id, entry);
|
|
38383
|
+
return entry;
|
|
38384
|
+
}
|
|
38385
|
+
/**
|
|
38386
|
+
* Starts one named sound: a gain at 1 into the bus, the source into that gain. When the source
|
|
38387
|
+
* ends, its gain is disconnected and the id is freed, unless a later sound took the id meanwhile.
|
|
38388
|
+
*
|
|
38389
|
+
* @param state - The plugin state.
|
|
38390
|
+
* @param context - The running audio context.
|
|
38391
|
+
* @param named - The id, its entry, the decoded buffer and the gain of the bus.
|
|
38392
|
+
* @param named.id - The id the sound plays under.
|
|
38393
|
+
* @param named.entry - The entry `claimNamed` returned for it.
|
|
38394
|
+
* @param named.buffer - The decoded buffer of the sound.
|
|
38395
|
+
* @param named.bus - The gain node of the bus the sound goes to.
|
|
38396
|
+
*/
|
|
38397
|
+
function startNamed(state, context, named) {
|
|
38398
|
+
const { id, entry } = named;
|
|
38399
|
+
const gain = context.createGain();
|
|
38400
|
+
const source = context.createBufferSource();
|
|
38401
|
+
gain.gain.value = 1;
|
|
38402
|
+
gain.connect(named.bus);
|
|
38403
|
+
source.buffer = named.buffer;
|
|
38404
|
+
source.connect(gain);
|
|
38405
|
+
source.addEventListener("ended", () => {
|
|
38406
|
+
gain.disconnect();
|
|
38407
|
+
if (state.named.get(id) === entry) state.named.delete(id);
|
|
38408
|
+
}, { once: true });
|
|
38409
|
+
entry.gain = gain;
|
|
38410
|
+
entry.source = source;
|
|
38411
|
+
source.start();
|
|
38412
|
+
}
|
|
38413
|
+
/**
|
|
38414
|
+
* Ends the sound under one id: the stop of `stopSfx` and of `app.audio.stop`. The entry leaves the
|
|
38415
|
+
* map, so a sound still decoding starts nothing, and the one that plays fades out. A sound with
|
|
38416
|
+
* that id waiting for the unlock is dropped. An id nothing plays under changes nothing.
|
|
38417
|
+
*
|
|
38418
|
+
* @param state - The plugin state.
|
|
38419
|
+
* @param id - The id the sound was started with.
|
|
38420
|
+
* @param fadeMs - Length of the fade, in real milliseconds.
|
|
38421
|
+
*/
|
|
38422
|
+
function stopNamed(state, id, fadeMs) {
|
|
38423
|
+
state.pendingSfx.delete(waitingKey(id));
|
|
38424
|
+
const entry = state.named.get(id);
|
|
38425
|
+
if (entry === void 0) return;
|
|
38426
|
+
state.named.delete(id);
|
|
38427
|
+
fadeNamed(state, entry, fadeMs / 1e3);
|
|
38428
|
+
}
|
|
38429
|
+
/**
|
|
38430
|
+
* Stops every named sound at once and forgets the ids. `onStop` runs it: a teardown has no time
|
|
38431
|
+
* for a fade, and the context is closed right after.
|
|
38432
|
+
*
|
|
38433
|
+
* @param state - The plugin state.
|
|
38434
|
+
*/
|
|
38435
|
+
function stopAllNamed(state) {
|
|
38436
|
+
for (const entry of state.named.values()) entry.source?.stop();
|
|
38437
|
+
state.named.clear();
|
|
38438
|
+
}
|
|
38439
|
+
//#endregion
|
|
38183
38440
|
//#region src/plugins/audio/api.ts
|
|
38184
38441
|
/**
|
|
38185
|
-
* @file audio plugin — API factory. Three buses, the unlock flag
|
|
38186
|
-
* is persisted; a game keeps the player's choice in the player state and
|
|
38187
|
-
* it on the next commit.
|
|
38442
|
+
* @file audio plugin — API factory. Three buses, the unlock flag, the journal of started sounds and the stop of a
|
|
38443
|
+
* named sound. Nothing here is persisted; a game keeps the player's choice in the player state and
|
|
38444
|
+
* `config.volumes` applies it on the next commit.
|
|
38188
38445
|
*/
|
|
38189
38446
|
/**
|
|
38190
38447
|
* Brings a value into 0..1. `NaN`, which is what a slider divided by zero produces, becomes 0.
|
|
@@ -38226,7 +38483,7 @@ function setBusVolume(state, bus, value) {
|
|
|
38226
38483
|
applyGain(state, bus);
|
|
38227
38484
|
}
|
|
38228
38485
|
/**
|
|
38229
|
-
* Creates the audio API: the three buses, the unlock flag and the
|
|
38486
|
+
* Creates the audio API: the three buses, the unlock flag, the journal and the stop of a named sound.
|
|
38230
38487
|
*
|
|
38231
38488
|
* @param ctx - Kernel context of the audio plugin.
|
|
38232
38489
|
* @returns The plugin API.
|
|
@@ -38243,7 +38500,8 @@ function createAudioApi(ctx) {
|
|
|
38243
38500
|
},
|
|
38244
38501
|
muted: (bus) => state.buses[busOf(state, bus)].muted,
|
|
38245
38502
|
unlocked: () => state.unlocked,
|
|
38246
|
-
journal: () => state.journal
|
|
38503
|
+
journal: () => state.journal,
|
|
38504
|
+
stop: (id, options) => stopNamed(state, id, options?.fadeMs ?? ctx.config.sfxFadeMs)
|
|
38247
38505
|
};
|
|
38248
38506
|
}
|
|
38249
38507
|
//#endregion
|
|
@@ -38251,13 +38509,15 @@ function createAudioApi(ctx) {
|
|
|
38251
38509
|
/**
|
|
38252
38510
|
* Replaces the journal with a frozen list that ends with one started sound, stamped with the
|
|
38253
38511
|
* elapsed game time, and keeps only the newest `config.journal` entries. A size of 0, or anything
|
|
38254
|
-
* that is not a positive number, records nothing.
|
|
38512
|
+
* that is not a positive number, records nothing. The entry has an `id` only when the sound has
|
|
38513
|
+
* one: the caller leaves the field out otherwise, so no entry carries an `undefined` key.
|
|
38255
38514
|
*
|
|
38256
38515
|
* @param ctx - Domain context of the plugin.
|
|
38257
38516
|
* @param sound - What started.
|
|
38258
38517
|
* @param sound.key - Asset key of the sound.
|
|
38259
38518
|
* @param sound.bus - The bus it plays on.
|
|
38260
38519
|
* @param sound.kind - `"sfx"` for a play, `"music"` for a track.
|
|
38520
|
+
* @param sound.id - The id of a named sound; absent for every other sound.
|
|
38261
38521
|
*/
|
|
38262
38522
|
function recordSound(ctx, sound) {
|
|
38263
38523
|
const size = Math.floor(ctx.config.journal);
|
|
@@ -38385,18 +38645,47 @@ function fieldOf(payload, field) {
|
|
|
38385
38645
|
return payload[field];
|
|
38386
38646
|
}
|
|
38387
38647
|
/**
|
|
38388
|
-
* Reads the
|
|
38648
|
+
* Reads the fade of a descriptor that ends a named sound.
|
|
38389
38649
|
*
|
|
38390
38650
|
* @param descriptor - The descriptor the handler was called with.
|
|
38651
|
+
* @param fadeMs - The configured fade, used when the descriptor names none.
|
|
38652
|
+
* @returns The fade, in milliseconds.
|
|
38653
|
+
*/
|
|
38654
|
+
function fadeOf(descriptor, fadeMs) {
|
|
38655
|
+
return validFade(fieldOf(descriptor.payload, "fadeMs"), fadeMs);
|
|
38656
|
+
}
|
|
38657
|
+
/**
|
|
38658
|
+
* Takes a fade a descriptor named only when the clock can schedule it: a finite number, 0 or more.
|
|
38659
|
+
* `NaN` or a negative fade would make the ramp and the stop throw inside the handler.
|
|
38660
|
+
*
|
|
38661
|
+
* @param fade - The `fadeMs` the payload carries.
|
|
38662
|
+
* @param fallback - The configured fade.
|
|
38663
|
+
* @returns The fade, in milliseconds.
|
|
38664
|
+
* @example
|
|
38665
|
+
* ```ts
|
|
38666
|
+
* validFade(-5, 120); // 120
|
|
38667
|
+
* ```
|
|
38668
|
+
*/
|
|
38669
|
+
function validFade(fade, fallback) {
|
|
38670
|
+
return typeof fade === "number" && Number.isFinite(fade) && fade >= 0 ? fade : fallback;
|
|
38671
|
+
}
|
|
38672
|
+
/**
|
|
38673
|
+
* Reads the key, the bus, the id and the fade out of the `sfx` descriptor `anim` builds.
|
|
38674
|
+
*
|
|
38675
|
+
* @param descriptor - The descriptor the handler was called with.
|
|
38676
|
+
* @param fadeMs - The configured fade of a named sound, used when the descriptor names none.
|
|
38391
38677
|
* @returns The request, or `undefined` when the descriptor names no key.
|
|
38392
38678
|
*/
|
|
38393
|
-
function sfxOf(descriptor) {
|
|
38679
|
+
function sfxOf(descriptor, fadeMs) {
|
|
38394
38680
|
const key = fieldOf(descriptor.payload, "key");
|
|
38395
38681
|
const bus = fieldOf(descriptor.payload, "bus");
|
|
38682
|
+
const id = fieldOf(descriptor.payload, "id");
|
|
38396
38683
|
if (typeof key !== "string") return void 0;
|
|
38397
38684
|
return {
|
|
38398
38685
|
key,
|
|
38399
|
-
bus: typeof bus === "string" ? bus : DEFAULT_BUS$1
|
|
38686
|
+
bus: typeof bus === "string" ? bus : DEFAULT_BUS$1,
|
|
38687
|
+
id: typeof id === "string" ? id : void 0,
|
|
38688
|
+
fadeMs: fadeOf(descriptor, fadeMs)
|
|
38400
38689
|
};
|
|
38401
38690
|
}
|
|
38402
38691
|
/**
|
|
@@ -38408,10 +38697,9 @@ function sfxOf(descriptor) {
|
|
|
38408
38697
|
*/
|
|
38409
38698
|
function musicOf(descriptor, fadeMs) {
|
|
38410
38699
|
const key = fieldOf(descriptor.payload, "key");
|
|
38411
|
-
const fade = fieldOf(descriptor.payload, "fadeMs");
|
|
38412
38700
|
return {
|
|
38413
38701
|
key: typeof key === "string" ? key : null,
|
|
38414
|
-
fadeMs:
|
|
38702
|
+
fadeMs: validFade(fieldOf(descriptor.payload, "fadeMs"), fadeMs)
|
|
38415
38703
|
};
|
|
38416
38704
|
}
|
|
38417
38705
|
/**
|
|
@@ -38470,9 +38758,10 @@ async function bufferOf(ctx, key) {
|
|
|
38470
38758
|
/**
|
|
38471
38759
|
* Plays one sound: the handler of the kind `"sfx"`. Every play is its own source, so the same key
|
|
38472
38760
|
* twice in one frame is heard twice, and the promise resolves when the sound STARTED. The handler
|
|
38473
|
-
* ignores its signal: a sound that began plays to its end
|
|
38474
|
-
* pending the sound waits in `pendingSfx`, one per
|
|
38475
|
-
*
|
|
38761
|
+
* ignores its signal: a sound that began plays to its end, unless a sound with its id or a stop
|
|
38762
|
+
* ends it. While a gesture's `resume()` is pending the sound waits in `pendingSfx`, one per id
|
|
38763
|
+
* and one per key without an id. A locked context no gesture is resuming drops it: the start
|
|
38764
|
+
* attempt of the unlock queues nothing.
|
|
38476
38765
|
*
|
|
38477
38766
|
* @param ctx - Domain context of the plugin.
|
|
38478
38767
|
* @param descriptor - The `sfx` descriptor `anim` built.
|
|
@@ -38480,20 +38769,79 @@ async function bufferOf(ctx, key) {
|
|
|
38480
38769
|
async function playSfx(ctx, descriptor) {
|
|
38481
38770
|
const state = ctx.state;
|
|
38482
38771
|
if (state.context === void 0) return;
|
|
38483
|
-
const request = sfxOf(descriptor);
|
|
38772
|
+
const request = sfxOf(descriptor, ctx.config.sfxFadeMs);
|
|
38484
38773
|
if (request === void 0) return;
|
|
38485
38774
|
if (!state.unlocked) {
|
|
38486
|
-
|
|
38775
|
+
queueWhileResuming(state, request);
|
|
38487
38776
|
return;
|
|
38488
38777
|
}
|
|
38489
38778
|
await playRequest(ctx, request);
|
|
38490
38779
|
}
|
|
38491
38780
|
/**
|
|
38781
|
+
* Keeps a sound fired while the context is locked, one per key and one per id, when a gesture's
|
|
38782
|
+
* `resume()` is on its way. A locked context nobody resumes drops it.
|
|
38783
|
+
*
|
|
38784
|
+
* @param state - The plugin state.
|
|
38785
|
+
* @param request - The sound that was fired.
|
|
38786
|
+
*/
|
|
38787
|
+
function queueWhileResuming(state, request) {
|
|
38788
|
+
if (!state.resuming) return;
|
|
38789
|
+
state.pendingSfx.set(request.id === void 0 ? request.key : waitingKey(request.id), request);
|
|
38790
|
+
}
|
|
38791
|
+
/**
|
|
38792
|
+
* Ends the sound under one id: the handler of the kind `"stopSfx"`, for an effect, an animation
|
|
38793
|
+
* step and a released hint alike. A descriptor without an id ends nothing.
|
|
38794
|
+
*
|
|
38795
|
+
* @param ctx - Domain context of the plugin.
|
|
38796
|
+
* @param descriptor - The `stopSfx` descriptor `anim` built.
|
|
38797
|
+
*/
|
|
38798
|
+
function stopNamedSfx(ctx, descriptor) {
|
|
38799
|
+
const id = fieldOf(descriptor.payload, "id");
|
|
38800
|
+
if (typeof id !== "string") return;
|
|
38801
|
+
stopNamed(ctx.state, id, fadeOf(descriptor, ctx.config.sfxFadeMs));
|
|
38802
|
+
}
|
|
38803
|
+
/**
|
|
38804
|
+
* Plays a sound under its id: the sound that has the id fades out over the fade of this one, and
|
|
38805
|
+
* this one starts through its own gain once its buffer is there, unless a later sound with the id
|
|
38806
|
+
* or a stop took its place while it decoded. A file that is missing frees the id.
|
|
38807
|
+
*
|
|
38808
|
+
* @param ctx - Domain context of the plugin.
|
|
38809
|
+
* @param request - The key, the fade and the id of the sound.
|
|
38810
|
+
* @param sound - The id and the bus, both checked.
|
|
38811
|
+
* @param sound.id - The id the sound plays under.
|
|
38812
|
+
* @param sound.bus - The bus it goes to.
|
|
38813
|
+
*/
|
|
38814
|
+
async function playNamed(ctx, request, sound) {
|
|
38815
|
+
const state = ctx.state;
|
|
38816
|
+
const { id, bus } = sound;
|
|
38817
|
+
const entry = claimNamed(state, id, request.fadeMs / 1e3);
|
|
38818
|
+
const buffer = await bufferOf(ctx, request.key);
|
|
38819
|
+
if (state.named.get(id) !== entry) return;
|
|
38820
|
+
const context = state.context;
|
|
38821
|
+
const gain = state.buses[bus].gain;
|
|
38822
|
+
if (buffer === void 0 || context === void 0 || gain === void 0) {
|
|
38823
|
+
state.named.delete(id);
|
|
38824
|
+
return;
|
|
38825
|
+
}
|
|
38826
|
+
startNamed(state, context, {
|
|
38827
|
+
id,
|
|
38828
|
+
entry,
|
|
38829
|
+
buffer,
|
|
38830
|
+
bus: gain
|
|
38831
|
+
});
|
|
38832
|
+
recordSound(ctx, {
|
|
38833
|
+
key: request.key,
|
|
38834
|
+
bus,
|
|
38835
|
+
kind: "sfx",
|
|
38836
|
+
id
|
|
38837
|
+
});
|
|
38838
|
+
}
|
|
38839
|
+
/**
|
|
38492
38840
|
* Starts one source of a sound on its bus. `playSfx` calls it for a live play and the unlock for
|
|
38493
|
-
* every sound that waited for the resume.
|
|
38841
|
+
* every sound that waited for the resume. A sound with an id goes through `playNamed`.
|
|
38494
38842
|
*
|
|
38495
38843
|
* @param ctx - Domain context of the plugin.
|
|
38496
|
-
* @param request - The key and the
|
|
38844
|
+
* @param request - The key, the bus, the id and the fade of the sound.
|
|
38497
38845
|
*/
|
|
38498
38846
|
async function playRequest(ctx, request) {
|
|
38499
38847
|
const state = ctx.state;
|
|
@@ -38503,6 +38851,13 @@ async function playRequest(ctx, request) {
|
|
|
38503
38851
|
ctx.log.warn("audio: unknown bus", { bus: request.bus });
|
|
38504
38852
|
return;
|
|
38505
38853
|
}
|
|
38854
|
+
if (request.id !== void 0) {
|
|
38855
|
+
await playNamed(ctx, request, {
|
|
38856
|
+
id: request.id,
|
|
38857
|
+
bus: request.bus
|
|
38858
|
+
});
|
|
38859
|
+
return;
|
|
38860
|
+
}
|
|
38506
38861
|
const buffer = await bufferOf(ctx, request.key);
|
|
38507
38862
|
const gain = state.buses[request.bus].gain;
|
|
38508
38863
|
if (buffer === void 0 || gain === void 0) return;
|
|
@@ -39027,8 +39382,8 @@ function createContext(config) {
|
|
|
39027
39382
|
return typeof browserContext === "function" ? new browserContext() : void 0;
|
|
39028
39383
|
}
|
|
39029
39384
|
/**
|
|
39030
|
-
* Starts the plugin in `onStart`: the audio session first, before any sound exists, then
|
|
39031
|
-
* effect handlers, then the context with its graph and its unlock. The unlock asks the context to
|
|
39385
|
+
* Starts the plugin in `onStart`: the audio session first, before any sound exists, then the
|
|
39386
|
+
* three effect handlers, then the context with its graph and its unlock. The unlock asks the context to
|
|
39032
39387
|
* run at once: a shell that lets audio play is unlocked with no touch, and a browser that wants a
|
|
39033
39388
|
* gesture gets the two listeners. The handlers are registered even headless, so a node that
|
|
39034
39389
|
* awaits a sound resolves in plain Bun exactly as it does in a browser.
|
|
@@ -39040,7 +39395,7 @@ function startAudio(ctx) {
|
|
|
39040
39395
|
const audio = withDeps$4(ctx);
|
|
39041
39396
|
const state = audio.state;
|
|
39042
39397
|
const fx = audio.deps.flow.fx;
|
|
39043
|
-
state.removers.push(fx.handle("sfx", (descriptor) => playSfx(audio, descriptor), { runInFast: false }), fx.handle("music", (descriptor) => playMusic(audio, musicOf(descriptor, ctx.config.musicFadeMs)), { runInFast: false }));
|
|
39398
|
+
state.removers.push(fx.handle("sfx", (descriptor) => playSfx(audio, descriptor), { runInFast: false }), fx.handle("stopSfx", (descriptor) => stopNamedSfx(audio, descriptor), { runInFast: false }), fx.handle("music", (descriptor) => playMusic(audio, musicOf(descriptor, ctx.config.musicFadeMs)), { runInFast: false }));
|
|
39044
39399
|
const context = createContext(ctx.config);
|
|
39045
39400
|
if (context === void 0) return;
|
|
39046
39401
|
state.context = context;
|
|
@@ -39049,9 +39404,9 @@ function startAudio(ctx) {
|
|
|
39049
39404
|
startUnlock(audio);
|
|
39050
39405
|
}
|
|
39051
39406
|
/**
|
|
39052
|
-
* Frees everything `onStart` opened: the
|
|
39053
|
-
* music track, every streamed track still fading with its timer, the
|
|
39054
|
-
* and the context itself. The tracks are freed before the close, while their nodes can still
|
|
39407
|
+
* Frees everything `onStart` opened: the three handler registrations, the two window listeners,
|
|
39408
|
+
* the music track, every named sound, every streamed track still fading with its timer, the
|
|
39409
|
+
* journal, the audio session and the context itself. The tracks are freed before the close, while their nodes can still
|
|
39055
39410
|
* disconnect. A browser caps how many contexts a page may hold, so the close is the point of this
|
|
39056
39411
|
* teardown.
|
|
39057
39412
|
*
|
|
@@ -39062,6 +39417,7 @@ async function stopAudio(state) {
|
|
|
39062
39417
|
for (const remove of state.removers.splice(0)) remove();
|
|
39063
39418
|
removeUnlock(state);
|
|
39064
39419
|
stopMusic(state);
|
|
39420
|
+
stopAllNamed(state);
|
|
39065
39421
|
for (const entry of state.retiring) disposeStream(state, entry.stream);
|
|
39066
39422
|
state.decoded.clear();
|
|
39067
39423
|
state.journal = Object.freeze([]);
|
|
@@ -39241,12 +39597,20 @@ function emptyWarned$2() {
|
|
|
39241
39597
|
/**
|
|
39242
39598
|
* Creates the queue of sounds fired while the unlock is pending.
|
|
39243
39599
|
*
|
|
39244
|
-
* @returns An empty queue, keyed by asset key.
|
|
39600
|
+
* @returns An empty queue, keyed by `"#" + id` or by asset key.
|
|
39245
39601
|
*/
|
|
39246
39602
|
function emptyPending() {
|
|
39247
39603
|
return /* @__PURE__ */ new Map();
|
|
39248
39604
|
}
|
|
39249
39605
|
/**
|
|
39606
|
+
* Creates the map of the sounds that play under an id.
|
|
39607
|
+
*
|
|
39608
|
+
* @returns An empty map, keyed by id.
|
|
39609
|
+
*/
|
|
39610
|
+
function emptyNamed() {
|
|
39611
|
+
return /* @__PURE__ */ new Map();
|
|
39612
|
+
}
|
|
39613
|
+
/**
|
|
39250
39614
|
* Creates the set of streamed tracks that fade out.
|
|
39251
39615
|
*
|
|
39252
39616
|
* @returns An empty set of retiring tracks.
|
|
@@ -39291,6 +39655,7 @@ function createAudioState(ctx) {
|
|
|
39291
39655
|
unlocked: false,
|
|
39292
39656
|
resuming: false,
|
|
39293
39657
|
pendingSfx: emptyPending(),
|
|
39658
|
+
named: emptyNamed(),
|
|
39294
39659
|
decoded: emptyCache$1(),
|
|
39295
39660
|
warned: emptyWarned$2(),
|
|
39296
39661
|
warnedVolumes: false,
|
|
@@ -39304,8 +39669,8 @@ function createAudioState(ctx) {
|
|
|
39304
39669
|
};
|
|
39305
39670
|
}
|
|
39306
39671
|
/**
|
|
39307
|
-
* Audio plugin: `app.audio`, the handlers of the effect kinds `sfx` and `music`, and
|
|
39308
|
-
* buses a game drives from its player state.
|
|
39672
|
+
* Audio plugin: `app.audio`, the handlers of the effect kinds `sfx`, `stopSfx` and `music`, and
|
|
39673
|
+
* the three buses a game drives from its player state.
|
|
39309
39674
|
*
|
|
39310
39675
|
* @example
|
|
39311
39676
|
* ```ts
|
|
@@ -39333,6 +39698,7 @@ const audioPlugin = /*#__PURE__*/ createPlugin$1("audio", {
|
|
|
39333
39698
|
sfx: 1
|
|
39334
39699
|
},
|
|
39335
39700
|
musicFadeMs: 600,
|
|
39701
|
+
sfxFadeMs: 120,
|
|
39336
39702
|
volumes: void 0,
|
|
39337
39703
|
context: void 0,
|
|
39338
39704
|
journal: 0,
|
|
@@ -48874,24 +49240,61 @@ function spawned(id) {
|
|
|
48874
49240
|
}
|
|
48875
49241
|
/**
|
|
48876
49242
|
* A sound. `anim` owns the descriptor because it sits below `audio`; `audio` owns the handler,
|
|
48877
|
-
* and a game without `audio` plays nothing and hears no error.
|
|
49243
|
+
* and a game without `audio` plays nothing and hears no error. A sound with an `id` is named:
|
|
49244
|
+
* one sound plays per id, a new one with the same id fades the old one out over `fadeMs`, and
|
|
49245
|
+
* `stopSfx(id)` stops it. The payload carries `id` and `fadeMs` only when they are given, so a
|
|
49246
|
+
* sound without them stays `{ key, bus }`.
|
|
48878
49247
|
*
|
|
48879
49248
|
* @param key - Asset key of the sound.
|
|
48880
|
-
* @param options - The bus
|
|
49249
|
+
* @param options - The bus, the name and the fade.
|
|
48881
49250
|
* @param options.bus - `"sfx"` when omitted.
|
|
49251
|
+
* @param options.id - Name of the sound; one sound per id plays.
|
|
49252
|
+
* @param options.fadeMs - Fade of the sound with the same id this one replaces; `audio`'s
|
|
49253
|
+
* `sfxFadeMs` when omitted.
|
|
48882
49254
|
* @returns The descriptor, usable as a timeline step and as `fx(...)` in a node.
|
|
48883
49255
|
* @example
|
|
48884
49256
|
* ```ts
|
|
48885
49257
|
* sfx("board.merge"); // { kind: "sfx", payload: { key: "board.merge", bus: "sfx" }, cosmetic: true }
|
|
49258
|
+
* sfx("voice.hello", { id: "voice", fadeMs: 200 });
|
|
49259
|
+
* // { kind: "sfx", payload: { key: "voice.hello", bus: "sfx", id: "voice", fadeMs: 200 }, cosmetic: true }
|
|
48886
49260
|
* ```
|
|
48887
49261
|
*/
|
|
48888
49262
|
function sfx(key, options) {
|
|
49263
|
+
const payload = {
|
|
49264
|
+
key,
|
|
49265
|
+
bus: options?.bus ?? DEFAULT_BUS
|
|
49266
|
+
};
|
|
49267
|
+
if (options?.id !== void 0) payload.id = options.id;
|
|
49268
|
+
if (options?.fadeMs !== void 0) payload.fadeMs = options.fadeMs;
|
|
48889
49269
|
return Object.freeze({
|
|
48890
49270
|
kind: "sfx",
|
|
48891
|
-
payload: Object.freeze(
|
|
48892
|
-
|
|
48893
|
-
|
|
48894
|
-
|
|
49271
|
+
payload: Object.freeze(payload),
|
|
49272
|
+
cosmetic: true
|
|
49273
|
+
});
|
|
49274
|
+
}
|
|
49275
|
+
/**
|
|
49276
|
+
* Stops the named sound `sfx(key, { id })` started, fading it out over `fadeMs`. `audio` owns the
|
|
49277
|
+
* handler; an id nothing plays is silent. It works as `fx.start(stopSfx("voice"))` in a node, as a
|
|
49278
|
+
* step a timeline reaches, and as the hint `hint("stopSfx", { id: "voice" })`. A timeline that is
|
|
49279
|
+
* finished or cut fires no effect, this one included: a game that must stop the voice then stops
|
|
49280
|
+
* it from the node.
|
|
49281
|
+
*
|
|
49282
|
+
* @param id - The id the sound was played with.
|
|
49283
|
+
* @param options - The fade.
|
|
49284
|
+
* @param options.fadeMs - Fade-out in ms; `audio`'s `sfxFadeMs` when omitted.
|
|
49285
|
+
* @returns The descriptor, usable as a timeline step and as `fx(...)` in a node.
|
|
49286
|
+
* @example
|
|
49287
|
+
* ```ts
|
|
49288
|
+
* stopSfx("voice", { fadeMs: 200 }); // { kind: "stopSfx", payload: { id: "voice", fadeMs: 200 }, cosmetic: true }
|
|
49289
|
+
* stopSfx("voice"); // { kind: "stopSfx", payload: { id: "voice" }, cosmetic: true }
|
|
49290
|
+
* ```
|
|
49291
|
+
*/
|
|
49292
|
+
function stopSfx(id, options) {
|
|
49293
|
+
const payload = { id };
|
|
49294
|
+
if (options?.fadeMs !== void 0) payload.fadeMs = options.fadeMs;
|
|
49295
|
+
return Object.freeze({
|
|
49296
|
+
kind: "stopSfx",
|
|
49297
|
+
payload: Object.freeze(payload),
|
|
48895
49298
|
cosmetic: true
|
|
48896
49299
|
});
|
|
48897
49300
|
}
|
|
@@ -57245,7 +57648,10 @@ function createReconciler(ctx, modules, fields) {
|
|
|
57245
57648
|
function reconcileDirtyRoot(root) {
|
|
57246
57649
|
const tree = ecs.get(root.entity, Tree);
|
|
57247
57650
|
const place = ctx.deps.world.projection.keyOf(root.entity);
|
|
57248
|
-
if (place !== void 0
|
|
57651
|
+
if (place !== void 0 && place.projection !== root.name) {
|
|
57652
|
+
root.name = place.projection;
|
|
57653
|
+
hostsMoved(state);
|
|
57654
|
+
}
|
|
57249
57655
|
if (tree !== void 0) root.tree = tree.node;
|
|
57250
57656
|
refreshRootOrder(root);
|
|
57251
57657
|
root.dirty = !modules.layout.loaded();
|
|
@@ -62466,6 +62872,7 @@ function cubicBezier(x1, y1, x2, y2) {
|
|
|
62466
62872
|
* | ui | textInput | { caretWidth: 3, caret: 0x000000, selection: 0x3390ff, selectionAlpha: 0.35, composingUnderline: 3, keyboardMargin: 16 }; shallow merge |
|
|
62467
62873
|
* | audio | buses | { master: 1, music: 0.6, sfx: 1 }; shallow merge: a game that sets `buses` gives all three fields |
|
|
62468
62874
|
* | audio | musicFadeMs | 600 |
|
|
62875
|
+
* | audio | sfxFadeMs | 120 |
|
|
62469
62876
|
* | audio | volumes | undefined, the buses stay at `buses` |
|
|
62470
62877
|
* | audio | context | undefined, the real AudioContext |
|
|
62471
62878
|
* | audio | journal | 0, no started sound is kept |
|
|
@@ -62597,4 +63004,4 @@ const screen = [
|
|
|
62597
63004
|
uiPlugin
|
|
62598
63005
|
];
|
|
62599
63006
|
//#endregion
|
|
62600
|
-
export { sfx as $,
|
|
63007
|
+
export { sfx as $, flowPlugin as $t, types_exports$10 as A, Glow as At, defineMotion as B, Animation as Bt, types_exports$3 as C, messageDuration as Ct, types_exports$7 as D, Blur as Dt, types_exports$6 as E, Alpha as Et, types_exports$15 as F, audioPlugin as Ft, external as G, MONITOR_SECTIONS as Gt, platformPlugin as H, rendererPlugin as Ht, types_exports$16 as I, scenesPlugin as It, mark as J, PERF_LOG_VERSION as Jt, frames as K, PERF_CAUSES as Kt, types_exports$17 as L, assetsPlugin as Lt, types_exports$12 as M, Outline as Mt, types_exports$13 as N, defineFilter as Nt, types_exports$8 as O, ColorMatrix as Ot, types_exports$14 as P, debugPlugin as Pt, set as Q, worldPlugin as Qt, uiPlugin as R, animPlugin as Rt, types_exports$2 as S, messageArgument as St, types_exports$5 as T, Emitter as Tt, HAPTIC_KINDS as U, PASS_KINDS as Ut, textPlugin as V, Frames as Vt, defineAnimation as W, frameAt as Wt, play$1 as X, SHADER_WHATS as Xt, parallel as Y, PROBLEM_RULES as Yt, sequence as Z, DEMAND_REASONS as Zt, defineBundles as _, Tappable as _t, cubicBezier as a, modelPlugin as an, use as at, types_exports as b, Traced as bt, hint as c, timePlugin as cn, Draggable as ct, defineScene as d, Hovered as dt, refuseStart as en, spawn as et, tr as f, Pointer as ft, music as g, Swipeable as gt, defineEmitter as h, Pressed as ht, screen as i, type as in, tween as it, types_exports$11 as j, Noise as jt, types_exports$9 as k, Displacement as kt, schedule as l, clockPlugin as ln, DropTarget as lt, flattenFeatures as m, Pressable as mt, createPlugin as n, slot as nn, stagger as nt, defineEffect as o, SaveUnreadableError as on, wait as ot, defineFeature as p, PointerOver as pt, haptic as q, PERF_GAP_BUCKETS as qt, defineGame as r, to as rn, stopSfx as rt, guide as s, lifecyclePlugin as sn, inputPlugin as st, createApp as t, exit as tn, spawned as tt, projection as u, Held as ut, load as v, Touchable as vt, types_exports$4 as w, effectsPlugin as wt, types_exports$1 as x, i18nPlugin as xt, ready as y, Traceable as yt, resolve as z, AnimPlayer as zt };
|