@codexstar/pi-pompom 1.4.0 → 1.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/extensions/pompom.ts +104 -65
- package/package.json +1 -1
package/extensions/pompom.ts
CHANGED
|
@@ -276,74 +276,113 @@ function shadeObject(hit: ReturnType<typeof getObjHit>, px: number, py: number,
|
|
|
276
276
|
const spot = Math.sin(hitNx * 10) * Math.cos(hitNy * 8);
|
|
277
277
|
if (spot > 0.6) { r = Math.max(0, r - 40); g = Math.max(0, g - 20); b = Math.max(0, b - 10); }
|
|
278
278
|
}
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
if (!isSleeping && !hasBall) {
|
|
324
|
-
const mx = bdx - lookX * 0.06, my = bdy - lookY * 0.05 - 0.06;
|
|
325
|
-
if ((Math.abs(my - (mx - 0.025) ** 2 * 20 + 0.01) < 0.01 && mx > 0 && mx < 0.05) ||
|
|
326
|
-
(Math.abs(my - (mx + 0.025) ** 2 * 20 + 0.01) < 0.01 && mx < 0 && mx > -0.05)) {
|
|
327
|
-
r = 50; g = 30; b = 40;
|
|
328
|
-
}
|
|
329
|
-
if (currentState === "excited" || currentState === "singing" || speechTimer > 0 || isTalking) {
|
|
330
|
-
const mouthOpen = (speechTimer > 0 || currentState === "singing" || isTalking)
|
|
331
|
-
? (isTalking ? talkAudioLevel * 0.04 + 0.005 : Math.abs(Math.sin(time * 12)) * 0.025)
|
|
332
|
-
: 0.015;
|
|
333
|
-
if (mx * mx + (my + 0.01) ** 2 < mouthOpen && my < -0.01) {
|
|
334
|
-
r = 240; g = 80; b = 100;
|
|
335
|
-
if (my < -0.025) { r = 255; g = 120; b = 140; }
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
}
|
|
279
|
+
let isOnFace = false;
|
|
280
|
+
if (hitObj.id === "body") {
|
|
281
|
+
let bdx = px - hitObj.x, bdy = py - hitObj.y;
|
|
282
|
+
if (isFlipping) {
|
|
283
|
+
const s = Math.sin(-flipPhase), c = Math.cos(-flipPhase);
|
|
284
|
+
const nx = bdx * c - bdy * s, ny = bdx * s + bdy * c;
|
|
285
|
+
bdx = nx; bdy = ny;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// ── Face plate: bright cream area so features pop ──
|
|
289
|
+
const faceR = Math.sqrt(bdx * bdx + bdy * bdy);
|
|
290
|
+
if (faceR < 0.22) {
|
|
291
|
+
isOnFace = true;
|
|
292
|
+
const faceMix = Math.max(0, 1.0 - faceR / 0.22);
|
|
293
|
+
r = Math.floor(r * (1 - faceMix * 0.6) + 255 * faceMix * 0.6);
|
|
294
|
+
g = Math.floor(g * (1 - faceMix * 0.6) + 252 * faceMix * 0.6);
|
|
295
|
+
b = Math.floor(b * (1 - faceMix * 0.6) + 248 * faceMix * 0.6);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// ── Blush: big rosy cheeks ──
|
|
299
|
+
const blx1 = bdx + 0.15, bly1 = bdy - 0.05;
|
|
300
|
+
const blx2 = bdx - 0.15, bly2 = bdy - 0.05;
|
|
301
|
+
const blush = Math.exp(-(blx1 * blx1 + bly1 * bly1) * 40) + Math.exp(-(blx2 * blx2 + bly2 * bly2) * 40);
|
|
302
|
+
if (!isSleeping) {
|
|
303
|
+
r = Math.floor(r * (1 - blush) + 255 * blush);
|
|
304
|
+
g = Math.floor(g * (1 - blush) + 70 * blush);
|
|
305
|
+
b = Math.floor(b * (1 - blush) + 90 * blush);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// ── Eyes: kawaii style — white sclera → colored iris → dark pupil → highlight ──
|
|
309
|
+
const isTired = (energy < 20 || hunger < 30) && !isSleeping;
|
|
310
|
+
const eyeOpen = isSleeping ? 0.05 : (isTired ? 0.4 : 1.0) - blinkFade;
|
|
311
|
+
const ex1 = bdx - lookX * 0.08 + 0.11, ey1 = bdy - lookY * 0.05 + 0.02;
|
|
312
|
+
const ex2 = bdx - lookX * 0.08 - 0.11, ey2 = bdy - lookY * 0.05 + 0.02;
|
|
313
|
+
|
|
314
|
+
if (isSleeping || currentState === "singing") {
|
|
315
|
+
// Closed eyes — horizontal lines
|
|
316
|
+
if (isSleeping) {
|
|
317
|
+
if ((Math.abs(ey1) < 0.012 && Math.abs(ex1) < 0.06) || (Math.abs(ey2) < 0.012 && Math.abs(ex2) < 0.06)) { r = 40; g = 30; b = 50; }
|
|
318
|
+
} else {
|
|
319
|
+
// Happy squint arcs
|
|
320
|
+
if ((Math.abs(ey1 + ex1 * ex1 * 12) < 0.018 && Math.abs(ex1) < 0.07 && ey1 > -ex1 * ex1 * 12) ||
|
|
321
|
+
(Math.abs(ey2 + ex2 * ex2 * 12) < 0.018 && Math.abs(ex2) < 0.07 && ey2 > -ex2 * ex2 * 12)) { r = 40; g = 30; b = 50; }
|
|
322
|
+
}
|
|
339
323
|
} else {
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
324
|
+
const eDist1 = ex1 * ex1 + (ey1 * ey1) / (eyeOpen * eyeOpen + 0.001);
|
|
325
|
+
const eDist2 = ex2 * ex2 + (ey2 * ey2) / (eyeOpen * eyeOpen + 0.001);
|
|
326
|
+
|
|
327
|
+
// Layer 1: White sclera (outermost)
|
|
328
|
+
if (eDist1 < 0.009 || eDist2 < 0.009) {
|
|
329
|
+
r = 250; g = 250; b = 255;
|
|
330
|
+
|
|
331
|
+
// Layer 2: Colored iris
|
|
332
|
+
if (eDist1 < 0.005 || eDist2 < 0.005) {
|
|
333
|
+
r = 40; g = 130; b = 90; // teal-green iris
|
|
334
|
+
// Lower iris lighter
|
|
335
|
+
if (ey1 > 0.01 || ey2 > 0.01) { r = 60; g = 170; b = 110; }
|
|
336
|
+
|
|
337
|
+
// Layer 3: Dark pupil
|
|
338
|
+
if (eDist1 < 0.002 || eDist2 < 0.002) {
|
|
339
|
+
r = 10; g = 10; b = 15;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// Big highlight (upper-left) — spans ~2 chars
|
|
344
|
+
if ((ex1 + 0.02) ** 2 + (ey1 + 0.02) ** 2 < 0.0015 || (ex2 + 0.02) ** 2 + (ey2 + 0.02) ** 2 < 0.0015) {
|
|
345
|
+
if (!isTired) { r = 255; g = 255; b = 255; }
|
|
346
|
+
}
|
|
347
|
+
// Small secondary highlight (lower-right)
|
|
348
|
+
if ((ex1 - 0.02) ** 2 + (ey1 - 0.02) ** 2 < 0.0006 || (ex2 - 0.02) ** 2 + (ey2 - 0.02) ** 2 < 0.0006) {
|
|
349
|
+
if (!isTired) { r = 230; g = 240; b = 255; }
|
|
350
|
+
}
|
|
351
|
+
}
|
|
343
352
|
}
|
|
344
|
-
|
|
345
|
-
|
|
353
|
+
|
|
354
|
+
// ── Nose: small dark oval ──
|
|
355
|
+
const nnx = bdx - lookX * 0.06, nny = bdy - lookY * 0.05 - 0.03;
|
|
356
|
+
if (nnx * nnx * 1.2 + nny * nny < 0.001 && !isSleeping) { r = 40; g = 30; b = 40; }
|
|
357
|
+
|
|
358
|
+
// ── Mouth: clear smile arc ──
|
|
359
|
+
if (!isSleeping && !hasBall) {
|
|
360
|
+
const mx = bdx - lookX * 0.06, my = bdy - lookY * 0.05 - 0.07;
|
|
361
|
+
// Smile curves
|
|
362
|
+
if ((Math.abs(my - (mx - 0.03) ** 2 * 15 + 0.012) < 0.013 && mx > 0 && mx < 0.06) ||
|
|
363
|
+
(Math.abs(my - (mx + 0.03) ** 2 * 15 + 0.012) < 0.013 && mx < 0 && mx > -0.06)) {
|
|
364
|
+
r = 50; g = 30; b = 40;
|
|
365
|
+
}
|
|
366
|
+
// Open mouth when excited/talking
|
|
367
|
+
if (currentState === "excited" || currentState === "singing" || currentState === "dance" || speechTimer > 0 || isTalking) {
|
|
368
|
+
const mouthOpen = (speechTimer > 0 || currentState === "singing" || isTalking)
|
|
369
|
+
? (isTalking ? talkAudioLevel * 0.04 + 0.008 : Math.abs(Math.sin(time * 12)) * 0.03)
|
|
370
|
+
: 0.02;
|
|
371
|
+
if (mx * mx + (my + 0.012) ** 2 < mouthOpen && my < -0.01) {
|
|
372
|
+
r = 230; g = 70; b = 90;
|
|
373
|
+
if (my < -0.03) { r = 255; g = 110; b = 130; }
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
} else {
|
|
378
|
+
if (hitObj.id === "earL" || hitObj.id === "earR") {
|
|
379
|
+
if (hitU > -0.3 && hitU < 0.3 && hitV > -0.5 && hitV < 0.5) { r = 255; g = 130; b = 160; }
|
|
346
380
|
}
|
|
381
|
+
}
|
|
382
|
+
// Dark outline — but NOT on the face area (preserves feature contrast)
|
|
383
|
+
if (hitNz < 0.25 && !isOnFace) {
|
|
384
|
+
r = Math.floor(r * 0.6); g = Math.floor(g * 0.6); b = Math.floor(b * 0.6);
|
|
385
|
+
}
|
|
347
386
|
} else if (hitObj.mat === 2) {
|
|
348
387
|
r = Math.max(0, th.r - 20); g = Math.max(0, th.g - 15); b = Math.max(0, th.b - 10);
|
|
349
388
|
if (hitNy > 0.5) { r = 255; g = 180; b = 190; }
|