easter-egg-quest 1.0.17 → 1.0.19
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/easter-egg-quest.es.js +153 -67
- package/dist/easter-egg-quest.umd.js +1 -1
- package/dist/types/input/InputTracker.d.ts +9 -2
- package/dist/types/rendering/NarrativeRenderer.d.ts +3 -1
- package/dist/types/rendering/PageBreather.d.ts +1 -0
- package/dist/types/rendering/ResultsRenderer.d.ts +2 -0
- package/dist/types/stages/RhythmStage.d.ts +1 -0
- package/package.json +1 -1
|
@@ -57,44 +57,30 @@ const DEFAULT_SCRIPT = {
|
|
|
57
57
|
stage2Success: [
|
|
58
58
|
"movement was the answer",
|
|
59
59
|
"the second egg appears",
|
|
60
|
-
"
|
|
61
|
-
"not stillness this time",
|
|
62
|
-
"but life in motion"
|
|
60
|
+
"hold it to collect"
|
|
63
61
|
],
|
|
64
62
|
// ── Stage 3: Rhythm ───────────────────────────────────────────────────
|
|
65
63
|
stage3Intro: [
|
|
66
|
-
"two truths that shouldn’t exist together",
|
|
67
64
|
"the final riddle:",
|
|
68
|
-
"
|
|
65
|
+
"Every living thing knows it. You’ve been doing it your whole life without thinking."
|
|
69
66
|
],
|
|
70
67
|
stage3Reactions: [
|
|
71
|
-
"
|
|
72
|
-
"
|
|
73
|
-
"
|
|
74
|
-
"
|
|
75
|
-
"you’ve been doing it since birth",
|
|
76
|
-
"listen to yourself",
|
|
77
|
-
"closer...",
|
|
78
|
-
"can you feel it?",
|
|
79
|
-
"the body remembers",
|
|
68
|
+
"move for a few seconds",
|
|
69
|
+
"then hold still for a moment",
|
|
70
|
+
"don’t rush it",
|
|
71
|
+
"alternate move and pause",
|
|
80
72
|
"in",
|
|
81
73
|
"out"
|
|
82
74
|
],
|
|
83
75
|
stage3Success: [
|
|
84
76
|
"rhythm was the answer",
|
|
85
|
-
"
|
|
86
|
-
"but the dance between them",
|
|
87
|
-
"the third egg appears",
|
|
88
|
-
"for those who breathe"
|
|
77
|
+
"you found the third egg"
|
|
89
78
|
],
|
|
90
79
|
// ── Finale ────────────────────────────────────────────────────────────
|
|
91
80
|
finale: [
|
|
92
|
-
"
|
|
93
|
-
"
|
|
94
|
-
"
|
|
95
|
-
"motion",
|
|
96
|
-
"rhythm",
|
|
97
|
-
"this is where life appears"
|
|
81
|
+
"all three eggs found",
|
|
82
|
+
"stillness, motion, rhythm",
|
|
83
|
+
"that's the whole set"
|
|
98
84
|
],
|
|
99
85
|
// ── Results / Share ───────────────────────────────────────────────────
|
|
100
86
|
results: [
|
|
@@ -336,8 +322,10 @@ class InputTracker {
|
|
|
336
322
|
this._prevX = 0;
|
|
337
323
|
this._prevY = 0;
|
|
338
324
|
this._prevMoveTs = 0;
|
|
339
|
-
this.
|
|
340
|
-
this.
|
|
325
|
+
this.DEFAULT_STILL_THRESHOLD_MS = 400;
|
|
326
|
+
this._stillThresholdMs = this.DEFAULT_STILL_THRESHOLD_MS;
|
|
327
|
+
this.DEFAULT_MIN_PHASE_MS = 300;
|
|
328
|
+
this._minPhaseMs = this.DEFAULT_MIN_PHASE_MS;
|
|
341
329
|
this._stillTimer = null;
|
|
342
330
|
this._handlers = [];
|
|
343
331
|
this._active = false;
|
|
@@ -476,6 +464,18 @@ class InputTracker {
|
|
|
476
464
|
this._snapshot.totalDistance = 0;
|
|
477
465
|
this._snapshot.maxVelocity = 0;
|
|
478
466
|
}
|
|
467
|
+
configurePhaseDetection(opts) {
|
|
468
|
+
if (typeof opts.stillThresholdMs === "number") {
|
|
469
|
+
this._stillThresholdMs = Math.max(250, opts.stillThresholdMs);
|
|
470
|
+
}
|
|
471
|
+
if (typeof opts.minPhaseMs === "number") {
|
|
472
|
+
this._minPhaseMs = Math.max(150, opts.minPhaseMs);
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
resetPhaseDetection() {
|
|
476
|
+
this._stillThresholdMs = this.DEFAULT_STILL_THRESHOLD_MS;
|
|
477
|
+
this._minPhaseMs = this.DEFAULT_MIN_PHASE_MS;
|
|
478
|
+
}
|
|
479
479
|
// ─── Movement / Stillness transition logic ─────────────────────────────
|
|
480
480
|
_registerMove(now) {
|
|
481
481
|
this._snapshot.lastMoveTime = now;
|
|
@@ -488,7 +488,7 @@ class InputTracker {
|
|
|
488
488
|
if (this._stillTimer) clearTimeout(this._stillTimer);
|
|
489
489
|
this._stillTimer = setTimeout(() => {
|
|
490
490
|
this._becomeStill();
|
|
491
|
-
}, this.
|
|
491
|
+
}, this._stillThresholdMs);
|
|
492
492
|
}
|
|
493
493
|
_becomeStill() {
|
|
494
494
|
if (!this._snapshot.isMoving) return;
|
|
@@ -502,7 +502,7 @@ class InputTracker {
|
|
|
502
502
|
}
|
|
503
503
|
_completePhase(type, endTime) {
|
|
504
504
|
const duration = endTime - this._currentPhaseStart;
|
|
505
|
-
if (duration >= this.
|
|
505
|
+
if (duration >= this._minPhaseMs) {
|
|
506
506
|
this._phases.push({
|
|
507
507
|
type,
|
|
508
508
|
startTime: this._currentPhaseStart,
|
|
@@ -1317,6 +1317,7 @@ class NarrativeRenderer {
|
|
|
1317
1317
|
this.container = null;
|
|
1318
1318
|
this.currentLine = null;
|
|
1319
1319
|
this.clearTimer = null;
|
|
1320
|
+
this.awaitingConfirm = false;
|
|
1320
1321
|
this.config = config;
|
|
1321
1322
|
}
|
|
1322
1323
|
mount() {
|
|
@@ -1340,14 +1341,22 @@ class NarrativeRenderer {
|
|
|
1340
1341
|
this.container.className = "eeq-narrative";
|
|
1341
1342
|
this.shadow.appendChild(this.container);
|
|
1342
1343
|
}
|
|
1344
|
+
_removeCurrentLine(immediate = false) {
|
|
1345
|
+
if (!this.currentLine) return;
|
|
1346
|
+
const prev = this.currentLine;
|
|
1347
|
+
this.currentLine = null;
|
|
1348
|
+
if (immediate) {
|
|
1349
|
+
prev.remove();
|
|
1350
|
+
return;
|
|
1351
|
+
}
|
|
1352
|
+
prev.classList.add("eeq-narrative-exit");
|
|
1353
|
+
setTimeout(() => prev.remove(), 750);
|
|
1354
|
+
}
|
|
1343
1355
|
/** Show a single narrative line. Fades in, replaces any previous line. */
|
|
1344
1356
|
showLine(text, className = "eeq-line") {
|
|
1345
1357
|
if (!this.container) return;
|
|
1346
|
-
if (this.
|
|
1347
|
-
|
|
1348
|
-
prev.classList.add("eeq-narrative-exit");
|
|
1349
|
-
setTimeout(() => prev.remove(), 750);
|
|
1350
|
-
}
|
|
1358
|
+
if (this.awaitingConfirm) return;
|
|
1359
|
+
this._removeCurrentLine(true);
|
|
1351
1360
|
if (this.clearTimer) {
|
|
1352
1361
|
clearTimeout(this.clearTimer);
|
|
1353
1362
|
this.clearTimer = null;
|
|
@@ -1376,11 +1385,8 @@ class NarrativeRenderer {
|
|
|
1376
1385
|
resolve();
|
|
1377
1386
|
return;
|
|
1378
1387
|
}
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
prev.classList.add("eeq-narrative-exit");
|
|
1382
|
-
setTimeout(() => prev.remove(), 750);
|
|
1383
|
-
}
|
|
1388
|
+
this.awaitingConfirm = true;
|
|
1389
|
+
this._removeCurrentLine(true);
|
|
1384
1390
|
if (this.clearTimer) {
|
|
1385
1391
|
clearTimeout(this.clearTimer);
|
|
1386
1392
|
this.clearTimer = null;
|
|
@@ -1392,7 +1398,10 @@ class NarrativeRenderer {
|
|
|
1392
1398
|
const btn = document.createElement("button");
|
|
1393
1399
|
btn.className = "eeq-confirm-btn";
|
|
1394
1400
|
btn.textContent = "OK";
|
|
1395
|
-
btn.addEventListener("click", () =>
|
|
1401
|
+
btn.addEventListener("click", () => {
|
|
1402
|
+
this.awaitingConfirm = false;
|
|
1403
|
+
resolve();
|
|
1404
|
+
}, { once: true });
|
|
1396
1405
|
line.appendChild(btn);
|
|
1397
1406
|
requestAnimationFrame(() => {
|
|
1398
1407
|
requestAnimationFrame(() => {
|
|
@@ -1414,22 +1423,19 @@ class NarrativeRenderer {
|
|
|
1414
1423
|
this.showLine(text, "eeq-line eeq-celebration");
|
|
1415
1424
|
}
|
|
1416
1425
|
/** Clear current narrative text. */
|
|
1417
|
-
clear() {
|
|
1426
|
+
clear(force = false) {
|
|
1427
|
+
if (this.awaitingConfirm && !force) return;
|
|
1418
1428
|
if (this.clearTimer) {
|
|
1419
1429
|
clearTimeout(this.clearTimer);
|
|
1420
1430
|
this.clearTimer = null;
|
|
1421
1431
|
}
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
const ref = this.currentLine;
|
|
1425
|
-
setTimeout(() => ref.remove(), 750);
|
|
1426
|
-
this.currentLine = null;
|
|
1427
|
-
}
|
|
1432
|
+
this._removeCurrentLine(force);
|
|
1433
|
+
this.awaitingConfirm = false;
|
|
1428
1434
|
}
|
|
1429
1435
|
/** Remove all DOM. */
|
|
1430
1436
|
destroy() {
|
|
1431
1437
|
var _a2;
|
|
1432
|
-
this.clear();
|
|
1438
|
+
this.clear(true);
|
|
1433
1439
|
(_a2 = this.host) == null ? void 0 : _a2.remove();
|
|
1434
1440
|
this.host = null;
|
|
1435
1441
|
this.shadow = null;
|
|
@@ -3724,16 +3730,16 @@ const _ResultsRenderer = class _ResultsRenderer {
|
|
|
3724
3730
|
inner.appendChild(badgesDiv);
|
|
3725
3731
|
const invite = document.createElement("div");
|
|
3726
3732
|
invite.className = "eeq-share-invite";
|
|
3727
|
-
invite.textContent = "share your result
|
|
3733
|
+
invite.textContent = "share or copy your result";
|
|
3728
3734
|
inner.appendChild(invite);
|
|
3729
3735
|
const actions = document.createElement("div");
|
|
3730
3736
|
actions.className = "eeq-results-actions";
|
|
3731
3737
|
const shareBtn = document.createElement("button");
|
|
3732
3738
|
shareBtn.className = "eeq-btn eeq-btn-share";
|
|
3733
|
-
shareBtn.title = "share result";
|
|
3739
|
+
shareBtn.title = "share or copy result";
|
|
3734
3740
|
shareBtn.innerHTML = '<svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4.5 10.5a2 2 0 1 1 0-4 2 2 0 0 1 0 4zm7-4a2 2 0 1 1 0-4 2 2 0 0 1 0 4zm0 8a2 2 0 1 1 0-4 2 2 0 0 1 0 4z" stroke="currentColor" stroke-width="1.2"/><path d="M6.3 9.2l3.4 1.6M6.3 7.8l3.4-1.6" stroke="currentColor" stroke-width="1.2"/></svg>';
|
|
3735
3741
|
const shareLabel = document.createElement("span");
|
|
3736
|
-
shareLabel.textContent = "share";
|
|
3742
|
+
shareLabel.textContent = "share / copy";
|
|
3737
3743
|
shareBtn.appendChild(shareLabel);
|
|
3738
3744
|
actions.appendChild(shareBtn);
|
|
3739
3745
|
const finishBtn = document.createElement("button");
|
|
@@ -3967,6 +3973,7 @@ const _ResultsRenderer = class _ResultsRenderer {
|
|
|
3967
3973
|
_copyShareImage(s) {
|
|
3968
3974
|
var _a2;
|
|
3969
3975
|
const canvas = this._renderCard(s);
|
|
3976
|
+
const shareText = this._buildShareText(s);
|
|
3970
3977
|
const dataUrl = canvas.toDataURL("image/png");
|
|
3971
3978
|
const byteString = atob(dataUrl.split(",")[1]);
|
|
3972
3979
|
const ab = new ArrayBuffer(byteString.length);
|
|
@@ -3980,17 +3987,35 @@ const _ResultsRenderer = class _ResultsRenderer {
|
|
|
3980
3987
|
navigator.share({
|
|
3981
3988
|
files: [file],
|
|
3982
3989
|
title: "Easter Egg Quest",
|
|
3983
|
-
text:
|
|
3990
|
+
text: shareText
|
|
3984
3991
|
}).catch(() => {
|
|
3985
3992
|
});
|
|
3986
3993
|
return;
|
|
3987
3994
|
}
|
|
3995
|
+
navigator.share({
|
|
3996
|
+
title: "Easter Egg Quest",
|
|
3997
|
+
text: shareText
|
|
3998
|
+
}).catch(() => {
|
|
3999
|
+
});
|
|
4000
|
+
return;
|
|
3988
4001
|
}
|
|
3989
4002
|
if (navigator.clipboard && typeof ClipboardItem !== "undefined") {
|
|
3990
4003
|
navigator.clipboard.write([
|
|
3991
4004
|
new ClipboardItem({ "image/png": blob })
|
|
3992
4005
|
]).then(() => {
|
|
3993
|
-
this._flashButton(".eeq-btn-share", "copied
|
|
4006
|
+
this._flashButton(".eeq-btn-share", "image copied");
|
|
4007
|
+
}).catch(() => {
|
|
4008
|
+
this._copyShareText(shareText, blob);
|
|
4009
|
+
});
|
|
4010
|
+
return;
|
|
4011
|
+
}
|
|
4012
|
+
this._copyShareText(shareText, blob);
|
|
4013
|
+
}
|
|
4014
|
+
_copyShareText(text, blob) {
|
|
4015
|
+
var _a2;
|
|
4016
|
+
if ((_a2 = navigator.clipboard) == null ? void 0 : _a2.writeText) {
|
|
4017
|
+
navigator.clipboard.writeText(text).then(() => {
|
|
4018
|
+
this._flashButton(".eeq-btn-share", "text copied");
|
|
3994
4019
|
}).catch(() => {
|
|
3995
4020
|
this._downloadBlob(blob);
|
|
3996
4021
|
});
|
|
@@ -3998,6 +4023,11 @@ const _ResultsRenderer = class _ResultsRenderer {
|
|
|
3998
4023
|
}
|
|
3999
4024
|
this._downloadBlob(blob);
|
|
4000
4025
|
}
|
|
4026
|
+
_buildShareText(s) {
|
|
4027
|
+
const time = formatTime(s.totalTime);
|
|
4028
|
+
const eggs = `${s.eggsFound}/3 eggs`;
|
|
4029
|
+
return `Easter Egg Quest: ${s.behaviorProfile.title} • ${time} • ${eggs}`;
|
|
4030
|
+
}
|
|
4001
4031
|
_downloadBlob(blob) {
|
|
4002
4032
|
const url = URL.createObjectURL(blob);
|
|
4003
4033
|
const a = document.createElement("a");
|
|
@@ -4077,10 +4107,10 @@ const _ResultsRenderer = class _ResultsRenderer {
|
|
|
4077
4107
|
ctx.fillText(labels.join(" · "), W / 2, divY + 92);
|
|
4078
4108
|
ctx.font = "14px Georgia, serif";
|
|
4079
4109
|
ctx.fillStyle = "rgba(232,224,214,0.55)";
|
|
4080
|
-
ctx.fillText("
|
|
4110
|
+
ctx.fillText("All three eggs found", W / 2, divY + 130);
|
|
4081
4111
|
ctx.font = "11px Georgia, serif";
|
|
4082
4112
|
ctx.fillStyle = "rgba(232,224,214,0.3)";
|
|
4083
|
-
ctx.fillText("
|
|
4113
|
+
ctx.fillText("Share or copy your result", W / 2, divY + 150);
|
|
4084
4114
|
ctx.font = "9px Georgia, serif";
|
|
4085
4115
|
ctx.fillStyle = "rgba(232,224,214,0.12)";
|
|
4086
4116
|
ctx.fillText("easter egg quest", W / 2, H - 16);
|
|
@@ -4550,6 +4580,8 @@ class StillnessStage {
|
|
|
4550
4580
|
this._attempts = 1;
|
|
4551
4581
|
this.input.resetPhases();
|
|
4552
4582
|
this.input.resetCounts();
|
|
4583
|
+
this.bus.emit("narrative:clear");
|
|
4584
|
+
await this._wait(850);
|
|
4553
4585
|
this._introPlayed = false;
|
|
4554
4586
|
const introLines = this.script.stage1Intro;
|
|
4555
4587
|
for (let i = 0; i < introLines.length; i++) {
|
|
@@ -4644,6 +4676,8 @@ class MotionStage {
|
|
|
4644
4676
|
this._attempts = 1;
|
|
4645
4677
|
this.input.resetPhases();
|
|
4646
4678
|
this.input.resetCounts();
|
|
4679
|
+
this.bus.emit("narrative:clear");
|
|
4680
|
+
await this._wait(850);
|
|
4647
4681
|
this._introPlayed = false;
|
|
4648
4682
|
const introLines = this.script.stage2Intro;
|
|
4649
4683
|
for (let i = 0; i < introLines.length; i++) {
|
|
@@ -4743,6 +4777,7 @@ class RhythmStage {
|
|
|
4743
4777
|
this._totalCycles = 0;
|
|
4744
4778
|
this._lastPhaseCount = 0;
|
|
4745
4779
|
this._bestAccuracy = 0;
|
|
4780
|
+
this._lastGuideTime = 0;
|
|
4746
4781
|
this.IDEAL_MOVE_MIN = 1800;
|
|
4747
4782
|
this.IDEAL_MOVE_MAX = 5e3;
|
|
4748
4783
|
this.IDEAL_PAUSE_MIN = 1200;
|
|
@@ -4757,8 +4792,11 @@ class RhythmStage {
|
|
|
4757
4792
|
this._attempts = 1;
|
|
4758
4793
|
this._goodCycles = 0;
|
|
4759
4794
|
this._totalCycles = 0;
|
|
4795
|
+
this.input.configurePhaseDetection({ stillThresholdMs: 900, minPhaseMs: 500 });
|
|
4760
4796
|
this.input.resetPhases();
|
|
4761
4797
|
this.input.resetCounts();
|
|
4798
|
+
this.bus.emit("narrative:clear");
|
|
4799
|
+
await this._wait(850);
|
|
4762
4800
|
this._introPlayed = false;
|
|
4763
4801
|
const introLines = this.script.stage3Intro;
|
|
4764
4802
|
for (let i = 0; i < introLines.length; i++) {
|
|
@@ -4774,6 +4812,8 @@ class RhythmStage {
|
|
|
4774
4812
|
this.input.resetPhases();
|
|
4775
4813
|
this._lastPhaseCount = this.input.phases.length;
|
|
4776
4814
|
this._introPlayed = true;
|
|
4815
|
+
this.bus.emit("narrative:show", "move for a few seconds, then hold still");
|
|
4816
|
+
this._lastGuideTime = Date.now();
|
|
4777
4817
|
}
|
|
4778
4818
|
update(_dt) {
|
|
4779
4819
|
if (this._status === "complete" || !this._introPlayed) return this._status;
|
|
@@ -4800,7 +4840,17 @@ class RhythmStage {
|
|
|
4800
4840
|
if (accuracy > this._bestAccuracy) this._bestAccuracy = accuracy;
|
|
4801
4841
|
const progress = Math.min(1, this._goodCycles / requiredCycles);
|
|
4802
4842
|
this.bus.emit("stage:progress", progress);
|
|
4803
|
-
this.bus.emit("rhythm:breathe", {
|
|
4843
|
+
this.bus.emit("rhythm:breathe", {
|
|
4844
|
+
accuracy,
|
|
4845
|
+
isMoving: this.input.snapshot.isMoving,
|
|
4846
|
+
progress,
|
|
4847
|
+
moveDuration: this.input.moveDuration,
|
|
4848
|
+
stillDuration: this.input.stillDuration
|
|
4849
|
+
});
|
|
4850
|
+
if (accuracy === 0 && Date.now() - this._lastGuideTime > 12e3) {
|
|
4851
|
+
this._lastGuideTime = Date.now();
|
|
4852
|
+
this.bus.emit("narrative:show", "try this: move 2-4s, then stay still 1-3s");
|
|
4853
|
+
}
|
|
4804
4854
|
if (this._goodCycles >= requiredCycles) {
|
|
4805
4855
|
this._status = "complete";
|
|
4806
4856
|
this.bus.emit("stage:progress", 1);
|
|
@@ -4810,6 +4860,7 @@ class RhythmStage {
|
|
|
4810
4860
|
return this._status;
|
|
4811
4861
|
}
|
|
4812
4862
|
cleanup() {
|
|
4863
|
+
this.input.resetPhaseDetection();
|
|
4813
4864
|
}
|
|
4814
4865
|
getResult() {
|
|
4815
4866
|
return {
|
|
@@ -4829,7 +4880,7 @@ class RhythmStage {
|
|
|
4829
4880
|
}
|
|
4830
4881
|
_showReaction() {
|
|
4831
4882
|
const now = Date.now();
|
|
4832
|
-
if (now - this._lastNarrativeTime >
|
|
4883
|
+
if (now - this._lastNarrativeTime > 12e3) {
|
|
4833
4884
|
this._lastNarrativeTime = now;
|
|
4834
4885
|
const reactions = this.script.stage3Reactions;
|
|
4835
4886
|
const line = reactions[this._narrativeIndex % reactions.length];
|
|
@@ -4945,6 +4996,7 @@ class PageBreather {
|
|
|
4945
4996
|
this._targetIntensity = 0;
|
|
4946
4997
|
this._rafId = 0;
|
|
4947
4998
|
this._overlay = null;
|
|
4999
|
+
this._label = null;
|
|
4948
5000
|
this._startTime = 0;
|
|
4949
5001
|
this._isUserMoving = false;
|
|
4950
5002
|
this._inSync = false;
|
|
@@ -4960,9 +5012,16 @@ class PageBreather {
|
|
|
4960
5012
|
const shouldMove = phase < 0.5;
|
|
4961
5013
|
this._inSync = this._accuracy > 0 && shouldMove === this._isUserMoving;
|
|
4962
5014
|
const baseOpacity = wave * 0.85;
|
|
4963
|
-
const syncBonus = this._inSync ? 0.
|
|
4964
|
-
const opacity = this._intensity * (baseOpacity + (1 - wave) * 0.
|
|
4965
|
-
this._overlay.style.opacity = String(Math.min(
|
|
5015
|
+
const syncBonus = this._inSync ? 0.22 : 0;
|
|
5016
|
+
const opacity = this._intensity * (0.18 + baseOpacity + (1 - wave) * 0.12 + syncBonus);
|
|
5017
|
+
this._overlay.style.opacity = String(Math.min(0.9, opacity));
|
|
5018
|
+
this._overlay.style.transform = `scale(${1 + wave * 0.015})`;
|
|
5019
|
+
if (this._label) {
|
|
5020
|
+
this._label.textContent = shouldMove ? "Move" : "Hold still";
|
|
5021
|
+
this._label.style.opacity = shouldMove === this._isUserMoving ? "0.98" : "0.82";
|
|
5022
|
+
this._label.style.boxShadow = this._inSync ? "0 0 28px rgba(212,165,80,0.4)" : "0 0 20px rgba(255,255,255,0.08)";
|
|
5023
|
+
this._label.style.borderColor = this._inSync ? "rgba(212,165,80,0.55)" : "rgba(255,255,255,0.12)";
|
|
5024
|
+
}
|
|
4966
5025
|
};
|
|
4967
5026
|
}
|
|
4968
5027
|
start() {
|
|
@@ -4979,31 +5038,56 @@ class PageBreather {
|
|
|
4979
5038
|
"pointer-events:none",
|
|
4980
5039
|
"z-index:999980",
|
|
4981
5040
|
"opacity:0",
|
|
4982
|
-
"background:radial-gradient(ellipse at center,
|
|
5041
|
+
"background:radial-gradient(ellipse at center, rgba(255,255,255,0.03) 0%, rgba(255,255,255,0.02) 24%, rgba(8,12,22,0.18) 46%, rgba(4,6,12,0.82) 100%)",
|
|
4983
5042
|
"transition:opacity 0.3s ease"
|
|
4984
5043
|
].join(";");
|
|
4985
5044
|
document.body.appendChild(el);
|
|
4986
5045
|
this._overlay = el;
|
|
5046
|
+
const label = document.createElement("div");
|
|
5047
|
+
label.style.cssText = [
|
|
5048
|
+
"position:fixed",
|
|
5049
|
+
"left:50%",
|
|
5050
|
+
"top:18%",
|
|
5051
|
+
"transform:translateX(-50%)",
|
|
5052
|
+
"pointer-events:none",
|
|
5053
|
+
"z-index:999981",
|
|
5054
|
+
"padding:8px 14px",
|
|
5055
|
+
"border-radius:999px",
|
|
5056
|
+
"font-family:Georgia, Times New Roman, serif",
|
|
5057
|
+
"font-size:12px",
|
|
5058
|
+
"letter-spacing:0.12em",
|
|
5059
|
+
"text-transform:uppercase",
|
|
5060
|
+
"color:rgba(255,248,232,0.95)",
|
|
5061
|
+
"background:rgba(8,10,18,0.36)",
|
|
5062
|
+
"border:1px solid rgba(255,255,255,0.12)",
|
|
5063
|
+
"box-shadow:0 0 20px rgba(255,255,255,0.08)",
|
|
5064
|
+
"opacity:0.78"
|
|
5065
|
+
].join(";");
|
|
5066
|
+
label.textContent = "Move";
|
|
5067
|
+
document.body.appendChild(label);
|
|
5068
|
+
this._label = label;
|
|
4987
5069
|
this._tick();
|
|
4988
5070
|
}
|
|
4989
5071
|
update(accuracy, isMoving) {
|
|
4990
5072
|
if (!this._active) return;
|
|
4991
5073
|
this._isUserMoving = isMoving;
|
|
4992
5074
|
this._accuracy = accuracy;
|
|
4993
|
-
const raw = Math.max(0, Math.min(1, (accuracy - 0.
|
|
4994
|
-
this._targetIntensity = 0.
|
|
5075
|
+
const raw = Math.max(0, Math.min(1, (accuracy - 0.02) / 0.45));
|
|
5076
|
+
this._targetIntensity = 0.68 + raw * 0.32;
|
|
4995
5077
|
}
|
|
4996
5078
|
/** Whether the user is currently in sync with the breathing rhythm. */
|
|
4997
5079
|
isInSync() {
|
|
4998
5080
|
return this._inSync;
|
|
4999
5081
|
}
|
|
5000
5082
|
stop() {
|
|
5001
|
-
var _a2;
|
|
5083
|
+
var _a2, _b2;
|
|
5002
5084
|
if (!this._active) return;
|
|
5003
5085
|
this._active = false;
|
|
5004
5086
|
cancelAnimationFrame(this._rafId);
|
|
5005
5087
|
(_a2 = this._overlay) == null ? void 0 : _a2.remove();
|
|
5006
5088
|
this._overlay = null;
|
|
5089
|
+
(_b2 = this._label) == null ? void 0 : _b2.remove();
|
|
5090
|
+
this._label = null;
|
|
5007
5091
|
this._intensity = 0;
|
|
5008
5092
|
this._targetIntensity = 0;
|
|
5009
5093
|
this._inSync = false;
|
|
@@ -5097,7 +5181,7 @@ class GameController {
|
|
|
5097
5181
|
});
|
|
5098
5182
|
this.bus.on("narrative:clear", () => {
|
|
5099
5183
|
var _a3;
|
|
5100
|
-
return (_a3 = this.narrative) == null ? void 0 : _a3.clear();
|
|
5184
|
+
return (_a3 = this.narrative) == null ? void 0 : _a3.clear(true);
|
|
5101
5185
|
});
|
|
5102
5186
|
this.bus.on("egg:reveal", (index) => this._handleEggReveal(index));
|
|
5103
5187
|
this.bus.on("stage:progress", (p) => this._handleStageProgress(p));
|
|
@@ -5109,8 +5193,10 @@ class GameController {
|
|
|
5109
5193
|
var _a3, _b3;
|
|
5110
5194
|
(_a3 = this.eggRenderer) == null ? void 0 : _a3.setBreathIntensity(data.accuracy);
|
|
5111
5195
|
(_b3 = this.pageBreather) == null ? void 0 : _b3.update(data.accuracy, data.isMoving);
|
|
5112
|
-
|
|
5113
|
-
|
|
5196
|
+
const rhythmSignal = Math.max(data.accuracy, data.progress * 0.75);
|
|
5197
|
+
const hasLiveCycleSignal = data.moveDuration > 1200 || data.stillDuration > 900;
|
|
5198
|
+
if ((rhythmSignal > 0.08 || hasLiveCycleSignal) && this.threeRenderer) {
|
|
5199
|
+
this.threeRenderer.showProgressParticles(0.45 + rhythmSignal * 0.55);
|
|
5114
5200
|
} else if (this.threeRenderer) {
|
|
5115
5201
|
this.threeRenderer.fadeOutAmbientParticles();
|
|
5116
5202
|
}
|
|
@@ -5474,7 +5560,7 @@ class GameController {
|
|
|
5474
5560
|
(_d = this.narrative) == null ? void 0 : _d.showCelebration(congrats[eggIndex] ?? "egg collected");
|
|
5475
5561
|
await this._wait(3500);
|
|
5476
5562
|
(_e = this.narrative) == null ? void 0 : _e.clear();
|
|
5477
|
-
await this._wait(
|
|
5563
|
+
await this._wait(900);
|
|
5478
5564
|
if (eggIndex < 2) {
|
|
5479
5565
|
if (eggIndex === 0) {
|
|
5480
5566
|
this.fsm.transitionTo("stage2-intro");
|