@idle-screens/saver-metaquarium 0.4.0 → 0.5.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-BWIJDJPQ.js → chunk-23P73MYY.js} +352 -21
- package/dist/chunk-23P73MYY.js.map +1 -0
- package/dist/chunk-SBRO3JPO.js +36 -0
- package/dist/chunk-SBRO3JPO.js.map +1 -0
- package/dist/index.d.ts +3 -42
- package/dist/index.js +5 -3
- package/dist/index.js.map +1 -1
- package/dist/{manifest--GHWtt5W.d.ts → manifest-Bk9LNZb9.d.ts} +163 -4
- package/dist/manifest.d.ts +1 -1
- package/dist/manifest.js +5 -1
- package/dist/{tank-2TH4D2EE.js → tank-6IEOYV4X.js} +251 -52
- package/dist/tank-6IEOYV4X.js.map +1 -0
- package/package.json +1 -1
- package/dist/chunk-BWIJDJPQ.js.map +0 -1
- package/dist/chunk-UXMO57ND.js +0 -133
- package/dist/chunk-UXMO57ND.js.map +0 -1
- package/dist/tank-2TH4D2EE.js.map +0 -1
|
@@ -107,29 +107,87 @@ function fishVariation(index, variance) {
|
|
|
107
107
|
}
|
|
108
108
|
var FISH_LENGTH = 18;
|
|
109
109
|
var FORMATION_HALF_WIDTH = 62;
|
|
110
|
-
|
|
110
|
+
var FORMATION_SHAPES = ["phalanx", "line", "ring", "wedge", "ball"];
|
|
111
|
+
function formationSlot(index, count, variance, halfWidth = FORMATION_HALF_WIDTH, shape = "phalanx") {
|
|
112
|
+
const j = 0.35 + 0.65 * Math.max(0, Math.min(1, variance));
|
|
113
|
+
if (shape === "line") {
|
|
114
|
+
const FILE = 6;
|
|
115
|
+
const files = Math.ceil(Math.max(1, count) / FILE);
|
|
116
|
+
const col2 = Math.floor(index / FILE);
|
|
117
|
+
const within = index % FILE;
|
|
118
|
+
const fileLen = Math.min(FILE, count - col2 * FILE);
|
|
119
|
+
return {
|
|
120
|
+
side: (col2 - (files - 1) / 2) * FISH_LENGTH * 1.6 + (fishHash(index, 4) - 0.5) * FISH_LENGTH * 0.4 * j,
|
|
121
|
+
up: (fishHash(index, 5) - 0.5) * FISH_LENGTH * 0.5 * j,
|
|
122
|
+
back: (within - (fileLen - 1) / 2) * FISH_LENGTH * 1.7
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
if (shape === "ring") {
|
|
126
|
+
const r = Math.max(FISH_LENGTH * 1.6, count * FISH_LENGTH * 1.35 / (Math.PI * 2));
|
|
127
|
+
const a = index / Math.max(1, count) * Math.PI * 2;
|
|
128
|
+
return {
|
|
129
|
+
side: Math.cos(a) * r,
|
|
130
|
+
up: (fishHash(index, 5) - 0.5) * FISH_LENGTH * 0.6 * j,
|
|
131
|
+
back: Math.sin(a) * r
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
if (shape === "wedge") {
|
|
135
|
+
const VSIZE = 9;
|
|
136
|
+
const layers = Math.ceil(Math.max(1, count) / VSIZE);
|
|
137
|
+
const layer = Math.floor(index / VSIZE);
|
|
138
|
+
const within = index % VSIZE;
|
|
139
|
+
const rank = within + 1 >> 1;
|
|
140
|
+
const wing = within === 0 ? 0 : within % 2 === 1 ? -1 : 1;
|
|
141
|
+
return {
|
|
142
|
+
side: wing * rank * FISH_LENGTH * 1.25 + (fishHash(index, 4) - 0.5) * FISH_LENGTH * 0.3 * j,
|
|
143
|
+
// 1.5 body lengths per layer with a small jitter: adjacent layers seat
|
|
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,
|
|
147
|
+
back: (rank - 2) * FISH_LENGTH * 1.35
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
if (shape === "ball") {
|
|
151
|
+
const r = FISH_LENGTH * (1.1 + 0.34 * Math.sqrt(count));
|
|
152
|
+
const g = (1 + Math.sqrt(5)) / 2;
|
|
153
|
+
const u = count <= 1 ? 0 : index / (count - 1);
|
|
154
|
+
const incl = Math.acos(1 - 2 * u);
|
|
155
|
+
const az = 2 * Math.PI * index / (g * g);
|
|
156
|
+
return {
|
|
157
|
+
side: r * Math.sin(incl) * Math.cos(az),
|
|
158
|
+
// Vertically squashed, hard-capped at 28: the water column is 57 units
|
|
159
|
+
// and the carrier's y-clamp needs the shell's top inside it whatever
|
|
160
|
+
// the cast size makes of the radius.
|
|
161
|
+
up: Math.cos(incl) * Math.min(r * 0.62, 28),
|
|
162
|
+
back: r * Math.sin(incl) * Math.sin(az)
|
|
163
|
+
};
|
|
164
|
+
}
|
|
111
165
|
const cols = Math.max(1, Math.ceil(Math.sqrt(Math.max(1, count))));
|
|
112
166
|
const col = index % cols;
|
|
113
167
|
const row = Math.floor(index / cols);
|
|
114
|
-
const j = 0.35 + 0.65 * Math.max(0, Math.min(1, variance));
|
|
115
168
|
const span = Math.max(1, cols - 1);
|
|
116
169
|
const pitch = Math.min(FISH_LENGTH * 1.9, halfWidth * 2 / span);
|
|
117
170
|
const jit = pitch * 0.35;
|
|
171
|
+
const rows = Math.ceil(Math.max(1, count) / cols);
|
|
118
172
|
return {
|
|
119
173
|
side: (col - (cols - 1) / 2) * pitch + (fishHash(index, 4) - 0.5) * jit * j,
|
|
120
174
|
up: (fishHash(index, 5) - 0.5) * FISH_LENGTH * j,
|
|
121
|
-
|
|
175
|
+
// Centred fore-aft like every other shape — a 24-fish lattice trailing
|
|
176
|
+
// its whole depth behind the carrier pushed the extent past what the
|
|
177
|
+
// centre-clamp could absorb.
|
|
178
|
+
back: (row - (rows - 1) / 2) * FISH_LENGTH * 1.6 + fishHash(index, 6) * FISH_LENGTH * 0.4 * j
|
|
122
179
|
};
|
|
123
180
|
}
|
|
124
|
-
function formationExtent(count, variance) {
|
|
125
|
-
let side = 0, up = 0, back = 0;
|
|
181
|
+
function formationExtent(count, variance, shape = "phalanx") {
|
|
182
|
+
let side = 0, up = 0, back = 0, reach = 0;
|
|
126
183
|
for (let i = 0; i < count; i += 1) {
|
|
127
|
-
const s = formationSlot(i, count, variance);
|
|
184
|
+
const s = formationSlot(i, count, variance, void 0, shape);
|
|
128
185
|
side = Math.max(side, Math.abs(s.side));
|
|
129
186
|
up = Math.max(up, Math.abs(s.up));
|
|
130
187
|
back = Math.max(back, Math.abs(s.back));
|
|
188
|
+
reach = Math.max(reach, Math.hypot(s.side, s.back));
|
|
131
189
|
}
|
|
132
|
-
return { side, up, back };
|
|
190
|
+
return { side, up, back, reach };
|
|
133
191
|
}
|
|
134
192
|
function bandRange(band) {
|
|
135
193
|
switch (band) {
|
|
@@ -147,6 +205,225 @@ function bandRange(band) {
|
|
|
147
205
|
}
|
|
148
206
|
}
|
|
149
207
|
|
|
208
|
+
// src/plan.ts
|
|
209
|
+
var ARC_SAMPLES = 1024;
|
|
210
|
+
function catmullRom(p0, p1, p2, p3, u) {
|
|
211
|
+
const u2 = u * u;
|
|
212
|
+
const u3 = u2 * u;
|
|
213
|
+
return 0.5 * (2 * p1 + (-p0 + p2) * u + (2 * p0 - 5 * p1 + 4 * p2 - p3) * u2 + (-p0 + 3 * p1 - 3 * p2 + p3) * u3);
|
|
214
|
+
}
|
|
215
|
+
function splineAt(points, g) {
|
|
216
|
+
const n = points.length;
|
|
217
|
+
const i = Math.floor(g) % n;
|
|
218
|
+
const u = g - Math.floor(g);
|
|
219
|
+
const p0 = points[(i - 1 + n) % n];
|
|
220
|
+
const p1 = points[i];
|
|
221
|
+
const p2 = points[(i + 1) % n];
|
|
222
|
+
const p3 = points[(i + 2) % n];
|
|
223
|
+
return [
|
|
224
|
+
catmullRom(p0[0], p1[0], p2[0], p3[0], u),
|
|
225
|
+
catmullRom(p0[1], p1[1], p2[1], p3[1], u),
|
|
226
|
+
catmullRom(p0[2], p1[2], p2[2], p3[2], u)
|
|
227
|
+
];
|
|
228
|
+
}
|
|
229
|
+
var PATH_SHAPES = ["wander", "orbit", "eight", "helix", "canyon"];
|
|
230
|
+
function shapeWaypoints(shape, rng, bounds) {
|
|
231
|
+
const points = [];
|
|
232
|
+
const ySpan = bounds.yMax - bounds.yMin;
|
|
233
|
+
if (shape === "orbit") {
|
|
234
|
+
const n2 = 14 + rng.int(0, 4);
|
|
235
|
+
const r0 = bounds.radius * rng.range(0.45, 0.8);
|
|
236
|
+
const y0 = bounds.yMin + ySpan * rng.range(0.25, 0.75);
|
|
237
|
+
const dir = rng.next() < 0.5 ? 1 : -1;
|
|
238
|
+
for (let i = 0; i < n2; i++) {
|
|
239
|
+
const a = dir * (i / n2) * Math.PI * 2;
|
|
240
|
+
const r = r0 * (1 + rng.range(-0.08, 0.08));
|
|
241
|
+
points.push([Math.cos(a) * r, y0 + ySpan * rng.range(-0.08, 0.08), Math.sin(a) * r]);
|
|
242
|
+
}
|
|
243
|
+
return points;
|
|
244
|
+
}
|
|
245
|
+
if (shape === "eight") {
|
|
246
|
+
const n2 = 20 + rng.int(0, 4);
|
|
247
|
+
const rx = bounds.radius * rng.range(0.6, 0.85);
|
|
248
|
+
const rz = bounds.radius * rng.range(0.35, 0.55);
|
|
249
|
+
const y0 = bounds.yMin + ySpan * rng.range(0.3, 0.7);
|
|
250
|
+
const phase = rng.range(0, Math.PI * 2);
|
|
251
|
+
for (let i = 0; i < n2; i++) {
|
|
252
|
+
const u = i / n2 * Math.PI * 2;
|
|
253
|
+
points.push([
|
|
254
|
+
Math.sin(u + phase) * rx,
|
|
255
|
+
y0 + Math.sin(u * 2 + phase) * ySpan * 0.12,
|
|
256
|
+
Math.sin(u * 2 + phase * 2) * rz
|
|
257
|
+
]);
|
|
258
|
+
}
|
|
259
|
+
return points;
|
|
260
|
+
}
|
|
261
|
+
if (shape === "helix") {
|
|
262
|
+
const n2 = 24;
|
|
263
|
+
const r0 = bounds.radius * rng.range(0.35, 0.6);
|
|
264
|
+
const phase = rng.range(0, Math.PI * 2);
|
|
265
|
+
for (let i = 0; i < n2; i++) {
|
|
266
|
+
const u = i / n2;
|
|
267
|
+
const a = phase + u * Math.PI * 4;
|
|
268
|
+
const climb = 1 - Math.abs(1 - 2 * u);
|
|
269
|
+
points.push([
|
|
270
|
+
Math.cos(a) * r0,
|
|
271
|
+
bounds.yMin + ySpan * (0.1 + climb * 0.8),
|
|
272
|
+
Math.sin(a) * r0
|
|
273
|
+
]);
|
|
274
|
+
}
|
|
275
|
+
return points;
|
|
276
|
+
}
|
|
277
|
+
if (shape === "canyon") {
|
|
278
|
+
const n2 = 16;
|
|
279
|
+
const axis = rng.range(0, Math.PI);
|
|
280
|
+
const ax = Math.cos(axis), az = Math.sin(axis);
|
|
281
|
+
const y0 = bounds.yMin + ySpan * rng.range(0.08, 0.28);
|
|
282
|
+
const len = bounds.radius * 0.85;
|
|
283
|
+
const width = bounds.radius * rng.range(0.12, 0.3);
|
|
284
|
+
for (let i = 0; i < n2; i++) {
|
|
285
|
+
const u = i / n2 * Math.PI * 2;
|
|
286
|
+
const along = Math.sin(u) * len;
|
|
287
|
+
const across = Math.cos(u) * width;
|
|
288
|
+
points.push([
|
|
289
|
+
ax * along - az * across,
|
|
290
|
+
y0 + ySpan * rng.range(0, 0.08),
|
|
291
|
+
az * along + ax * across
|
|
292
|
+
]);
|
|
293
|
+
}
|
|
294
|
+
return points;
|
|
295
|
+
}
|
|
296
|
+
const n = 16 + rng.int(0, 4);
|
|
297
|
+
for (let i = 0; i < n; i++) {
|
|
298
|
+
const baseAngle = i / n * Math.PI * 2;
|
|
299
|
+
const angle = baseAngle + rng.range(-0.25, 0.25);
|
|
300
|
+
const inner = i % 2 === 0;
|
|
301
|
+
const radius = inner ? bounds.radius * rng.range(0.15, 0.45) : bounds.radius * rng.range(0.55, 0.85);
|
|
302
|
+
const y = bounds.yMin + ySpan * rng.range(0.2, 0.8);
|
|
303
|
+
points.push([Math.cos(angle) * radius, y, Math.sin(angle) * radius]);
|
|
304
|
+
}
|
|
305
|
+
return points;
|
|
306
|
+
}
|
|
307
|
+
function compileSwimPlan(rng, bounds, shape = "wander") {
|
|
308
|
+
const points = shapeWaypoints(shape, rng, bounds);
|
|
309
|
+
const n = points.length;
|
|
310
|
+
const arc = new Float32Array(ARC_SAMPLES + 1);
|
|
311
|
+
let prev = splineAt(points, 0);
|
|
312
|
+
let total = 0;
|
|
313
|
+
for (let s = 1; s <= ARC_SAMPLES; s++) {
|
|
314
|
+
const g = s / ARC_SAMPLES * n;
|
|
315
|
+
const p = splineAt(points, g);
|
|
316
|
+
total += Math.hypot(p[0] - prev[0], p[1] - prev[1], p[2] - prev[2]);
|
|
317
|
+
arc[s] = total;
|
|
318
|
+
prev = p;
|
|
319
|
+
}
|
|
320
|
+
const cruise = 8 + rng.range(0, 4);
|
|
321
|
+
const wobble = [
|
|
322
|
+
{ amp: cruise * 0.18, w: 0.11 + rng.range(0, 0.05), phase: rng.range(0, Math.PI * 2) },
|
|
323
|
+
{ amp: cruise * 0.1, w: 0.043 + rng.range(0, 0.02), phase: rng.range(0, Math.PI * 2) }
|
|
324
|
+
];
|
|
325
|
+
return { points, arc, totalLength: total, cruise, wobble };
|
|
326
|
+
}
|
|
327
|
+
function distanceAt(plan, tSec, speed) {
|
|
328
|
+
let d = plan.cruise * tSec;
|
|
329
|
+
for (const h of plan.wobble) {
|
|
330
|
+
d += h.amp / h.w * (Math.cos(h.phase) - Math.cos(h.w * tSec + h.phase));
|
|
331
|
+
}
|
|
332
|
+
return d * speed;
|
|
333
|
+
}
|
|
334
|
+
function paramForDistance(plan, dist) {
|
|
335
|
+
const n = plan.points.length;
|
|
336
|
+
const d = (dist % plan.totalLength + plan.totalLength) % plan.totalLength;
|
|
337
|
+
const arc = plan.arc;
|
|
338
|
+
let lo = 0;
|
|
339
|
+
let hi = ARC_SAMPLES;
|
|
340
|
+
while (lo + 1 < hi) {
|
|
341
|
+
const mid = lo + hi >> 1;
|
|
342
|
+
if (arc[mid] <= d) lo = mid;
|
|
343
|
+
else hi = mid;
|
|
344
|
+
}
|
|
345
|
+
const seg = arc[hi] - arc[lo] || 1;
|
|
346
|
+
const frac = (d - arc[lo]) / seg;
|
|
347
|
+
return (lo + frac) / ARC_SAMPLES * n;
|
|
348
|
+
}
|
|
349
|
+
function swimPoseAtDistance(plan, dist) {
|
|
350
|
+
const g = paramForDistance(plan, dist);
|
|
351
|
+
const [x, y, z] = splineAt(plan.points, g);
|
|
352
|
+
const step = plan.points.length / ARC_SAMPLES * 8;
|
|
353
|
+
const ahead = splineAt(plan.points, g + step);
|
|
354
|
+
const behind = splineAt(plan.points, g - step + plan.points.length);
|
|
355
|
+
let fx = ahead[0] - behind[0];
|
|
356
|
+
let fy = ahead[1] - behind[1];
|
|
357
|
+
let fz = ahead[2] - behind[2];
|
|
358
|
+
const m = Math.hypot(fx, fy, fz) || 1;
|
|
359
|
+
fx /= m;
|
|
360
|
+
fy /= m;
|
|
361
|
+
fz /= m;
|
|
362
|
+
const h0 = Math.atan2(z - behind[2], x - behind[0]);
|
|
363
|
+
const h1 = Math.atan2(ahead[2] - z, ahead[0] - x);
|
|
364
|
+
let turn = h1 - h0;
|
|
365
|
+
if (turn > Math.PI) turn -= Math.PI * 2;
|
|
366
|
+
if (turn < -Math.PI) turn += Math.PI * 2;
|
|
367
|
+
const roll = Math.max(-0.35, Math.min(0.35, turn * 3.5));
|
|
368
|
+
return { x, y, z, fx, fy, fz, roll, dist };
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// src/maneuver.ts
|
|
372
|
+
var MANEUVERS = ["none", "dart", "startle", "graze", "curious", "zoomies"];
|
|
373
|
+
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
|
+
graze: { name: "graze", dur: 5.5, interval: 16, advance: -3.2, kick: 0.6, lift: -0.7, flurry: 0.3, bursts: 1 },
|
|
377
|
+
curious: { name: "curious", dur: 4, interval: 15, advance: 0.8, kick: 2, lift: 0.2, flurry: 0.5, bursts: 1 },
|
|
378
|
+
zoomies: { name: "zoomies", dur: 3.2, interval: 20, advance: 7.5, kick: 0.9, lift: 0.15, flurry: 1.5, bursts: 3 }
|
|
379
|
+
};
|
|
380
|
+
function maneuverSpecOf(name) {
|
|
381
|
+
return Object.hasOwn(SPECS, name) ? SPECS[name] : null;
|
|
382
|
+
}
|
|
383
|
+
var smooth = (u) => u <= 0 ? 0 : u >= 1 ? 1 : u * u * (3 - 2 * u);
|
|
384
|
+
var bump = (u) => {
|
|
385
|
+
if (u <= 0 || u >= 1) return 0;
|
|
386
|
+
const b = 4 * u * (1 - u);
|
|
387
|
+
return b * b;
|
|
388
|
+
};
|
|
389
|
+
function surges(u, bursts) {
|
|
390
|
+
if (u <= 0) return 0;
|
|
391
|
+
if (u >= 1) return 1;
|
|
392
|
+
const x = u * bursts;
|
|
393
|
+
return (Math.floor(x) + smooth(x - Math.floor(x))) / bursts;
|
|
394
|
+
}
|
|
395
|
+
var IDLE = { along: 0, alongBump: 0, side: 0, up: 0, flurry: 0 };
|
|
396
|
+
function maneuverAt(spec, index, tSec, rate, intensity) {
|
|
397
|
+
if (!spec || rate <= 0 || intensity <= 0 || tSec < 0) return IDLE;
|
|
398
|
+
const interval = spec.interval * (3 - 2 * Math.min(1, rate));
|
|
399
|
+
const k0 = Math.floor(tSec / interval);
|
|
400
|
+
let along = 0;
|
|
401
|
+
let side = 0;
|
|
402
|
+
let up = 0;
|
|
403
|
+
let flurry = 0;
|
|
404
|
+
let alongBump = 0;
|
|
405
|
+
const done = Math.max(0, k0 - 1);
|
|
406
|
+
along += done * spec.advance * intensity;
|
|
407
|
+
for (const k of [k0 - 1, k0, k0 + 1]) {
|
|
408
|
+
if (k < 0) continue;
|
|
409
|
+
const start = (k + fishHash(index, 11 + k)) * interval;
|
|
410
|
+
const u = (tSec - start) / spec.dur;
|
|
411
|
+
if (u <= 0) continue;
|
|
412
|
+
if (u >= 1) {
|
|
413
|
+
if (k >= done) along += spec.advance * intensity;
|
|
414
|
+
continue;
|
|
415
|
+
}
|
|
416
|
+
const sign = fishHash(index, 23 + k) < 0.5 ? -1 : 1;
|
|
417
|
+
const env = bump(u) * intensity;
|
|
418
|
+
along += spec.advance * surges(u, spec.bursts) * intensity;
|
|
419
|
+
alongBump += spec.advance * bump(u) * intensity;
|
|
420
|
+
side += sign * spec.kick * env;
|
|
421
|
+
up += spec.lift * spec.kick * env;
|
|
422
|
+
flurry += spec.flurry * env;
|
|
423
|
+
}
|
|
424
|
+
return { along, alongBump, side, up, flurry };
|
|
425
|
+
}
|
|
426
|
+
|
|
150
427
|
// src/asset-cids.ts
|
|
151
428
|
var ASSET_CIDS = [
|
|
152
429
|
"QmZCzKVLJ1MYRzaZEFyuMCVmrPR3urQYMFR9RWerJYpCEr",
|
|
@@ -702,14 +979,19 @@ var BREEDS = [
|
|
|
702
979
|
metadataCid: "QmZTntNpvwxe8Mii7s1XTpTrbaci4ZgYUXs9sEbsBodtaF",
|
|
703
980
|
model: "sea-turtle"
|
|
704
981
|
},
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
{ breed: "
|
|
982
|
+
// Unminted: `model` names the CURRENT basename in the original aquarium's
|
|
983
|
+
// NPC set (versioned there; these are the ones its entity classes load,
|
|
984
|
+
// except glow-fish, where the app loads the 503 KB glow-fish5 while an
|
|
985
|
+
// identical-material 30 KB Draco glow-fish4 sits beside it — we take the
|
|
986
|
+
// small one). All are clip-less and unrigged, so they swim on `bodyWiggle`.
|
|
987
|
+
{ breed: "blowfish", minted: false, count: 0, model: "blow-fish4" },
|
|
988
|
+
{ breed: "hackerfish", minted: false, count: 0, model: "hacker-fish3" },
|
|
989
|
+
{ breed: "glowfish", minted: false, count: 0, model: "glow-fish4" },
|
|
990
|
+
{ breed: "babyfish", minted: false, count: 0, model: "baby-fish2" },
|
|
991
|
+
{ breed: "shark", minted: false, count: 0, model: "shark3" },
|
|
992
|
+
{ breed: "crab", minted: false, count: 0, model: "crab3" },
|
|
711
993
|
{ breed: "jellyfish", minted: false, count: 0, model: "jellyfish" },
|
|
712
|
-
{ breed: "dori", minted: false, count: 0, model: "
|
|
994
|
+
{ breed: "dori", minted: false, count: 0, model: "dori3" }
|
|
713
995
|
];
|
|
714
996
|
function breedOf(id) {
|
|
715
997
|
if (!isMintedId(id)) return null;
|
|
@@ -800,6 +1082,16 @@ var FISH_CATALOG = [
|
|
|
800
1082
|
{ id: 497, name: "Fish 497", breed: "seaturtle", ipfs3d: "ipfs://QmTBNvoUiwPw9HSUmy1qKCWPBKkBRAensgooYVqMmsviaE/fish_497_of_the_metaquarium_3d.glb", localGlb: "" }
|
|
801
1083
|
];
|
|
802
1084
|
var DEFAULT_FISH = FISH_CATALOG[0];
|
|
1085
|
+
var NPC_CATALOG = [
|
|
1086
|
+
{ id: 601, name: "Blowfish", breed: "blowfish", ipfs3d: "", localGlb: "/assets/metaquarium/npc-blowfish.glb" },
|
|
1087
|
+
{ id: 602, name: "Hackerfish", breed: "hackerfish", ipfs3d: "", localGlb: "/assets/metaquarium/npc-hackerfish.glb" },
|
|
1088
|
+
{ id: 603, name: "Glowfish", breed: "glowfish", ipfs3d: "", localGlb: "/assets/metaquarium/npc-glowfish.glb" },
|
|
1089
|
+
{ id: 604, name: "Babyfish", breed: "babyfish", ipfs3d: "", localGlb: "/assets/metaquarium/npc-babyfish.glb" },
|
|
1090
|
+
{ id: 605, name: "Shark", breed: "shark", ipfs3d: "", localGlb: "/assets/metaquarium/shark3.glb" },
|
|
1091
|
+
{ id: 606, name: "Crab", breed: "crab", ipfs3d: "", localGlb: "/assets/metaquarium/npc-crab.glb" },
|
|
1092
|
+
{ id: 607, name: "Jellyfish", breed: "jellyfish", ipfs3d: "", localGlb: "/assets/metaquarium/npc-jellyfish.glb" },
|
|
1093
|
+
{ id: 608, name: "Dori", breed: "dori", ipfs3d: "", localGlb: "/assets/metaquarium/npc-dori.glb" }
|
|
1094
|
+
];
|
|
803
1095
|
function parseFishMix(mix, catalog = FISH_CATALOG) {
|
|
804
1096
|
const entries = [];
|
|
805
1097
|
const problems = [];
|
|
@@ -815,26 +1107,37 @@ function parseFishMix(mix, catalog = FISH_CATALOG) {
|
|
|
815
1107
|
let fish = /^\d+$/.test(key) ? catalog.find((f) => f.id === Number(key)) : catalog.find((f) => f.breed.toLowerCase() === key);
|
|
816
1108
|
if (!fish && catalog === FISH_CATALOG && /^\d+$/.test(key)) {
|
|
817
1109
|
const n = Number(key);
|
|
818
|
-
const
|
|
1110
|
+
const url2 = fishAsset(n, "3d");
|
|
819
1111
|
const breed = breedOf(n);
|
|
820
|
-
if (
|
|
1112
|
+
if (url2 && breed) fish = { id: n, name: `Fish ${n}`, breed, ipfs3d: url2, localGlb: "" };
|
|
821
1113
|
}
|
|
822
1114
|
if (!fish) {
|
|
1115
|
+
const breeds = [...new Set(catalog.map((f) => f.breed))].join(", ");
|
|
823
1116
|
problems.push(
|
|
824
|
-
/^\d+$/.test(key) ? catalog === FISH_CATALOG ? `"${key}": not a minted token id (1-${TOTAL_SUPPLY})` : `"${key}": not a catalog id` : `"${key}": not a breed (${
|
|
1117
|
+
/^\d+$/.test(key) ? catalog === FISH_CATALOG ? `"${key}": not a minted token id (1-${TOTAL_SUPPLY})` : `"${key}": not a catalog id` : `"${key}": not a breed (${breeds})`
|
|
825
1118
|
);
|
|
826
1119
|
continue;
|
|
827
1120
|
}
|
|
1121
|
+
const url = fish.ipfs3d;
|
|
1122
|
+
if (!url) {
|
|
1123
|
+
problems.push(`"${key}": model not hosted here (catalog carries no URL for it)`);
|
|
1124
|
+
continue;
|
|
1125
|
+
}
|
|
828
1126
|
let count = 1;
|
|
829
1127
|
if (countRaw !== void 0) {
|
|
830
1128
|
const n = Number(countRaw.trim());
|
|
831
|
-
if (!Number.isInteger(n) || n < 1
|
|
1129
|
+
if (!Number.isInteger(n) || n < 1) {
|
|
832
1130
|
problems.push(`"${token}": count must be an integer 1-24`);
|
|
833
1131
|
continue;
|
|
834
1132
|
}
|
|
835
|
-
|
|
1133
|
+
if (n > 24) {
|
|
1134
|
+
problems.push(`"${token}": count clamped to 24`);
|
|
1135
|
+
count = 24;
|
|
1136
|
+
} else {
|
|
1137
|
+
count = n;
|
|
1138
|
+
}
|
|
836
1139
|
}
|
|
837
|
-
entries.push({ id: fish.id, url
|
|
1140
|
+
entries.push({ id: fish.id, url, count });
|
|
838
1141
|
}
|
|
839
1142
|
return { entries, problems };
|
|
840
1143
|
}
|
|
@@ -916,10 +1219,30 @@ var METAQUARIUM_PARAMS = {
|
|
|
916
1219
|
* default changes nothing. A small named set on purpose: a silhouette of
|
|
917
1220
|
* movement you can name is one you can choose from. */
|
|
918
1221
|
swimStyle: { type: "enum", default: "loop", options: [...SWIM_STYLE_NAMES], ease: "step" },
|
|
1222
|
+
/** The shape a fish's loop is drawn on. `wander` is the original roaming
|
|
1223
|
+
* itinerary, so the default changes nothing. `orbit` laps, `eight` crosses
|
|
1224
|
+
* the middle, `helix` tours the water column, `canyon` sweeps low — same
|
|
1225
|
+
* spline engine, different itinerary. */
|
|
1226
|
+
pathShape: { type: "enum", default: "wander", options: [...PATH_SHAPES], ease: "step" },
|
|
1227
|
+
/** How a `school` holds together: the original lattice, single file, a
|
|
1228
|
+
* carousel ring, the migratory V, or a bait-ball. Ignored by every
|
|
1229
|
+
* non-formation style; the same no-pair-inside-a-body-length law holds
|
|
1230
|
+
* for all of them. */
|
|
1231
|
+
formationShape: { type: "enum", default: "phalanx", options: [...FORMATION_SHAPES], ease: "step" },
|
|
919
1232
|
/** Per-fish spread: 0 a uniform shoal, 1 every fish visibly its own animal
|
|
920
1233
|
* (±40% speed, ±25% size, own phase). The uniqueness dial — one number
|
|
921
1234
|
* instead of per-fish values nobody wants to author. */
|
|
922
1235
|
swimVariance: { type: "number", default: 0, min: 0, max: 1, ease: "smooth" },
|
|
1236
|
+
/** Named event layered over the swim style — the thing that turns uniform
|
|
1237
|
+
* cruising into behaviour a viewer recognizes. Each fish runs its own
|
|
1238
|
+
* seeded schedule of the chosen event; displacement-based, so frames stay
|
|
1239
|
+
* addressable. `none` (default) changes nothing. */
|
|
1240
|
+
maneuver: { type: "enum", default: "none", options: [...MANEUVERS], ease: "step" },
|
|
1241
|
+
/** How often events fire: 0 never, 1 the maneuver's own tempo (roughly
|
|
1242
|
+
* every 14-20s per fish, desynchronised). */
|
|
1243
|
+
maneuverRate: { type: "number", default: 0.5, min: 0, max: 1, ease: "smooth" },
|
|
1244
|
+
/** How hard: scales the surge, the kick, and the tail flurry together. */
|
|
1245
|
+
maneuverIntensity: { type: "number", default: 0.7, min: 0, max: 1, ease: "smooth" },
|
|
923
1246
|
/** Procedural body yaw for models that carry NO animation clip — most of
|
|
924
1247
|
* the breed library. Distance-driven like the tail beat, so it speeds up
|
|
925
1248
|
* with the fish and stays frame-addressable. Clipped models ignore it:
|
|
@@ -1033,9 +1356,16 @@ export {
|
|
|
1033
1356
|
fishHash,
|
|
1034
1357
|
fishVariation,
|
|
1035
1358
|
FISH_LENGTH,
|
|
1359
|
+
FORMATION_SHAPES,
|
|
1036
1360
|
formationSlot,
|
|
1037
1361
|
formationExtent,
|
|
1038
1362
|
bandRange,
|
|
1363
|
+
PATH_SHAPES,
|
|
1364
|
+
compileSwimPlan,
|
|
1365
|
+
distanceAt,
|
|
1366
|
+
swimPoseAtDistance,
|
|
1367
|
+
maneuverSpecOf,
|
|
1368
|
+
maneuverAt,
|
|
1039
1369
|
ASSET_CIDS,
|
|
1040
1370
|
TOTAL_SUPPLY,
|
|
1041
1371
|
isMintedId,
|
|
@@ -1053,6 +1383,7 @@ export {
|
|
|
1053
1383
|
resolveIpfsUrls,
|
|
1054
1384
|
FISH_CATALOG,
|
|
1055
1385
|
DEFAULT_FISH,
|
|
1386
|
+
NPC_CATALOG,
|
|
1056
1387
|
parseFishMix,
|
|
1057
1388
|
expandFishMix,
|
|
1058
1389
|
METAQUARIUM_PARAMS,
|
|
@@ -1063,4 +1394,4 @@ export {
|
|
|
1063
1394
|
paramSpaceWith,
|
|
1064
1395
|
coerceNum
|
|
1065
1396
|
};
|
|
1066
|
-
//# sourceMappingURL=chunk-
|
|
1397
|
+
//# sourceMappingURL=chunk-23P73MYY.js.map
|