@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.
Files changed (2) hide show
  1. package/extensions/pompom.ts +104 -65
  2. package/package.json +1 -1
@@ -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
- if (hitObj.id === "body") {
280
- let bdx = px - hitObj.x, bdy = py - hitObj.y;
281
- if (isFlipping) {
282
- const s = Math.sin(-flipPhase), c = Math.cos(-flipPhase);
283
- const nx = bdx * c - bdy * s, ny = bdx * s + bdy * c;
284
- bdx = nx; bdy = ny;
285
- }
286
- // Blush
287
- const blx1 = bdx + 0.16, bly1 = bdy - 0.04;
288
- const blx2 = bdx - 0.16, bly2 = bdy - 0.04;
289
- const blush = Math.exp(-(blx1 * blx1 + bly1 * bly1) * 80) + Math.exp(-(blx2 * blx2 + bly2 * bly2) * 80);
290
- if (!isSleeping) {
291
- r = r * (1 - blush) + 255 * blush; g = g * (1 - blush) + 80 * blush; b = b * (1 - blush) + 100 * blush;
292
- }
293
- // Eyes
294
- const isTired = (energy < 20 || hunger < 30) && !isSleeping;
295
- const eyeOpen = isSleeping ? 0.05 : (isTired ? 0.4 : 1.0) - blinkFade;
296
- const ex1 = bdx - lookX * 0.08 + 0.1, ey1 = bdy - lookY * 0.05 + 0.02;
297
- const ex2 = bdx - lookX * 0.08 - 0.1, ey2 = bdy - lookY * 0.05 + 0.02;
298
-
299
- if (isSleeping || currentState === "singing") {
300
- if (isSleeping) {
301
- if ((Math.abs(ey1) < 0.01 && Math.abs(ex1) < 0.05) || (Math.abs(ey2) < 0.01 && Math.abs(ex2) < 0.05)) { r = 30; g = 20; b = 30; }
302
- } else {
303
- if ((Math.abs(ey1 + ex1 * ex1 * 15) < 0.015 && Math.abs(ex1) < 0.06 && ey1 > -ex1 * ex1 * 15) ||
304
- (Math.abs(ey2 + ex2 * ex2 * 15) < 0.015 && Math.abs(ex2) < 0.06 && ey2 > -ex2 * ex2 * 15)) { r = 30; g = 20; b = 30; }
305
- }
306
- } else {
307
- const eDist1 = ex1 * ex1 + (ey1 * ey1) / (eyeOpen * eyeOpen + 0.001);
308
- const eDist2 = ex2 * ex2 + (ey2 * ey2) / (eyeOpen * eyeOpen + 0.001);
309
- if (eDist1 < 0.004 || eDist2 < 0.004) {
310
- r = 15; g = 10; b = 20;
311
- if (ey1 > 0 || ey2 > 0) { r = 50; g = 180; b = 100; }
312
- if ((ex1 + 0.012) ** 2 + (ey1 + 0.012) ** 2 < 0.0008 || (ex2 + 0.012) ** 2 + (ey2 + 0.012) ** 2 < 0.0008) {
313
- if (!isTired) { r = 255; g = 255; b = 255; }
314
- } else if ((ex1 - 0.015) ** 2 + (ey1 - 0.015) ** 2 < 0.0005 || (ex2 - 0.015) ** 2 + (ey2 - 0.015) ** 2 < 0.0005) {
315
- if (!isTired) { r = 255; g = 255; b = 255; }
316
- }
317
- }
318
- }
319
- // Nose
320
- const nnx = bdx - lookX * 0.06, nny = bdy - lookY * 0.05 - 0.02;
321
- if (nnx * nnx * 1.5 + nny * nny < 0.0006 && !isSleeping) { r = 30; g = 20; b = 30; }
322
- // Mouth
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
- if (hitObj.id === "earL" || hitObj.id === "earR") {
341
- if (hitU > -0.3 && hitU < 0.3 && hitV > -0.5 && hitV < 0.5) { r = 255; g = 130; b = 160; }
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
- if (hitNz < 0.25) {
345
- r *= 0.6; g *= 0.6; b *= 0.6;
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; }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codexstar/pi-pompom",
3
- "version": "1.4.0",
3
+ "version": "1.5.0",
4
4
  "description": "A 3D raymarched virtual pet companion for Pi CLI",
5
5
  "type": "module",
6
6
  "author": "codexstar69 <engazedigital@gmail.com>",