@idle-screens/saver-metaquarium 0.21.0 → 0.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-5DJ5WBVN.js → chunk-M2PKNQNY.js} +257 -36
- package/dist/chunk-M2PKNQNY.js.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +3 -3
- package/dist/{lofi-tank-Z2GDMEPV.js → lofi-tank-H2IMIIF3.js} +2 -2
- package/dist/{manifest-CiE6U104.d.ts → manifest-CsnZvlWq.d.ts} +45 -2
- package/dist/manifest.d.ts +1 -1
- package/dist/manifest.js +1 -1
- package/dist/{tank-3CC56AJN.js → tank-S4MH4U2P.js} +607 -312
- package/dist/tank-S4MH4U2P.js.map +1 -0
- package/package.json +5 -5
- package/dist/chunk-5DJ5WBVN.js.map +0 -1
- package/dist/tank-3CC56AJN.js.map +0 -1
- /package/dist/{lofi-tank-Z2GDMEPV.js.map → lofi-tank-H2IMIIF3.js.map} +0 -0
|
@@ -1620,7 +1620,7 @@ function shardGeometry(rng) {
|
|
|
1620
1620
|
for (let i = 0; i < n; i += 1) tris.push([top[i], tip, top[(i + 1) % n]]);
|
|
1621
1621
|
const positions = new Float32Array(tris.length * 9);
|
|
1622
1622
|
const normals = new Float32Array(tris.length * 9);
|
|
1623
|
-
const
|
|
1623
|
+
const smooth4 = new Float32Array(tris.length * 9);
|
|
1624
1624
|
tris.forEach((tri, t) => {
|
|
1625
1625
|
const [a, b, c] = tri;
|
|
1626
1626
|
const ux = b[0] - a[0], uy = b[1] - a[1], uz = b[2] - a[2];
|
|
@@ -1648,12 +1648,12 @@ function shardGeometry(rng) {
|
|
|
1648
1648
|
normals[o + 2] = nz;
|
|
1649
1649
|
const sy = (p[1] - 0.55) * 0.25;
|
|
1650
1650
|
const sl = Math.hypot(p[0], sy, p[2]) || 1;
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1651
|
+
smooth4[o] = p[0] / sl;
|
|
1652
|
+
smooth4[o + 1] = sy / sl;
|
|
1653
|
+
smooth4[o + 2] = p[2] / sl;
|
|
1654
1654
|
});
|
|
1655
1655
|
});
|
|
1656
|
-
return { positions, normals, smooth:
|
|
1656
|
+
return { positions, normals, smooth: smooth4, triangles: tris.length };
|
|
1657
1657
|
}
|
|
1658
1658
|
var HABIT_RINGS = {
|
|
1659
1659
|
lotus: [[1, 0], [6, 30], [9, 55], [16, 85]],
|
|
@@ -2500,10 +2500,196 @@ function parseSignMix(value) {
|
|
|
2500
2500
|
return { entries, problems };
|
|
2501
2501
|
}
|
|
2502
2502
|
|
|
2503
|
+
// src/choreo.ts
|
|
2504
|
+
var STARFISH_MOVE_NAMES = [
|
|
2505
|
+
"march",
|
|
2506
|
+
"jacks",
|
|
2507
|
+
"reach",
|
|
2508
|
+
"kick",
|
|
2509
|
+
"twist",
|
|
2510
|
+
"circles",
|
|
2511
|
+
"disco",
|
|
2512
|
+
"spin",
|
|
2513
|
+
"mambo",
|
|
2514
|
+
"sway",
|
|
2515
|
+
"twirl",
|
|
2516
|
+
"dip",
|
|
2517
|
+
"hold",
|
|
2518
|
+
"walk",
|
|
2519
|
+
"lie",
|
|
2520
|
+
"crawl",
|
|
2521
|
+
"curl",
|
|
2522
|
+
"wave",
|
|
2523
|
+
"pop",
|
|
2524
|
+
"rise",
|
|
2525
|
+
"sit"
|
|
2526
|
+
];
|
|
2527
|
+
var CHOREO_MARKS = {
|
|
2528
|
+
home: [0, 0],
|
|
2529
|
+
left: [-2, 0],
|
|
2530
|
+
right: [2, 0],
|
|
2531
|
+
front: [0, 1.2],
|
|
2532
|
+
back: [0, -1.5],
|
|
2533
|
+
centre: "centre"
|
|
2534
|
+
};
|
|
2535
|
+
var MAX_CUES = 256;
|
|
2536
|
+
function parseChoreography(value, moves) {
|
|
2537
|
+
const lines = /* @__PURE__ */ new Map(), problems = [];
|
|
2538
|
+
const known = new Set(moves);
|
|
2539
|
+
for (const rawLine of value.split("|").map((s) => s.trim()).filter(Boolean)) {
|
|
2540
|
+
const head = /^([a-z]|\*)\s*:\s*(?=\d|\.)/.exec(rawLine);
|
|
2541
|
+
const actor = head ? head[1] === "*" ? "*" : head[1].charCodeAt(0) - 97 : "*";
|
|
2542
|
+
const body = head ? rawLine.slice(head[0].length) : rawLine;
|
|
2543
|
+
if (lines.has(actor)) problems.push(`dancer ${actor === "*" ? "*" : String.fromCharCode(97 + actor)} has two lines; the later one wins`);
|
|
2544
|
+
const cues = [];
|
|
2545
|
+
let b0 = 0;
|
|
2546
|
+
for (const raw of body.split(/,\s*(?=(?:\d+(?:\.\d+)?|\.\d+)\s*:)/).map((s) => s.trim()).filter(Boolean)) {
|
|
2547
|
+
if (cues.length >= MAX_CUES) {
|
|
2548
|
+
problems.push(`more than ${MAX_CUES} cues on one line; the rest are dropped`);
|
|
2549
|
+
break;
|
|
2550
|
+
}
|
|
2551
|
+
const m = /^(\d+(?:\.\d+)?|\.\d+)\s*:\s*([a-z][a-z_]*)([^a-z_].*)?$/.exec(raw);
|
|
2552
|
+
if (!m) {
|
|
2553
|
+
problems.push(`"${raw}" is not <beats>:<move>`);
|
|
2554
|
+
continue;
|
|
2555
|
+
}
|
|
2556
|
+
const beats = Number(m[1]);
|
|
2557
|
+
if (!(beats > 0)) {
|
|
2558
|
+
problems.push(`"${raw}": a cue needs some beats`);
|
|
2559
|
+
continue;
|
|
2560
|
+
}
|
|
2561
|
+
if (!known.has(m[2])) {
|
|
2562
|
+
problems.push(`"${raw}": no move "${m[2]}" (moves: ${moves.join(" ")})`);
|
|
2563
|
+
continue;
|
|
2564
|
+
}
|
|
2565
|
+
const cue = { b0, beats: Math.min(beats, 512), move: m[2], turn: 0, lift: 0 };
|
|
2566
|
+
for (const tok of (m[3] ?? "").replace(/^[,\s]+/, "").split(/\s+/).filter(Boolean)) {
|
|
2567
|
+
const mod = /^([>@~^/%*x=])(.+)$/.exec(tok);
|
|
2568
|
+
if (!mod) {
|
|
2569
|
+
problems.push(`"${raw}": "${tok}" is not >to, @face, ~turn, ^leap, /lean, %crouch, *groove, xspeed or =pose`);
|
|
2570
|
+
continue;
|
|
2571
|
+
}
|
|
2572
|
+
const [, op, arg] = mod;
|
|
2573
|
+
if (op === ">") {
|
|
2574
|
+
const mark = CHOREO_MARKS[arg];
|
|
2575
|
+
const xy = /^(-?\d+(?:\.\d+)?),(-?\d+(?:\.\d+)?)$/.exec(arg);
|
|
2576
|
+
if (mark) cue.to = mark;
|
|
2577
|
+
else if (xy) cue.to = [clamp(Number(xy[1]), -6, 6), clamp(Number(xy[2]), -6, 6)];
|
|
2578
|
+
else problems.push(`"${raw}": travel to "${arg}" \u2014 a mark (${Object.keys(CHOREO_MARKS).join(" ")}) or side,depth`);
|
|
2579
|
+
} else if (op === "@") {
|
|
2580
|
+
const named = { cam: "cam", room: 0, front: 0, back: 180, left: -90, right: 90 };
|
|
2581
|
+
if (arg in named) cue.face = named[arg];
|
|
2582
|
+
else if (Number.isFinite(Number(arg))) cue.face = Number(arg);
|
|
2583
|
+
else problems.push(`"${raw}": face "${arg}" \u2014 degrees, cam, room, left, right or back`);
|
|
2584
|
+
} else if (op === "/") {
|
|
2585
|
+
if (Number.isFinite(Number(arg))) cue.lean = clamp(Number(arg), -60, 60);
|
|
2586
|
+
else problems.push(`"${raw}": lean "${arg}" is not degrees`);
|
|
2587
|
+
} else if (op === "%") {
|
|
2588
|
+
if (Number.isFinite(Number(arg)) && Number(arg) > 0) cue.squash = clamp(Number(arg), 0.4, 1.5);
|
|
2589
|
+
else problems.push(`"${raw}": crouch "${arg}" is not a height scale`);
|
|
2590
|
+
} else if (op === "=") {
|
|
2591
|
+
if (arg !== "" && Number.isFinite(Number(arg)) && Number(arg) >= 0 && Number(arg) <= 1) cue.pose = Number(arg);
|
|
2592
|
+
else problems.push(`"${raw}": pose "${arg}" is not a fraction of the move (0\u20131)`);
|
|
2593
|
+
} else if (op === "x") {
|
|
2594
|
+
if (Number.isFinite(Number(arg)) && Number(arg) > 0) cue.rate = clamp(Number(arg), 0.25, 4);
|
|
2595
|
+
else problems.push(`"${raw}": speed "${arg}" is not a multiple`);
|
|
2596
|
+
} else if (op === "*") {
|
|
2597
|
+
if (Number.isFinite(Number(arg)) && Number(arg) > 0) cue.groove = clamp(Number(arg), 0.02, 0.3);
|
|
2598
|
+
else problems.push(`"${raw}": groove "${arg}" is not a depth`);
|
|
2599
|
+
} else if (op === "~") {
|
|
2600
|
+
if (Number.isFinite(Number(arg))) cue.turn = clamp(Number(arg), -1440, 1440);
|
|
2601
|
+
else problems.push(`"${raw}": turn "${arg}" is not degrees`);
|
|
2602
|
+
} else {
|
|
2603
|
+
if (Number.isFinite(Number(arg)) && Number(arg) >= 0) cue.lift = Math.min(Number(arg), 3);
|
|
2604
|
+
else problems.push(`"${raw}": leap "${arg}" is not a height`);
|
|
2605
|
+
}
|
|
2606
|
+
}
|
|
2607
|
+
cues.push(cue);
|
|
2608
|
+
b0 += cue.beats;
|
|
2609
|
+
}
|
|
2610
|
+
if (cues.length) lines.set(actor, { cues, beats: b0 });
|
|
2611
|
+
else problems.push(`line "${rawLine}" has no cues`);
|
|
2612
|
+
}
|
|
2613
|
+
return { lines, problems };
|
|
2614
|
+
}
|
|
2615
|
+
var CHOREO_HANDOVER = 0.5;
|
|
2616
|
+
var clamp = (v, lo, hi) => Math.min(hi, Math.max(lo, v));
|
|
2617
|
+
var smooth3 = (u) => {
|
|
2618
|
+
const c = clamp(u, 0, 1);
|
|
2619
|
+
return c * c * (3 - 2 * c);
|
|
2620
|
+
};
|
|
2621
|
+
var posMod = (v, m) => (v % m + m) % m;
|
|
2622
|
+
function choreoLine(sheet, slot) {
|
|
2623
|
+
return sheet?.lines.get(slot) ?? sheet?.lines.get("*") ?? null;
|
|
2624
|
+
}
|
|
2625
|
+
function choreoAt(line, beats) {
|
|
2626
|
+
const b = posMod(beats, line.beats);
|
|
2627
|
+
const n = line.cues.length;
|
|
2628
|
+
let i = 0;
|
|
2629
|
+
while (i < n - 1 && b >= line.cues[i].b0 + line.cues[i].beats) i++;
|
|
2630
|
+
const cue = line.cues[i], next = line.cues[(i + 1) % n], prev = line.cues[(i + n - 1) % n];
|
|
2631
|
+
let side = 0, depth = 0, centre = 0, face = 0, faceCam = 0, turn = 0;
|
|
2632
|
+
const end = (c) => {
|
|
2633
|
+
if (c.to === "centre") centre = 1;
|
|
2634
|
+
else if (c.to) {
|
|
2635
|
+
side = c.to[0];
|
|
2636
|
+
depth = c.to[1];
|
|
2637
|
+
centre = 0;
|
|
2638
|
+
}
|
|
2639
|
+
turn += c.turn;
|
|
2640
|
+
};
|
|
2641
|
+
const facing = (c) => {
|
|
2642
|
+
if (c.face === "cam") faceCam = 1;
|
|
2643
|
+
else if (c.face !== void 0) {
|
|
2644
|
+
face = c.face;
|
|
2645
|
+
faceCam = 0;
|
|
2646
|
+
}
|
|
2647
|
+
};
|
|
2648
|
+
for (let k = 0; k < i; k++) {
|
|
2649
|
+
end(line.cues[k]);
|
|
2650
|
+
facing(line.cues[k]);
|
|
2651
|
+
}
|
|
2652
|
+
facing(cue);
|
|
2653
|
+
const beat = b - cue.b0, u = clamp(beat / cue.beats, 0, 1);
|
|
2654
|
+
const s = smooth3(u);
|
|
2655
|
+
if (cue.to === "centre") centre += (1 - centre) * s;
|
|
2656
|
+
else if (cue.to) {
|
|
2657
|
+
side += (cue.to[0] - side) * s;
|
|
2658
|
+
depth += (cue.to[1] - depth) * s;
|
|
2659
|
+
centre *= 1 - s;
|
|
2660
|
+
}
|
|
2661
|
+
turn += cue.turn * s;
|
|
2662
|
+
const lift = cue.lift > 0 ? cue.lift * Math.sin(Math.PI * u) : 0;
|
|
2663
|
+
const last = i === n - 1;
|
|
2664
|
+
const h = Math.min(CHOREO_HANDOVER, cue.beats / 2, next.beats / 2);
|
|
2665
|
+
const ahead = smooth3((beat - (cue.beats - h)) / h);
|
|
2666
|
+
if (ahead > 0) {
|
|
2667
|
+
if (next.face === "cam") faceCam += (1 - faceCam) * ahead;
|
|
2668
|
+
else if (next.face !== void 0 && !last) {
|
|
2669
|
+
face += angleTo(face, next.face) * ahead;
|
|
2670
|
+
faceCam *= 1 - ahead;
|
|
2671
|
+
} else if (last) {
|
|
2672
|
+
face += angleTo(face, next.face === void 0 ? 0 : next.face) * ahead;
|
|
2673
|
+
faceCam *= 1 - ahead;
|
|
2674
|
+
}
|
|
2675
|
+
}
|
|
2676
|
+
const lean = (cue.lean ?? 0) + ((next.lean ?? 0) - (cue.lean ?? 0)) * ahead;
|
|
2677
|
+
let squash = (cue.squash ?? 1) + ((next.squash ?? 1) - (cue.squash ?? 1)) * ahead;
|
|
2678
|
+
if (next.lift > 0) squash *= 1 - 0.16 * ahead;
|
|
2679
|
+
if (cue.lift > 0) squash *= u < 0.15 ? 0.84 + 0.26 * smooth3(u / 0.15) : 1 + 0.1 * Math.sin(Math.PI * u) - 0.16 * smooth3((u - 0.85) / 0.15);
|
|
2680
|
+
if (prev.lift > 0 && n > 1) squash *= 0.84 + 0.16 * smooth3(beat / Math.min(0.75, cue.beats));
|
|
2681
|
+
const groove = cue.groove ?? 0;
|
|
2682
|
+
if (groove > 0 && cue.lift === 0) squash *= 1 - groove * (1 + Math.cos(2 * Math.PI * posMod(beats, 1))) / 2;
|
|
2683
|
+
return { cue, beat, u, next, ahead, side, depth, centre, face, faceCam, turn, lift, lean, squash };
|
|
2684
|
+
}
|
|
2685
|
+
function angleTo(a, b) {
|
|
2686
|
+
return posMod(b - a + 180, 360) - 180;
|
|
2687
|
+
}
|
|
2688
|
+
|
|
2503
2689
|
// src/vignette.ts
|
|
2504
2690
|
var DANCES = ["toprock", "footwork", "windmill", "headspin", "freeze", "drop"];
|
|
2505
|
-
var GESTURES = ["talk", "nod", "shake", "hop", "spin", "wiggle", "rest", "peek", "bow", "ink", ...DANCES];
|
|
2506
|
-
var BREED_GESTURES = { ink: ["octopus"] };
|
|
2691
|
+
var GESTURES = ["talk", "nod", "shake", "hop", "spin", "wiggle", "rest", "peek", "bow", "ink", "puff", ...DANCES];
|
|
2692
|
+
var BREED_GESTURES = { ink: ["octopus"], puff: ["blowfish"] };
|
|
2507
2693
|
var OPEN_MARKS = {
|
|
2508
2694
|
centre: { x: 0, y: 36, z: 0 },
|
|
2509
2695
|
left: { x: -70, y: 34, z: 0 },
|
|
@@ -2788,6 +2974,7 @@ function poseOf(v, actor, tSec, own) {
|
|
|
2788
2974
|
pitch = -0.08 * e;
|
|
2789
2975
|
break;
|
|
2790
2976
|
// ink: the body holds still; the octopus squirts its cloud (the tank's InkLayer) and pales.
|
|
2977
|
+
// puff: the body holds still; the blowfish gulps itself round (puffer.ts PufferInput.puffCue).
|
|
2791
2978
|
// The dances, for a fish with no clips for them: the whole body does what it can.
|
|
2792
2979
|
case "toprock":
|
|
2793
2980
|
roll += Math.sin(bt * Math.PI * 2) * 0.3 * e;
|
|
@@ -3121,23 +3308,25 @@ var PARAM_DOCS = {
|
|
|
3121
3308
|
cameraAzimuth: "Orbit bearing in degrees. 0 looks at the village from the front.",
|
|
3122
3309
|
cameraElevation: "Degrees above the floor. 8\u201315 is eye level with the fish; 40+ looks down on a castle.",
|
|
3123
3310
|
cameraDistance: "Distance from the tank centre. 80 is a close-up, 170\u2013220 a village, 300+ a landmark.",
|
|
3124
|
-
shot: "A framing: `orbit
|
|
3311
|
+
shot: "A framing: `orbit`, `hero` (three-quarter), `front` (level, long lens), `low`, `top` (a map), `surface`, `macro` (nearest landmark), `cast` (the mix's first three). Azimuth and autoRotate turn it, distance scales it.",
|
|
3125
3312
|
shotBlend: "Seconds a change of `shot` takes (0 = a cut): the camera moves to the new framing, eased.",
|
|
3126
|
-
cameraFollow: "Ride with one fish (cast slot); -1 is the orbit camera
|
|
3127
|
-
followAngle: "Where round the followed fish the chase camera stands, degrees: 0 behind (default), 90 beside, 180
|
|
3128
|
-
followDistance: "How far behind the followed fish the camera rides (40\u201370 is a chase). Under ~18 it is the fish's eye
|
|
3129
|
-
autoRotate: "Turntable speed, degrees/second. 0 holds the shot
|
|
3130
|
-
followAvoid: "Keep the followed fish in view, 0\u20131: the chase camera rises
|
|
3131
|
-
|
|
3313
|
+
cameraFollow: "Ride with one fish (cast slot); -1 is the orbit camera (its params ignored while following).",
|
|
3314
|
+
followAngle: "Where round the followed fish the chase camera stands, degrees: 0 behind (default), 90 beside, 180 facing it. A choreographed dancer is measured from the stage's facing instead.",
|
|
3315
|
+
followDistance: "How far behind the followed fish the camera rides (40\u201370 is a chase). Under ~18 it is the fish's eye (a walker's: its head height).",
|
|
3316
|
+
autoRotate: "Turntable speed, degrees/second. 0 holds the shot, 1\u20132 a slow reveal. frame.state.camera.bearing: where it stands now.",
|
|
3317
|
+
followAvoid: "Keep the followed fish in view, 0\u20131: the chase camera rises or pulls in, eased.",
|
|
3318
|
+
followHeight: "Chase camera height in body lengths over its feet, aimed at them (0.2: feet); -1 off.",
|
|
3319
|
+
followLens: "Chase lens, degrees (0: 55; 25 long).",
|
|
3320
|
+
nearFade: "A creature at the lens dissolves, not sliced, 0\u20131. The followed fish never fades.",
|
|
3132
3321
|
// cast
|
|
3133
3322
|
fishCount: "How many fish when `fishMix` is empty (all are `fishUrl`).",
|
|
3134
3323
|
finish: "A final full-screen pass: gentle grade, soft vignette, dither against banding, and restrained bloom on high-end devices (0 = none; never on the low tier).",
|
|
3135
3324
|
tiltShift: "Tilt-shift, 0\u20131: sharp through the middle, soft above and below, like a model. Mid/high tiers.",
|
|
3136
|
-
depthOfField: "Depth of field, 0\u20131: the subject
|
|
3325
|
+
depthOfField: "Depth of field, 0\u20131: the subject (followed fish, `cast` leads) sharp, the rest soft. High tier.",
|
|
3137
3326
|
ambientOcclusion: "Ambient occlusion, 0\u20131: crevices darken. High tier.",
|
|
3138
|
-
highlights: "Bright light rolls off instead of clipping, 0\u20131: a fish in a shaft stays a fish.
|
|
3327
|
+
highlights: "Bright light rolls off instead of clipping, 0\u20131: a fish in a shaft stays a fish. Mid/high tiers.",
|
|
3139
3328
|
fishUrl: "The single model used when `fishMix` is empty: ipfs://, https://assets.idlescreens.com/, or a URL on the page's own origin. Any other host is refused and the default fish swims.",
|
|
3140
|
-
fishMix: "The cast: `id[:count][@style]`, comma-separated: `100:2@drift,
|
|
3329
|
+
fishMix: "The cast: `id[:count][@style]`, comma-separated: `100:2@drift, seahorse:3, shark:1@patrol`. Minted breeds plus bundled: shark crab dori glowfish babyfish hackerfish blowfish starfish octopus snail dot dolphin. Floor walkers keep their own pace: crab, starfish (dances: `starfishDance`, `choreography`), snail. `dot` (a box on legs), `dolphin` (flips, breaches) play vignette gestures. Overrides fishCount/fishUrl.",
|
|
3141
3330
|
dracoPath: "Where the Draco decoder lives; leave empty on a channel. Only the page's own origin or the shipped copy is used.",
|
|
3142
3331
|
swimSpeed: "Global swim speed multiplier. 0.4 is contemplative, 1 lively.",
|
|
3143
3332
|
swimStyle: "How the cast swims unless a fishMix entry says otherwise (`@style`). `auto` picks by breed.",
|
|
@@ -3161,14 +3350,16 @@ var PARAM_DOCS = {
|
|
|
3161
3350
|
fishGlow: "How strongly GLOW- parts of a fish bloom and light their neighbours and the floor.",
|
|
3162
3351
|
fishMetal: "`on` (default) gives METAL- parts a polished, reflective finish.",
|
|
3163
3352
|
snailShell: "Snail shells: `banded`, `pearl` (iridescent), `gold`, `glow` (a spiral breathing light; best at night); `mix` (default), each its own. Lit fish only. frame.state.fish[i].shell says which.",
|
|
3164
|
-
starfishDance: "Starfish dance: `aerobics
|
|
3165
|
-
danceTempo: "
|
|
3353
|
+
starfishDance: "Starfish dance: `aerobics` (a class in unison), `freestyle` (a move each per eight counts), `duet` (couples), `off`. Cast a few: `starfish:7`.",
|
|
3354
|
+
danceTempo: "Dance BPM; glides on the beat.",
|
|
3355
|
+
choreography: "Starfish dance cues, on the beat (see grammar).",
|
|
3166
3356
|
fishLook: "`neon` dresses the bundled creatures for blacklight: near-black coats, eyes (and a crab's mouth) glowing a neon of their own. Best in a dark room with `finish`. Default `natural`.",
|
|
3167
3357
|
fishRim: "A thin edge of the water's light round each fish, 0\u20131 (lit mode): dark fish on dark water. 0.6\u20131 in dark rooms.",
|
|
3168
|
-
fishShadows: "Soft shadows
|
|
3169
|
-
|
|
3170
|
-
|
|
3171
|
-
|
|
3358
|
+
fishShadows: "Soft shadows under the swimmers, 0\u20131: dark near the sand, faint high up.",
|
|
3359
|
+
fishAO: "A fish's own corners darken, 0\u20131: solid fish. All tiers.",
|
|
3360
|
+
fishFadeIn: "Seconds an arriving fish takes to fade in (0 pops in).",
|
|
3361
|
+
sceneryShadows: "Soft shadows under rocks, homes, castle, pyramids, geodes, lamps, 0\u20131: nothing floats.",
|
|
3362
|
+
voxelAO: "Plant cubes darken where they touch each other and the sand, 0\u20131. All tiers.",
|
|
3172
3363
|
swimWave: "Fish bend as they swim: a wave nose to tail, the body curling into turns (0 = rigid wiggle). Skips the turtle, crab and fish with a skeleton; the seahorse gets fin, tail and nod instead.",
|
|
3173
3364
|
fishAvoid: "Fish make way for each other: a pair about to meet parts a second ahead, the smaller giving more, over or under before round. 1 (default) on, 0 pass-through. `inspect().crowding` counts pairs still touching.",
|
|
3174
3365
|
eyeLife: "Eyes blink, look, and emote (0 = painted on). Redraws each token's own pixel-grid eye; black and white only.",
|
|
@@ -3188,7 +3379,7 @@ var PARAM_DOCS = {
|
|
|
3188
3379
|
waterY: "Height of the water ceiling; -1 = the environment's own.",
|
|
3189
3380
|
rayStrength: "Surface light shafts, within device budget: 5 high tier, 2 standard; none on the lowest tier with a surface (abyss, vent, universe keep 2; perceiveChannel then reads 0).",
|
|
3190
3381
|
raySoftness: "Shafts as soft volume, not flat sheets, 0\u20131. 1 for anything cinematic.",
|
|
3191
|
-
floorDetail: "A seabed, not a coloured plane, 0\u20131: ripples, silt, grain;
|
|
3382
|
+
floorDetail: "A seabed, not a coloured plane, 0\u20131: ripples, silt, grain; its far edge fades into the fog.",
|
|
3192
3383
|
caustics: "The dancing net of light from the surface on the floor, rocks, plants and fish (0 = none). Sharp near the top, soft and dim deep down.",
|
|
3193
3384
|
causticScale: "Size of the caustic cells: below 1 a fine shimmer, above 1 broad slow bands.",
|
|
3194
3385
|
surfaceMirror: "The surface from below: a window to the light in a rippling mirror of the tank (0 = plain). Needs reef, kelp, ice or lagoon; for the `surface` and `low` shots. High tier mirrors the tank, mid the deep water.",
|
|
@@ -3243,7 +3434,7 @@ var PARAM_DOCS = {
|
|
|
3243
3434
|
spotColor: "Colour of the single `followSpot`.",
|
|
3244
3435
|
spotShadow: "The spotted fish's shadow in its pool. 0 none, 1 true size.",
|
|
3245
3436
|
spotRig: "Up to three spots: `slot[/color][*radius]` follows a fish, `@mark` stands on a mark (`@mic` lights whoever steps up). They are a, b, c in order. Overrides followSpot.",
|
|
3246
|
-
spotCues: "
|
|
3437
|
+
spotCues: "Looping cues for the rig: `8s:a, 12s:a+b, 4s:-` (`-` blackout, cues \u2265 2 s). Spots cross-fade.",
|
|
3247
3438
|
vignette: "A small scene for the first 2\u20133 fish: a preset (tea\xB7bedtime\xB7seek indoors; duet\xB7trio\xB7openmic on the open stage) or a script of beats. A floor creature cast as an actor stays on the floor. Grammar below."
|
|
3248
3439
|
};
|
|
3249
3440
|
var GRAMMAR = `
|
|
@@ -3269,13 +3460,14 @@ geodeMineral world | mineral, \u2026 amethyst agate celestine citrine carneli
|
|
|
3269
3460
|
floraPalette world | #rrggbb, \u2026 (\u22646) world \xB7 #ff4fa0, #ffb347, #4fd1ff
|
|
3270
3461
|
spotRig slot|@mark[/color][*radius], \u2026 (\u22643; they become a, b, c) 0/#ffd27a*24, 1/#ff8ad0*24, 2/#7fdcff*24
|
|
3271
3462
|
spotCues <sec>s:<spots>, \u2026 loops 4s:-, 7s:a, 7s:b, 12s:a+b
|
|
3463
|
+
choreography [a|*:] <beats>:<move> [>to @face ~turn ^leap /lean %crouch *groove xspeed =pose],\u2026|\u2026 moves ${STARFISH_MOVE_NAMES.join(" ")}
|
|
3272
3464
|
signs kind[@place][>target][/#color][*size][:text], \u2026 plank@gate:"Welcome to the reef", neon@home1/#ff4fa0:OPEN, led@plaza:TIDE 4.2M
|
|
3273
3465
|
kinds plank arrow ring porthole neon led \xB7 places gate plaza courtyard hub fountain home1 home2 home3 centre left right front back
|
|
3274
3466
|
an arrow points at >target \xB7 text \u2264 plank 24, arrow 16, ring 10, porthole 4 (empty = a fish), neon 14, led 64 (scrolls) \xB7 \u2264 8 signs
|
|
3275
3467
|
vignette beats separated by |, cues by comma; actors a b c d are fish slots 0 1 2 3
|
|
3276
3468
|
[<sec>s:] actor =mark be there, no travel (a later beat's = snaps at its start) actor >mark swim there
|
|
3277
3469
|
actor @mark | @actor face it a b circle mark each named actor rings it, back to its spot by the beat's end; others stay put
|
|
3278
|
-
actor follow actor gestures: talk nod shake hop spin wiggle rest peek bow \xB7 ink (octopus
|
|
3470
|
+
actor follow actor gestures: talk nod shake hop spin wiggle rest peek bow \xB7 ink (octopus) \xB7 puff (blowfish)
|
|
3279
3471
|
dances: toprock footwork windmill headspin freeze drop (a betafish dances them with its own body; 2 s beats keep them on a 120 bpm bar)
|
|
3280
3472
|
Give every beat a duration and end where you began and a spotCues sheet stays in step forever.
|
|
3281
3473
|
Fish cast @audience sit out front facing the stage: they laugh as each talk ends and applaud each bow.
|
|
@@ -3288,7 +3480,7 @@ vignette beats separated by |, cues by comma; actors a b c d are fish slots 0 1
|
|
|
3288
3480
|
var NIGHT = { fogColor: "#0a0a1a", fogNear: 120, fogFar: 700 };
|
|
3289
3481
|
var STAGE = { fogColor: "#01030a", floorColor: "#0a1322", fogNear: 120, fogFar: 650, autoRotate: 0 };
|
|
3290
3482
|
var ALIVE = { eyeLife: 1 };
|
|
3291
|
-
var CINEMATIC = { highlights: 1, raySoftness: 1, nearFade: 1, finish: 0.6 };
|
|
3483
|
+
var CINEMATIC = { highlights: 1, raySoftness: 1, nearFade: 1, voxelAO: 1, fishAO: 1, finish: 0.6 };
|
|
3292
3484
|
var RECIPES = [
|
|
3293
3485
|
{
|
|
3294
3486
|
id: "geode-harbor",
|
|
@@ -3567,7 +3759,7 @@ var RECIPES = [
|
|
|
3567
3759
|
{
|
|
3568
3760
|
id: "starfish-class",
|
|
3569
3761
|
label: "Starfish aerobics",
|
|
3570
|
-
what: "Seven starfish stand up on a pale ice floor, face the camera and dance
|
|
3762
|
+
what: "Seven starfish stand up on a pale ice floor, face the camera and dance aerobics in unison, an instructor in front.",
|
|
3571
3763
|
params: { environment: "ice", fishMix: "starfish:7", starfishDance: "aerobics", danceTempo: 120, shot: "front", autoRotate: 0 }
|
|
3572
3764
|
},
|
|
3573
3765
|
{
|
|
@@ -3609,7 +3801,7 @@ var RECIPES = [
|
|
|
3609
3801
|
{
|
|
3610
3802
|
id: "snail-night",
|
|
3611
3803
|
label: "Snails by night",
|
|
3612
|
-
what: "Glowing snails on a dark floor: each spiral breathes light,
|
|
3804
|
+
what: "Glowing snails on a dark floor: each spiral breathes light, feelers pool green on the sand, the camera beside one.",
|
|
3613
3805
|
params: {
|
|
3614
3806
|
...ALIVE,
|
|
3615
3807
|
...NIGHT,
|
|
@@ -3628,7 +3820,7 @@ var RECIPES = [
|
|
|
3628
3820
|
{
|
|
3629
3821
|
id: "reef-cinematic",
|
|
3630
3822
|
label: "Reef, cinematic",
|
|
3631
|
-
what: "A small reef cast framed close wherever it swims, in soft shafts, each fish over its shadow.
|
|
3823
|
+
what: "A small reef cast framed close wherever it swims, in soft shafts, each fish over its shadow.",
|
|
3632
3824
|
params: {
|
|
3633
3825
|
...ALIVE,
|
|
3634
3826
|
...CINEMATIC,
|
|
@@ -3653,7 +3845,7 @@ var RECIPES = [
|
|
|
3653
3845
|
{
|
|
3654
3846
|
id: "night-cinematic",
|
|
3655
3847
|
label: "Deep night, cinematic",
|
|
3656
|
-
what: "The deep at night: glowfish under jellyfish lanterns, a shark sliding through,
|
|
3848
|
+
what: "The deep at night: glowfish under jellyfish lanterns, a shark sliding through, outlines rimmed in light.",
|
|
3657
3849
|
params: {
|
|
3658
3850
|
...ALIVE,
|
|
3659
3851
|
...CINEMATIC,
|
|
@@ -3677,7 +3869,7 @@ var RECIPES = [
|
|
|
3677
3869
|
{
|
|
3678
3870
|
id: "village-cinematic",
|
|
3679
3871
|
label: "Village, cinematic",
|
|
3680
|
-
what: "Dot, Puff and Hex meet in a sunlit village of stone homes, seen
|
|
3872
|
+
what: "Dot, Puff and Hex meet in a sunlit village of stone homes, seen through a tilt-shift lens.",
|
|
3681
3873
|
params: {
|
|
3682
3874
|
...ALIVE,
|
|
3683
3875
|
...CINEMATIC,
|
|
@@ -3725,6 +3917,7 @@ function validateMetaquariumParams(params) {
|
|
|
3725
3917
|
if (str("townProps")) push("townProps", parseTownProps(str("townProps")).problems);
|
|
3726
3918
|
if (str("floorCover")) push("floorCover", parseFloorCover(str("floorCover")).problems);
|
|
3727
3919
|
if (str("pyramidShow")) push("pyramidShow", parsePyramidShow(str("pyramidShow")).problems);
|
|
3920
|
+
if (str("choreography")) push("choreography", parseChoreography(str("choreography"), STARFISH_MOVE_NAMES).problems);
|
|
3728
3921
|
if (str("geodeMineral")) push("geodeMineral", parseGeodeMineral(str("geodeMineral")).problems);
|
|
3729
3922
|
const rig = parseSpotRig(str("spotRig"));
|
|
3730
3923
|
push("spotRig", rig.problems);
|
|
@@ -3841,9 +4034,11 @@ function dependencyProblems(params) {
|
|
|
3841
4034
|
if (dance !== "off" && !starfishSwims) {
|
|
3842
4035
|
out.push({ path: "starfishDance", also: ["fishMix"], message: "no starfish in the cast to dance \u2014 add e.g. starfish:7 (slots past the cap never swim)" });
|
|
3843
4036
|
}
|
|
3844
|
-
|
|
4037
|
+
const choreographed = !!str("choreography").trim();
|
|
4038
|
+
if (set("danceTempo") && dance === "off" && !choreographed) out.push({ path: "danceTempo", also: ["starfishDance"], message: "does nothing while starfishDance is off" });
|
|
4039
|
+
if (choreographed && !starfishSwims) out.push({ path: "choreography", also: ["fishMix"], message: "no starfish in the cast to dance it \u2014 add e.g. starfish:5" });
|
|
3845
4040
|
if (follow < 0) {
|
|
3846
|
-
for (const k of ["followDistance", "followAngle", "followAvoid"]) if (set(k)) out.push({ path: k, also: ["cameraFollow"], message: "does nothing while cameraFollow is -1 (the orbit camera)" });
|
|
4041
|
+
for (const k of ["followDistance", "followAngle", "followAvoid", "followHeight", "followLens"]) if (set(k)) out.push({ path: k, also: ["cameraFollow"], message: "does nothing while cameraFollow is -1 (the orbit camera)" });
|
|
3847
4042
|
} else if (follow < cast) {
|
|
3848
4043
|
for (const k of ORBIT_PARAMS) if (set(k)) out.push({ path: k, also: ["cameraFollow"], message: `ignored while cameraFollow rides with slot ${follow} \u2014 they return when it is -1` });
|
|
3849
4044
|
}
|
|
@@ -3995,6 +4190,7 @@ function describeMetaquarium(params, timed = []) {
|
|
|
3995
4190
|
stage.push(VIGNETTES[v] ? `vignette: ${v}` : `vignette: ${v.split(/[|\n]/).filter((b) => b.trim()).length} beats`);
|
|
3996
4191
|
}
|
|
3997
4192
|
if (str("starfishDance") !== "off") stage.push(`starfish ${str("starfishDance")}${has("danceTempo") ? ` at ${num("danceTempo")} bpm` : ""}`);
|
|
4193
|
+
if (str("choreography").trim()) stage.push(`a starfish choreography${has("danceTempo") ? ` at ${num("danceTempo")} bpm` : ""}`);
|
|
3998
4194
|
const camera = [];
|
|
3999
4195
|
const follow = Math.round(num("cameraFollow"));
|
|
4000
4196
|
if (follow >= 0) {
|
|
@@ -4074,7 +4270,8 @@ var METAQUARIUM_PARAMS = {
|
|
|
4074
4270
|
followDistance: { type: "number", default: 45, min: 0, max: 160, ease: "smooth" },
|
|
4075
4271
|
/** Where round the followed fish the chase camera stands, degrees: 0 behind
|
|
4076
4272
|
* it (the default), 90 at its side, 180 in front looking back at its face
|
|
4077
|
-
* — a hackerfish's screen, a crab's smile.
|
|
4273
|
+
* — a hackerfish's screen, a crab's smile. A choreographed dancer is framed
|
|
4274
|
+
* from its stage's front instead, so a spin turns the dancer, not the shot. */
|
|
4078
4275
|
followAngle: { type: "number", default: 0, min: -180, max: 180, ease: "smooth" },
|
|
4079
4276
|
/** Keep the followed fish in view (sightline.ts): when a home, a wall, a lamp
|
|
4080
4277
|
* or a kelp stand comes between it and the chase camera, the camera rises a
|
|
@@ -4082,6 +4279,15 @@ var METAQUARIUM_PARAMS = {
|
|
|
4082
4279
|
* a game camera's spring arm does — eased, never a snap. 0 (default) is the
|
|
4083
4280
|
* plain chase camera; 1 the full arm. Not the fish's-eye view (it has none). */
|
|
4084
4281
|
followAvoid: { type: "number", default: 0, min: 0, max: 1, ease: "smooth" },
|
|
4282
|
+
/** The chase camera's height over the followed fish's feet, in its body
|
|
4283
|
+
* lengths. -1 (default) rides a little above it, looking at its middle;
|
|
4284
|
+
* set, the camera stands at that height aimed at its feet — 0.1–0.3 on a
|
|
4285
|
+
* long lens is a shot of a walker's feet, the floor behind them. */
|
|
4286
|
+
followHeight: { type: "number", default: -1, min: -1, max: 3, ease: "step" },
|
|
4287
|
+
/** The chase camera's lens: degrees of view, top to bottom. 0 (default) is
|
|
4288
|
+
* the orbit camera's 55; 20–30 is a long lens — flatter and tighter, from
|
|
4289
|
+
* further back. */
|
|
4290
|
+
followLens: { type: "number", default: 0, min: 0, max: 90, ease: "step" },
|
|
4085
4291
|
/** Continuous orbit speed, degrees/second. Zero by default: the tank is
|
|
4086
4292
|
* still, letting the fish movement carry the scene. Steer up for an orbit. */
|
|
4087
4293
|
autoRotate: { type: "number", default: 0, min: 0, max: 12, ease: "smooth" },
|
|
@@ -4284,6 +4490,10 @@ var METAQUARIUM_PARAMS = {
|
|
|
4284
4490
|
* mode), brighter on top: what separates a dark fish from dark water.
|
|
4285
4491
|
* 0 is none. */
|
|
4286
4492
|
fishRim: { type: "number", default: 0, min: 0, max: 1, ease: "smooth" },
|
|
4493
|
+
/** The fish's own corners darken — a fin's root, the step at the head, the
|
|
4494
|
+
* gap under a shell — read off the voxels they are built from. Baked once
|
|
4495
|
+
* per breed, the first frame it is on; every tier. 0 is off. */
|
|
4496
|
+
fishAO: { type: "number", default: 0, min: 0, max: 1, ease: "smooth" },
|
|
4287
4497
|
/** A soft shadow on the ground under every swimmer: small and dark near
|
|
4288
4498
|
* the sand, wide and faint high up — what puts a fish at a height instead
|
|
4289
4499
|
* of floating in front of the floor. 0 is none (floor walkers always
|
|
@@ -4352,6 +4562,13 @@ var METAQUARIUM_PARAMS = {
|
|
|
4352
4562
|
/** The dance's tempo, beats per minute. Every move is a bar of four beats.
|
|
4353
4563
|
* Integrated when steered, so a glide speeds the class up on the beat. */
|
|
4354
4564
|
danceTempo: { type: "number", default: 128, min: 60, max: 180, ease: "smooth" },
|
|
4565
|
+
/** An authored dance on the beat (choreo.ts): a cue sheet per dancer —
|
|
4566
|
+
* `a: 8:lie, 4:rise, 4:reach >right @cam | *: 4:march ~360, 4:jacks ^0.5`.
|
|
4567
|
+
* Set, every starfish dances it (in the class's places, or the couples'
|
|
4568
|
+
* under `starfishDance: duet`), facing `cameraAzimuth`; a dancer it leaves
|
|
4569
|
+
* out keeps `starfishDance`'s routine, or stands and waits. Beats on the
|
|
4570
|
+
* `danceTempo` clock from the start, so a sheet lines up with music. */
|
|
4571
|
+
choreography: { type: "string", default: "", ease: "step" },
|
|
4355
4572
|
/** Eye life, 0..1: blinks on a personal clock, idle saccades, pupils that
|
|
4356
4573
|
* lead a turn or a climb, eyes on whoever a vignette has it talking to, a
|
|
4357
4574
|
* glance at the camera now and then, wide for a hop and shut for a rest.
|
|
@@ -4751,6 +4968,10 @@ export {
|
|
|
4751
4968
|
SIGN_TEXT_MAX,
|
|
4752
4969
|
MAX_SIGNS,
|
|
4753
4970
|
parseSignMix,
|
|
4971
|
+
STARFISH_MOVE_NAMES,
|
|
4972
|
+
parseChoreography,
|
|
4973
|
+
choreoLine,
|
|
4974
|
+
choreoAt,
|
|
4754
4975
|
GESTURES,
|
|
4755
4976
|
OPEN_MARKS,
|
|
4756
4977
|
parseVignette,
|
|
@@ -4777,4 +4998,4 @@ export {
|
|
|
4777
4998
|
paramSpaceWith,
|
|
4778
4999
|
coerceNum
|
|
4779
5000
|
};
|
|
4780
|
-
//# sourceMappingURL=chunk-
|
|
5001
|
+
//# sourceMappingURL=chunk-M2PKNQNY.js.map
|