@idle-screens/saver-metaquarium 0.5.0 → 0.6.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-23P73MYY.js → chunk-O676ILL6.js} +115 -34
- package/dist/chunk-O676ILL6.js.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/{manifest-Bk9LNZb9.d.ts → manifest-C2vsSgNu.d.ts} +58 -9
- package/dist/manifest.d.ts +1 -1
- package/dist/manifest.js +3 -1
- package/dist/{tank-6IEOYV4X.js → tank-D3XVDWJM.js} +94 -38
- package/dist/tank-D3XVDWJM.js.map +1 -0
- package/package.json +3 -3
- package/dist/chunk-23P73MYY.js.map +0 -1
- package/dist/tank-6IEOYV4X.js.map +0 -1
|
@@ -1,55 +1,69 @@
|
|
|
1
1
|
// src/environments.ts
|
|
2
2
|
var FLOOR_KINDS = ["flat", "dunes", "ridges", "basin"];
|
|
3
3
|
var ENVIRONMENTS = [
|
|
4
|
-
{ name: "void", label: "Void", water: null, floor: "flat", rays: null },
|
|
4
|
+
{ name: "void", label: "Void", water: null, floor: "flat", rays: null, seedSalt: 0 },
|
|
5
5
|
{
|
|
6
6
|
name: "abyss",
|
|
7
7
|
label: "Abyss",
|
|
8
8
|
water: null,
|
|
9
9
|
floor: "basin",
|
|
10
|
-
|
|
10
|
+
seedSalt: 161,
|
|
11
|
+
rays: { strength: 0.35, color: "#0e4a6e", y: -620 },
|
|
12
|
+
palette: { fog: "#02030a", floor: "#05070f", mote: "#4fd4a8" }
|
|
11
13
|
},
|
|
12
14
|
{
|
|
13
15
|
name: "reef",
|
|
14
16
|
label: "Reef",
|
|
15
17
|
water: { y: 150, color: "#2ad4ff", opacity: 0.18 },
|
|
16
18
|
floor: "dunes",
|
|
17
|
-
rays: { strength: 0.5, color: "#bfefff", y: 900 }
|
|
19
|
+
rays: { strength: 0.5, color: "#bfefff", y: 900 },
|
|
20
|
+
seedSalt: 162,
|
|
21
|
+
palette: { fog: "#0a3d5c", floor: "#123c50", mote: "#bfe8ff" }
|
|
18
22
|
},
|
|
19
23
|
{
|
|
20
24
|
name: "kelp",
|
|
21
25
|
label: "Kelp forest",
|
|
22
26
|
water: { y: 170, color: "#1e6f5c", opacity: 0.2 },
|
|
23
27
|
floor: "ridges",
|
|
24
|
-
rays: { strength: 0.4, color: "#9ef5d0", y: 820 }
|
|
28
|
+
rays: { strength: 0.4, color: "#9ef5d0", y: 820 },
|
|
29
|
+
seedSalt: 163,
|
|
30
|
+
palette: { fog: "#07271e", floor: "#0c2f22", mote: "#a5f2c6" }
|
|
25
31
|
},
|
|
26
32
|
{
|
|
27
33
|
name: "ice",
|
|
28
34
|
label: "Under ice",
|
|
29
35
|
water: { y: 132, color: "#cfefff", opacity: 0.32 },
|
|
30
36
|
floor: "flat",
|
|
31
|
-
rays: { strength: 0.6, color: "#eaf8ff", y: 1e3 }
|
|
37
|
+
rays: { strength: 0.6, color: "#eaf8ff", y: 1e3 },
|
|
38
|
+
seedSalt: 164,
|
|
39
|
+
palette: { fog: "#12283c", floor: "#1b3d59", mote: "#d8f2ff" }
|
|
32
40
|
},
|
|
33
41
|
{
|
|
34
42
|
name: "vent",
|
|
35
43
|
label: "Hydrothermal vent",
|
|
36
44
|
water: null,
|
|
37
45
|
floor: "ridges",
|
|
38
|
-
|
|
46
|
+
seedSalt: 165,
|
|
47
|
+
rays: { strength: 0.45, color: "#ff7a3c", y: -520 },
|
|
48
|
+
palette: { fog: "#170502", floor: "#4a1a0a", mote: "#ff8a3c" }
|
|
39
49
|
},
|
|
40
50
|
{
|
|
41
51
|
name: "lagoon",
|
|
42
52
|
label: "Lagoon",
|
|
43
53
|
water: { y: 118, color: "#7ff3d0", opacity: 0.22 },
|
|
44
54
|
floor: "dunes",
|
|
45
|
-
rays: { strength: 0.55, color: "#fff3b0", y: 950 }
|
|
55
|
+
rays: { strength: 0.55, color: "#fff3b0", y: 950 },
|
|
56
|
+
seedSalt: 166,
|
|
57
|
+
palette: { fog: "#0d453f", floor: "#6e4457", mote: "#eafff4" }
|
|
46
58
|
},
|
|
47
59
|
{
|
|
48
60
|
name: "universe",
|
|
49
61
|
label: "Universe",
|
|
50
62
|
water: null,
|
|
51
63
|
floor: "ridges",
|
|
52
|
-
|
|
64
|
+
seedSalt: 167,
|
|
65
|
+
rays: { strength: 0.3, color: "#c39bff", y: 1200 },
|
|
66
|
+
palette: { fog: "#0d0618", floor: "#171030", mote: "#cfa8ff" }
|
|
53
67
|
}
|
|
54
68
|
];
|
|
55
69
|
var ENVIRONMENT_NAMES = ENVIRONMENTS.map((e) => e.name);
|
|
@@ -74,7 +88,10 @@ var SWIM_STYLES = [
|
|
|
74
88
|
{ name: "loop", label: "Loop", speedMul: 1, band: "free", bobAmp: 0, bobHz: 0, formation: false, travel: 1 },
|
|
75
89
|
{ name: "school", label: "School", speedMul: 1, band: "free", bobAmp: 1.5, bobHz: 0.5, formation: true, travel: 1 },
|
|
76
90
|
{ name: "drift", label: "Drift", speedMul: 0.35, band: "mid", bobAmp: 5, bobHz: 0.13, formation: false, travel: 0.6 },
|
|
77
|
-
|
|
91
|
+
// speedMul 0.55, not 0.2: QA measured hover at swimSpeed 0.5 pinned to a
|
|
92
|
+
// fixed screen x for 8+ seconds — frozen, not station-keeping. The two
|
|
93
|
+
// multiplied slowdowns (speedMul x travel) were the freeze.
|
|
94
|
+
{ name: "hover", label: "Hover", speedMul: 0.55, band: "free", bobAmp: 7, bobHz: 0.3, formation: false, travel: 0.12 },
|
|
78
95
|
{ name: "patrol", label: "Patrol", speedMul: 0.55, band: "mid", bobAmp: 1, bobHz: 0.09, formation: false, travel: 1 },
|
|
79
96
|
{ name: "bottom", label: "Bottom-hugger", speedMul: 0.7, band: "floor", bobAmp: 2, bobHz: 0.4, formation: false, travel: 1 },
|
|
80
97
|
{ name: "surface", label: "Surface-skimmer", speedMul: 0.9, band: "ceiling", bobAmp: 3, bobHz: 0.55, formation: false, travel: 1 }
|
|
@@ -105,6 +122,10 @@ function fishVariation(index, variance) {
|
|
|
105
122
|
anchor: fishHash(index, 7)
|
|
106
123
|
};
|
|
107
124
|
}
|
|
125
|
+
function anchorFraction(style, index, variance) {
|
|
126
|
+
if (style.name === "loop") return 0;
|
|
127
|
+
return fishVariation(index, variance).anchor;
|
|
128
|
+
}
|
|
108
129
|
var FISH_LENGTH = 18;
|
|
109
130
|
var FORMATION_HALF_WIDTH = 62;
|
|
110
131
|
var FORMATION_SHAPES = ["phalanx", "line", "ring", "wedge", "ball"];
|
|
@@ -138,12 +159,12 @@ function formationSlot(index, count, variance, halfWidth = FORMATION_HALF_WIDTH,
|
|
|
138
159
|
const within = index % VSIZE;
|
|
139
160
|
const rank = within + 1 >> 1;
|
|
140
161
|
const wing = within === 0 ? 0 : within % 2 === 1 ? -1 : 1;
|
|
162
|
+
const droop = (rank - 2) * FISH_LENGTH * (layers === 1 ? 0.24 : 0.12);
|
|
163
|
+
const pitch2 = layers === 1 ? 1.5 : 1.2;
|
|
164
|
+
const jit2 = layers === 1 ? 0.2 : 0.15;
|
|
141
165
|
return {
|
|
142
166
|
side: wing * rank * FISH_LENGTH * 1.25 + (fishHash(index, 4) - 0.5) * FISH_LENGTH * 0.3 * j,
|
|
143
|
-
|
|
144
|
-
// the same rank directly above each other, so pitch minus worst-case
|
|
145
|
-
// jitter closure must clear one body length.
|
|
146
|
-
up: (layer - (layers - 1) / 2) * FISH_LENGTH * 1.5 + (fishHash(index, 5) - 0.5) * FISH_LENGTH * 0.2 * j,
|
|
167
|
+
up: (layer - (layers - 1) / 2) * FISH_LENGTH * pitch2 + (fishHash(index, 5) - 0.5) * FISH_LENGTH * jit2 * j - droop,
|
|
147
168
|
back: (rank - 2) * FISH_LENGTH * 1.35
|
|
148
169
|
};
|
|
149
170
|
}
|
|
@@ -371,11 +392,13 @@ function swimPoseAtDistance(plan, dist) {
|
|
|
371
392
|
// src/maneuver.ts
|
|
372
393
|
var MANEUVERS = ["none", "dart", "startle", "graze", "curious", "zoomies"];
|
|
373
394
|
var SPECS = {
|
|
374
|
-
dart: { name: "dart", dur: 2.4, interval: 14, advance: 4.5, kick: 0.5, lift: 0, flurry: 1.1, bursts: 1 },
|
|
375
|
-
startle: { name: "startle", dur: 1.8, interval: 18, advance: 1.2, kick: 2.8, lift: 0.4, flurry: 1.3, bursts: 1 },
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
395
|
+
dart: { name: "dart", dur: 2.4, interval: 14, advance: 4.5, kick: 0.5, lift: 0, flurry: 1.1, bursts: 1, pitch: 0, contagious: false },
|
|
396
|
+
startle: { name: "startle", dur: 1.8, interval: 18, advance: 1.2, kick: 2.8, lift: 0.4, flurry: 1.3, bursts: 1, pitch: 0.18, contagious: true },
|
|
397
|
+
// Graze sinks for real now: lift beyond ±1 is deliberate — QA measured the
|
|
398
|
+
// old -0.7 × 0.6 at 7.6u of drop, indistinguishable from hover's idle bob.
|
|
399
|
+
graze: { name: "graze", dur: 5.5, interval: 16, advance: -3.2, kick: 0.6, lift: -2.1, flurry: 0.3, bursts: 1, pitch: -0.55, contagious: false },
|
|
400
|
+
curious: { name: "curious", dur: 4, interval: 15, advance: 0.8, kick: 2, lift: 0.2, flurry: 0.5, bursts: 1, pitch: 0.12, contagious: false },
|
|
401
|
+
zoomies: { name: "zoomies", dur: 3.2, interval: 20, advance: 7.5, kick: 0.9, lift: 0.15, flurry: 1.5, bursts: 3, pitch: 0, contagious: false }
|
|
379
402
|
};
|
|
380
403
|
function maneuverSpecOf(name) {
|
|
381
404
|
return Object.hasOwn(SPECS, name) ? SPECS[name] : null;
|
|
@@ -390,38 +413,44 @@ function surges(u, bursts) {
|
|
|
390
413
|
if (u <= 0) return 0;
|
|
391
414
|
if (u >= 1) return 1;
|
|
392
415
|
const x = u * bursts;
|
|
393
|
-
|
|
416
|
+
const frac = x - Math.floor(x);
|
|
417
|
+
return (Math.floor(x) + smooth(frac / 0.55)) / bursts;
|
|
394
418
|
}
|
|
395
|
-
var IDLE = { along: 0, alongBump: 0, side: 0, up: 0, flurry: 0 };
|
|
396
|
-
function maneuverAt(spec, index, tSec, rate, intensity) {
|
|
419
|
+
var IDLE = { along: 0, alongBump: 0, side: 0, up: 0, flurry: 0, pitch: 0 };
|
|
420
|
+
function maneuverAt(spec, index, tSec, rate, intensity, seatDelay = null) {
|
|
397
421
|
if (!spec || rate <= 0 || intensity <= 0 || tSec < 0) return IDLE;
|
|
398
|
-
const interval = spec.interval * (3 - 2 * Math.
|
|
422
|
+
const interval = rate <= 1 ? spec.interval * (3 - 2 * rate) : Math.max(spec.dur * 1.15, spec.interval / rate);
|
|
399
423
|
const k0 = Math.floor(tSec / interval);
|
|
400
424
|
let along = 0;
|
|
401
425
|
let side = 0;
|
|
402
426
|
let up = 0;
|
|
403
427
|
let flurry = 0;
|
|
428
|
+
let pitch = 0;
|
|
404
429
|
let alongBump = 0;
|
|
405
430
|
const done = Math.max(0, k0 - 1);
|
|
406
|
-
|
|
431
|
+
const perm = 0.6 + 0.8 * fishHash(index, 37);
|
|
432
|
+
along += done * spec.advance * intensity * perm;
|
|
407
433
|
for (const k of [k0 - 1, k0, k0 + 1]) {
|
|
408
434
|
if (k < 0) continue;
|
|
409
|
-
const
|
|
435
|
+
const wave = spec.contagious && seatDelay !== null;
|
|
436
|
+
const jitter = wave ? fishHash(1013, 11 + k) : fishHash(index, 11 + k);
|
|
437
|
+
const start = (k + jitter) * interval + (wave ? seatDelay : 0);
|
|
410
438
|
const u = (tSec - start) / spec.dur;
|
|
411
439
|
if (u <= 0) continue;
|
|
412
440
|
if (u >= 1) {
|
|
413
|
-
if (k >= done) along += spec.advance * intensity;
|
|
441
|
+
if (k >= done) along += spec.advance * intensity * perm;
|
|
414
442
|
continue;
|
|
415
443
|
}
|
|
416
444
|
const sign = fishHash(index, 23 + k) < 0.5 ? -1 : 1;
|
|
417
445
|
const env = bump(u) * intensity;
|
|
418
|
-
along += spec.advance * surges(u, spec.bursts) * intensity;
|
|
446
|
+
along += spec.advance * surges(u, spec.bursts) * intensity * perm;
|
|
419
447
|
alongBump += spec.advance * bump(u) * intensity;
|
|
420
448
|
side += sign * spec.kick * env;
|
|
421
449
|
up += spec.lift * spec.kick * env;
|
|
422
450
|
flurry += spec.flurry * env;
|
|
451
|
+
pitch += spec.pitch * env;
|
|
423
452
|
}
|
|
424
|
-
return { along, alongBump, side, up, flurry };
|
|
453
|
+
return { along, alongBump, side, up, flurry, pitch };
|
|
425
454
|
}
|
|
426
455
|
|
|
427
456
|
// src/asset-cids.ts
|
|
@@ -1095,6 +1124,26 @@ var NPC_CATALOG = [
|
|
|
1095
1124
|
function parseFishMix(mix, catalog = FISH_CATALOG) {
|
|
1096
1125
|
const entries = [];
|
|
1097
1126
|
const problems = [];
|
|
1127
|
+
const unique = catalog === FISH_CATALOG;
|
|
1128
|
+
const used = /* @__PURE__ */ new Set();
|
|
1129
|
+
const allocate = (breed, want) => {
|
|
1130
|
+
const b = BREEDS.find((x) => x.breed === breed);
|
|
1131
|
+
if (!b?.range) return null;
|
|
1132
|
+
const [lo, hi] = b.range;
|
|
1133
|
+
if (!used.has(want) && want >= lo && want <= hi) {
|
|
1134
|
+
used.add(want);
|
|
1135
|
+
return want;
|
|
1136
|
+
}
|
|
1137
|
+
for (let d = 1; d <= hi - lo; d += 1) {
|
|
1138
|
+
for (const cand of [want + d, want - d]) {
|
|
1139
|
+
if (cand >= lo && cand <= hi && !used.has(cand)) {
|
|
1140
|
+
used.add(cand);
|
|
1141
|
+
return cand;
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
return null;
|
|
1146
|
+
};
|
|
1098
1147
|
for (const rawToken of mix.split(",")) {
|
|
1099
1148
|
const token = rawToken.trim();
|
|
1100
1149
|
if (token === "") continue;
|
|
@@ -1108,8 +1157,8 @@ function parseFishMix(mix, catalog = FISH_CATALOG) {
|
|
|
1108
1157
|
if (!fish && catalog === FISH_CATALOG && /^\d+$/.test(key)) {
|
|
1109
1158
|
const n = Number(key);
|
|
1110
1159
|
const url2 = fishAsset(n, "3d");
|
|
1111
|
-
const
|
|
1112
|
-
if (url2 &&
|
|
1160
|
+
const breed2 = breedOf(n);
|
|
1161
|
+
if (url2 && breed2) fish = { id: n, name: `Fish ${n}`, breed: breed2, ipfs3d: url2, localGlb: "" };
|
|
1113
1162
|
}
|
|
1114
1163
|
if (!fish) {
|
|
1115
1164
|
const breeds = [...new Set(catalog.map((f) => f.breed))].join(", ");
|
|
@@ -1137,7 +1186,34 @@ function parseFishMix(mix, catalog = FISH_CATALOG) {
|
|
|
1137
1186
|
count = n;
|
|
1138
1187
|
}
|
|
1139
1188
|
}
|
|
1140
|
-
|
|
1189
|
+
if (!unique || fish.id > TOTAL_SUPPLY) {
|
|
1190
|
+
entries.push({ id: fish.id, url, count });
|
|
1191
|
+
continue;
|
|
1192
|
+
}
|
|
1193
|
+
const breed = breedOf(fish.id);
|
|
1194
|
+
if (!breed) {
|
|
1195
|
+
entries.push({ id: fish.id, url, count });
|
|
1196
|
+
continue;
|
|
1197
|
+
}
|
|
1198
|
+
const isAlias = !/^\d+$/.test(key);
|
|
1199
|
+
const b = BREEDS.find((x) => x.breed === breed);
|
|
1200
|
+
for (let i = 0; i < count; i += 1) {
|
|
1201
|
+
const want = isAlias && b?.range ? b.range[0] + Math.floor((b.range[1] - b.range[0]) * i / Math.max(1, count)) : fish.id;
|
|
1202
|
+
const got = allocate(breed, want);
|
|
1203
|
+
if (got === null) {
|
|
1204
|
+
problems.push(`"${token}": only ${i} distinct ${breed} left \u2014 a minted fish appears once per scene`);
|
|
1205
|
+
break;
|
|
1206
|
+
}
|
|
1207
|
+
if (!isAlias && i === 0 && got !== fish.id) {
|
|
1208
|
+
problems.push(`fish ${fish.id} already cast \u2014 fish ${got} swims instead`);
|
|
1209
|
+
}
|
|
1210
|
+
const gotUrl = fishAsset(got, "3d");
|
|
1211
|
+
if (!gotUrl) {
|
|
1212
|
+
problems.push(`fish ${got}: no asset URL`);
|
|
1213
|
+
continue;
|
|
1214
|
+
}
|
|
1215
|
+
entries.push({ id: got, url: gotUrl, count: 1 });
|
|
1216
|
+
}
|
|
1141
1217
|
}
|
|
1142
1218
|
return { entries, problems };
|
|
1143
1219
|
}
|
|
@@ -1173,7 +1249,10 @@ var METAQUARIUM_PARAMS = {
|
|
|
1173
1249
|
fishUrl: { type: "string", default: "ipfs://QmaHbEQAP6k2zopJHJBzyaK62zNX5yH8yASDjkaG4DY9Dp/fish_257_of_the_metaquarium_3d.glb" },
|
|
1174
1250
|
/** Mixed population DSL: comma-separated `id[:count]` of a minted token id
|
|
1175
1251
|
* (1-512, default catalog) or breed name — e.g. "257:3,100:2,seaturtle:1".
|
|
1176
|
-
* A
|
|
1252
|
+
* A minted fish is an INDIVIDUAL: no id appears twice in a scene, and a
|
|
1253
|
+
* count casts that many DISTINCT fish of the breed ("300:12" is twelve
|
|
1254
|
+
* different angelfish). A custom catalog is a closed world (no farm/IPFS
|
|
1255
|
+
* fallback, no uniqueness — NPC entries are species). Counts are
|
|
1177
1256
|
* absolute; the expanded total clamps to the device tier's fish cap.
|
|
1178
1257
|
* Non-empty mix OVERRIDES fishUrl and fishCount; empty string = single-
|
|
1179
1258
|
* breed mode. Unknown ids/bad counts degrade (good tokens still parse);
|
|
@@ -1239,8 +1318,9 @@ var METAQUARIUM_PARAMS = {
|
|
|
1239
1318
|
* addressable. `none` (default) changes nothing. */
|
|
1240
1319
|
maneuver: { type: "enum", default: "none", options: [...MANEUVERS], ease: "step" },
|
|
1241
1320
|
/** How often events fire: 0 never, 1 the maneuver's own tempo (roughly
|
|
1242
|
-
* every 14-20s per fish, desynchronised)
|
|
1243
|
-
|
|
1321
|
+
* every 14-20s per fish, desynchronised), above 1 faster — 3 packs events
|
|
1322
|
+
* nearly back to back. Values <=1 mean exactly what they always did. */
|
|
1323
|
+
maneuverRate: { type: "number", default: 0.5, min: 0, max: 3, ease: "smooth" },
|
|
1244
1324
|
/** How hard: scales the surge, the kick, and the tail flurry together. */
|
|
1245
1325
|
maneuverIntensity: { type: "number", default: 0.7, min: 0, max: 1, ease: "smooth" },
|
|
1246
1326
|
/** Procedural body yaw for models that carry NO animation clip — most of
|
|
@@ -1355,6 +1435,7 @@ export {
|
|
|
1355
1435
|
swimStyleOf,
|
|
1356
1436
|
fishHash,
|
|
1357
1437
|
fishVariation,
|
|
1438
|
+
anchorFraction,
|
|
1358
1439
|
FISH_LENGTH,
|
|
1359
1440
|
FORMATION_SHAPES,
|
|
1360
1441
|
formationSlot,
|
|
@@ -1394,4 +1475,4 @@ export {
|
|
|
1394
1475
|
paramSpaceWith,
|
|
1395
1476
|
coerceNum
|
|
1396
1477
|
};
|
|
1397
|
-
//# sourceMappingURL=chunk-
|
|
1478
|
+
//# sourceMappingURL=chunk-O676ILL6.js.map
|