@codexstar/pi-pompom 1.2.0 → 1.4.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 +129 -24
- package/package.json +1 -1
package/extensions/pompom.ts
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
// Widget dimensions — set once, used by renderPompom
|
|
10
10
|
let W = 50;
|
|
11
11
|
let H = 14; // character rows (each = 2 logical pixels via half-block)
|
|
12
|
-
const VIEW_OFFSET_Y = 0.
|
|
12
|
+
const VIEW_OFFSET_Y = 0.2; // shift camera down so ground is visible in compact mode
|
|
13
13
|
|
|
14
14
|
const PHYSICS_DT = 0.016; // 60fps physics sub-stepping
|
|
15
15
|
|
|
@@ -91,7 +91,7 @@ function say(text: string, duration = 4.0) {
|
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
function project2D(x: number, y: number): [number, number] {
|
|
94
|
-
const effectDim = Math.max(40, Math.min(W, H *
|
|
94
|
+
const effectDim = Math.max(40, Math.min(W, H * 4));
|
|
95
95
|
const scale = 2.0 / effectDim;
|
|
96
96
|
const cx = (x / scale) + (W / 2.0);
|
|
97
97
|
const cy = ((y - VIEW_OFFSET_Y) / scale + H) / 2.0;
|
|
@@ -145,13 +145,67 @@ function fbm(x: number, y: number): number {
|
|
|
145
145
|
Math.sin(x * 30 - time) * Math.cos(y * 30) * 0.02;
|
|
146
146
|
}
|
|
147
147
|
|
|
148
|
-
|
|
148
|
+
type Weather = "clear" | "cloudy" | "rain" | "snow" | "storm";
|
|
149
|
+
type TimeOfDay = "dawn" | "morning" | "day" | "sunset" | "dusk" | "night";
|
|
150
|
+
|
|
151
|
+
function getTimeOfDay(): TimeOfDay {
|
|
149
152
|
const h = new Date().getHours();
|
|
153
|
+
if (h >= 5 && h < 7) return "dawn";
|
|
154
|
+
if (h >= 7 && h < 10) return "morning";
|
|
155
|
+
if (h >= 10 && h < 16) return "day";
|
|
156
|
+
if (h >= 16 && h < 18) return "sunset";
|
|
157
|
+
if (h >= 18 && h < 20) return "dusk";
|
|
158
|
+
return "night";
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function getWeather(): Weather {
|
|
162
|
+
// Cycle weather based on minute within each hour for variety
|
|
163
|
+
const m = new Date().getMinutes();
|
|
164
|
+
if (m < 15) return "clear";
|
|
165
|
+
if (m < 25) return "cloudy";
|
|
166
|
+
if (m < 35) return "rain";
|
|
167
|
+
if (m < 40) return "storm";
|
|
168
|
+
if (m < 45) return "snow";
|
|
169
|
+
if (m < 55) return "cloudy";
|
|
170
|
+
return "clear";
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function getWeatherAndTime() {
|
|
174
|
+
const tod = getTimeOfDay();
|
|
175
|
+
const weather = getWeather();
|
|
150
176
|
let rTop = 0, gTop = 0, bTop = 0, rBot = 0, gBot = 0, bBot = 0;
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
177
|
+
|
|
178
|
+
// Sky gradients per time of day
|
|
179
|
+
if (tod === "dawn") {
|
|
180
|
+
rTop = 40; gTop = 30; bTop = 80; rBot = 255; gBot = 140; bBot = 80;
|
|
181
|
+
} else if (tod === "morning") {
|
|
182
|
+
rTop = 60; gTop = 140; bTop = 255; rBot = 200; gBot = 220; bBot = 255;
|
|
183
|
+
} else if (tod === "day") {
|
|
184
|
+
rTop = 40; gTop = 120; bTop = 255; rBot = 180; gBot = 220; bBot = 255;
|
|
185
|
+
} else if (tod === "sunset") {
|
|
186
|
+
rTop = 140; gTop = 50; bTop = 120; rBot = 255; gBot = 120; bBot = 60;
|
|
187
|
+
} else if (tod === "dusk") {
|
|
188
|
+
rTop = 50; gTop = 20; bTop = 80; rBot = 120; gBot = 60; bBot = 80;
|
|
189
|
+
} else { // night
|
|
190
|
+
rTop = 5; gTop = 5; bTop = 15; rBot = 15; gBot = 10; bBot = 30;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Weather tinting — overcast dims the sky, storm darkens further
|
|
194
|
+
if (weather === "cloudy") {
|
|
195
|
+
rTop = Math.floor(rTop * 0.7 + 40); gTop = Math.floor(gTop * 0.7 + 40); bTop = Math.floor(bTop * 0.7 + 40);
|
|
196
|
+
rBot = Math.floor(rBot * 0.7 + 40); gBot = Math.floor(gBot * 0.7 + 40); bBot = Math.floor(bBot * 0.7 + 40);
|
|
197
|
+
} else if (weather === "rain") {
|
|
198
|
+
rTop = Math.floor(rTop * 0.5 + 30); gTop = Math.floor(gTop * 0.5 + 30); bTop = Math.floor(bTop * 0.5 + 40);
|
|
199
|
+
rBot = Math.floor(rBot * 0.5 + 30); gBot = Math.floor(gBot * 0.5 + 30); bBot = Math.floor(bBot * 0.5 + 40);
|
|
200
|
+
} else if (weather === "storm") {
|
|
201
|
+
rTop = Math.floor(rTop * 0.3 + 15); gTop = Math.floor(gTop * 0.3 + 15); bTop = Math.floor(bTop * 0.3 + 20);
|
|
202
|
+
rBot = Math.floor(rBot * 0.3 + 20); gBot = Math.floor(gBot * 0.3 + 20); bBot = Math.floor(bBot * 0.3 + 25);
|
|
203
|
+
} else if (weather === "snow") {
|
|
204
|
+
rTop = Math.floor(rTop * 0.6 + 60); gTop = Math.floor(gTop * 0.6 + 60); bTop = Math.floor(bTop * 0.6 + 70);
|
|
205
|
+
rBot = Math.floor(rBot * 0.6 + 60); gBot = Math.floor(gBot * 0.6 + 60); bBot = Math.floor(bBot * 0.6 + 70);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return { rTop, gTop, bTop, rBot, gBot, bBot, isNight: tod === "night" || tod === "dusk", weather, timeOfDay: tod };
|
|
155
209
|
}
|
|
156
210
|
|
|
157
211
|
function getObjHit(px: number, py: number, objects: RenderObj[]) {
|
|
@@ -255,9 +309,9 @@ function shadeObject(hit: ReturnType<typeof getObjHit>, px: number, py: number,
|
|
|
255
309
|
if (eDist1 < 0.004 || eDist2 < 0.004) {
|
|
256
310
|
r = 15; g = 10; b = 20;
|
|
257
311
|
if (ey1 > 0 || ey2 > 0) { r = 50; g = 180; b = 100; }
|
|
258
|
-
if ((ex1 + 0.012) ** 2 + (ey1 + 0.012) ** 2 < 0.
|
|
312
|
+
if ((ex1 + 0.012) ** 2 + (ey1 + 0.012) ** 2 < 0.0008 || (ex2 + 0.012) ** 2 + (ey2 + 0.012) ** 2 < 0.0008) {
|
|
259
313
|
if (!isTired) { r = 255; g = 255; b = 255; }
|
|
260
|
-
} else if ((ex1 - 0.015) ** 2 + (ey1 - 0.015) ** 2 < 0.
|
|
314
|
+
} else if ((ex1 - 0.015) ** 2 + (ey1 - 0.015) ** 2 < 0.0005 || (ex2 - 0.015) ** 2 + (ey2 - 0.015) ** 2 < 0.0005) {
|
|
261
315
|
if (!isTired) { r = 255; g = 255; b = 255; }
|
|
262
316
|
}
|
|
263
317
|
}
|
|
@@ -407,11 +461,44 @@ function getPixel(px: number, py: number, objects: RenderObj[], skyColors: Retur
|
|
|
407
461
|
let bgR = Math.floor(skyColors.rTop * (1 - grad) + skyColors.rBot * grad);
|
|
408
462
|
let bgG = Math.floor(skyColors.gTop * (1 - grad) + skyColors.gBot * grad);
|
|
409
463
|
let bgB = Math.floor(skyColors.bTop * (1 - grad) + skyColors.bBot * grad);
|
|
464
|
+
|
|
465
|
+
// Stars at night / dusk — twinkling via time modulation
|
|
410
466
|
if (skyColors.isNight) {
|
|
411
|
-
const star = Math.sin(px * 80) * Math.cos(py * 80);
|
|
412
|
-
|
|
413
|
-
|
|
467
|
+
const star = Math.sin(px * 80 + 1.3) * Math.cos(py * 80 + 0.7);
|
|
468
|
+
const twinkle = Math.sin(time * 3 + px * 20 + py * 30) * 0.5 + 0.5;
|
|
469
|
+
if (star > 0.98) { const b = Math.floor(180 + twinkle * 75); bgR = b; bgG = b; bgB = b; }
|
|
470
|
+
else if (star > 0.96) { const b = Math.floor(40 + twinkle * 40); bgR += b; bgG += b; bgB += b; }
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
// Clouds — wispy noise shapes in upper sky
|
|
474
|
+
const w = (skyColors as any).weather as Weather | undefined;
|
|
475
|
+
if (w === "cloudy" || w === "rain" || w === "storm" || w === "snow") {
|
|
476
|
+
const cloudDensity = w === "storm" ? 0.7 : w === "rain" ? 0.5 : w === "snow" ? 0.4 : 0.3;
|
|
477
|
+
const cn = Math.sin(px * 8 + time * 0.3) * Math.cos(py * 12 - time * 0.2) +
|
|
478
|
+
Math.sin(px * 16 + py * 6) * 0.5;
|
|
479
|
+
if (cn > 1.0 - cloudDensity && py < 0.2) {
|
|
480
|
+
const blend = Math.min(1.0, (cn - (1.0 - cloudDensity)) * 4);
|
|
481
|
+
const cr = w === "storm" ? 50 : 180, cg = w === "storm" ? 50 : 185, cb = w === "storm" ? 55 : 195;
|
|
482
|
+
bgR = Math.floor(bgR * (1 - blend) + cr * blend);
|
|
483
|
+
bgG = Math.floor(bgG * (1 - blend) + cg * blend);
|
|
484
|
+
bgB = Math.floor(bgB * (1 - blend) + cb * blend);
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
// Sunset/dawn glow at horizon
|
|
489
|
+
const tod = (skyColors as any).timeOfDay as TimeOfDay | undefined;
|
|
490
|
+
if (tod === "sunset" || tod === "dawn") {
|
|
491
|
+
const horizonGlow = Math.exp(-(py - 0.4) * (py - 0.4) * 20);
|
|
492
|
+
if (tod === "sunset") {
|
|
493
|
+
bgR = Math.min(255, Math.floor(bgR + horizonGlow * 80));
|
|
494
|
+
bgG = Math.min(255, Math.floor(bgG + horizonGlow * 30));
|
|
495
|
+
} else {
|
|
496
|
+
bgR = Math.min(255, Math.floor(bgR + horizonGlow * 50));
|
|
497
|
+
bgG = Math.min(255, Math.floor(bgG + horizonGlow * 40));
|
|
498
|
+
bgB = Math.min(255, Math.floor(bgB + horizonGlow * 30));
|
|
499
|
+
}
|
|
414
500
|
}
|
|
501
|
+
|
|
415
502
|
return [bgR, bgG, bgB];
|
|
416
503
|
}
|
|
417
504
|
|
|
@@ -463,7 +550,7 @@ function buildObjects(): RenderObj[] {
|
|
|
463
550
|
}
|
|
464
551
|
|
|
465
552
|
function getScreenEdgeX(): number {
|
|
466
|
-
const effectDim = Math.max(40, Math.min(W, H *
|
|
553
|
+
const effectDim = Math.max(40, Math.min(W, H * 4));
|
|
467
554
|
const scale = 2.0 / effectDim;
|
|
468
555
|
return (W / 2.0) * scale;
|
|
469
556
|
}
|
|
@@ -486,14 +573,21 @@ function updatePhysics(dt: number) {
|
|
|
486
573
|
ffZ = posZ + Math.sin(time * 0.9) * 0.4;
|
|
487
574
|
|
|
488
575
|
// Weather particles
|
|
489
|
-
const
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
particles.push({
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
});
|
|
576
|
+
const weather = getWeather();
|
|
577
|
+
const effectDim = Math.max(40, Math.min(W, H * 4));
|
|
578
|
+
const wScale = 2.0 / effectDim;
|
|
579
|
+
if (weather === "rain" && Math.random() < 0.4) {
|
|
580
|
+
particles.push({ x: (Math.random() - 0.5) * W * wScale, y: -H * wScale, vx: 0.15, vy: 2.5 + Math.random(), char: "|", r: 150, g: 200, b: 255, life: 1.0, type: "rain" });
|
|
581
|
+
}
|
|
582
|
+
if (weather === "storm" && Math.random() < 0.6) {
|
|
583
|
+
particles.push({ x: (Math.random() - 0.5) * W * wScale, y: -H * wScale, vx: 0.4 + Math.random() * 0.3, vy: 3.0 + Math.random() * 2, char: "/", r: 180, g: 200, b: 255, life: 0.8, type: "rain" });
|
|
584
|
+
// Occasional lightning flash (brief bright particle)
|
|
585
|
+
if (Math.random() < 0.005) {
|
|
586
|
+
particles.push({ x: (Math.random() - 0.5) * W * wScale * 0.5, y: -H * wScale * 0.5, vx: 0, vy: 0, char: "#", r: 255, g: 255, b: 255, life: 0.1, type: "lightning" });
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
if (weather === "snow" && Math.random() < 0.2) {
|
|
590
|
+
particles.push({ x: (Math.random() - 0.5) * W * wScale, y: -H * wScale, vx: (Math.random() - 0.5) * 0.3, vy: 0.4 + Math.random() * 0.3, char: ".", r: 240, g: 245, b: 255, life: 3.0, type: "snow" });
|
|
497
591
|
}
|
|
498
592
|
|
|
499
593
|
// Ball physics
|
|
@@ -639,13 +733,15 @@ function updatePhysics(dt: number) {
|
|
|
639
733
|
if (p.type === "z") p.x += Math.sin(p.y * 4.0) * 0.005;
|
|
640
734
|
if (p.type === "note") p.x += Math.sin(p.y * 6.0) * 0.01;
|
|
641
735
|
if (p.type === "rain" && p.y > 0.6) { p.type = "splash"; p.char = "."; p.vy = -0.5; p.vx = (Math.random() - 0.5) * 0.5; p.life = 0.2; }
|
|
736
|
+
if (p.type === "snow") { p.vx += Math.sin(time * 2 + p.x * 5) * 0.01; if (p.y > 0.55) { p.life = 0; } }
|
|
737
|
+
if (p.type === "lightning") { p.life -= dt * 8; }
|
|
642
738
|
p.life -= dt * 0.8;
|
|
643
739
|
if (p.life <= 0) particles.splice(i, 1);
|
|
644
740
|
}
|
|
645
741
|
}
|
|
646
742
|
|
|
647
743
|
function renderToBuffers() {
|
|
648
|
-
const effectDim = Math.max(40, Math.min(W, H *
|
|
744
|
+
const effectDim = Math.max(40, Math.min(W, H * 4));
|
|
649
745
|
const scale = 2.0 / effectDim;
|
|
650
746
|
const objects = buildObjects();
|
|
651
747
|
const skyColors = getWeatherAndTime();
|
|
@@ -676,7 +772,7 @@ function renderToBuffers() {
|
|
|
676
772
|
if (d > maxD) maxD = d;
|
|
677
773
|
}
|
|
678
774
|
|
|
679
|
-
if (maxD >
|
|
775
|
+
if (maxD > 30) {
|
|
680
776
|
// EDGE CELL — use quadrant character for 2× horizontal detail
|
|
681
777
|
const lum0 = tl[0] * 77 + tl[1] * 150 + tl[2] * 29;
|
|
682
778
|
const lum1 = tr[0] * 77 + tr[1] * 150 + tr[2] * 29;
|
|
@@ -798,7 +894,16 @@ export function renderPompom(width: number, audioLevel: number, dt: number): str
|
|
|
798
894
|
else if (currentState === "peek") stateMsg = "Pompom is peeking back in... hi!";
|
|
799
895
|
else if (currentState === "offscreen") stateMsg = "Pompom wandered off... they'll be back";
|
|
800
896
|
else if (isTalking) stateMsg = "Pompom is listening to you speak";
|
|
801
|
-
else
|
|
897
|
+
else {
|
|
898
|
+
const w = getWeather(), tod = getTimeOfDay();
|
|
899
|
+
if (w === "storm") stateMsg = "Pompom hides from the thunder!";
|
|
900
|
+
else if (w === "rain") stateMsg = "Pompom watches the rain fall";
|
|
901
|
+
else if (w === "snow") stateMsg = "Pompom catches snowflakes!";
|
|
902
|
+
else if (tod === "dawn") stateMsg = "Pompom watches the sunrise";
|
|
903
|
+
else if (tod === "sunset") stateMsg = "Pompom enjoys the sunset";
|
|
904
|
+
else if (tod === "night") stateMsg = "Pompom stargazes under the night sky";
|
|
905
|
+
else stateMsg = "Pompom is vibing. Pet, feed, or play!";
|
|
906
|
+
}
|
|
802
907
|
|
|
803
908
|
// Build status: "─ ⌥ w·Wake p·Pet ... │ State ───" exactly W visible chars
|
|
804
909
|
const shortcuts: [string, string][] = [
|