easter-egg-quest 1.0.21 → 1.0.23
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 +111 -52
- package/dist/easter-egg-quest.umd.js +1 -1
- package/dist/types/rendering/PageBreather.d.ts +4 -4
- package/dist/types/rendering/ThreeRenderer.d.ts +1 -0
- package/dist/types/scoring/Persistence.d.ts +2 -0
- package/dist/types/stages/RhythmStage.d.ts +2 -0
- package/package.json +1 -1
|
@@ -142,7 +142,7 @@ function resolveConfig(raw = {}) {
|
|
|
142
142
|
stageDurations: {
|
|
143
143
|
stillnessMs: clampDuration((_i = raw.stageDurations) == null ? void 0 : _i.stillnessMs, 2e4),
|
|
144
144
|
motionMs: clampDuration((_j = raw.stageDurations) == null ? void 0 : _j.motionMs, 2e4),
|
|
145
|
-
rhythmCycles: clampInt((_k = raw.stageDurations) == null ? void 0 : _k.rhythmCycles,
|
|
145
|
+
rhythmCycles: clampInt((_k = raw.stageDurations) == null ? void 0 : _k.rhythmCycles, 3, 1, 50)
|
|
146
146
|
},
|
|
147
147
|
renderer: raw.renderer ?? "auto",
|
|
148
148
|
scoring: {
|
|
@@ -2178,6 +2178,41 @@ class ThreeRenderer {
|
|
|
2178
2178
|
}
|
|
2179
2179
|
}
|
|
2180
2180
|
}
|
|
2181
|
+
burstRhythmParticles(progress, cycles) {
|
|
2182
|
+
if (!this.THREE || !this.scene) return;
|
|
2183
|
+
const T = this.THREE;
|
|
2184
|
+
const burstSize = Math.min(14, 6 + Math.floor(progress * 6) + Math.min(3, Math.floor(cycles)));
|
|
2185
|
+
for (let i = 0; i < burstSize; i++) {
|
|
2186
|
+
const angle = Math.PI * 2 * i / burstSize + (Math.random() - 0.5) * 0.35;
|
|
2187
|
+
const radius = 0.45 + Math.random() * 2.15;
|
|
2188
|
+
const x = Math.cos(angle) * radius;
|
|
2189
|
+
const y = -2.95 - Math.random() * 0.75;
|
|
2190
|
+
const size = 0.036 + Math.random() * 0.038;
|
|
2191
|
+
const geo = this._createMiniEggGeo(T, size);
|
|
2192
|
+
const mat = this._createMiniEggMat(T, 0.18 + Math.random() * 0.12);
|
|
2193
|
+
const particle = new T.Mesh(geo, mat);
|
|
2194
|
+
particle.position.set(x, y, -1 + Math.random() * 0.8);
|
|
2195
|
+
particle.rotation.set(
|
|
2196
|
+
Math.random() * Math.PI,
|
|
2197
|
+
Math.random() * Math.PI,
|
|
2198
|
+
Math.random() * Math.PI
|
|
2199
|
+
);
|
|
2200
|
+
this.scene.add(particle);
|
|
2201
|
+
this._ambientParticles.push({
|
|
2202
|
+
mesh: particle,
|
|
2203
|
+
life: 1.7 + Math.random() * 0.45,
|
|
2204
|
+
vy: 0.85 + Math.random() * 0.65,
|
|
2205
|
+
fadeIn: true
|
|
2206
|
+
});
|
|
2207
|
+
}
|
|
2208
|
+
while (this._ambientParticles.length > 120) {
|
|
2209
|
+
const old = this._ambientParticles.shift();
|
|
2210
|
+
if (!old) break;
|
|
2211
|
+
this.scene.remove(old.mesh);
|
|
2212
|
+
old.mesh.geometry.dispose();
|
|
2213
|
+
old.mesh.material.dispose();
|
|
2214
|
+
}
|
|
2215
|
+
}
|
|
2181
2216
|
/** Mark all ambient particles for rapid fade-out (rhythm lost). */
|
|
2182
2217
|
fadeOutAmbientParticles() {
|
|
2183
2218
|
for (const p of this._ambientParticles) {
|
|
@@ -4500,6 +4535,7 @@ class ScoringEngine {
|
|
|
4500
4535
|
}
|
|
4501
4536
|
const STORAGE_KEY = "eeq_scores";
|
|
4502
4537
|
const PROGRESS_KEY = "eeq_progress";
|
|
4538
|
+
const COMPLETION_KEY = "eeq_completed";
|
|
4503
4539
|
class Persistence {
|
|
4504
4540
|
constructor(enabled) {
|
|
4505
4541
|
this._enabled = enabled;
|
|
@@ -4572,6 +4608,22 @@ class Persistence {
|
|
|
4572
4608
|
} catch {
|
|
4573
4609
|
}
|
|
4574
4610
|
}
|
|
4611
|
+
markCompleted() {
|
|
4612
|
+
if (!this._enabled) return;
|
|
4613
|
+
try {
|
|
4614
|
+
localStorage.setItem(COMPLETION_KEY, "1");
|
|
4615
|
+
localStorage.removeItem(PROGRESS_KEY);
|
|
4616
|
+
} catch {
|
|
4617
|
+
}
|
|
4618
|
+
}
|
|
4619
|
+
isCompleted() {
|
|
4620
|
+
if (!this._enabled) return false;
|
|
4621
|
+
try {
|
|
4622
|
+
return localStorage.getItem(COMPLETION_KEY) === "1";
|
|
4623
|
+
} catch {
|
|
4624
|
+
return false;
|
|
4625
|
+
}
|
|
4626
|
+
}
|
|
4575
4627
|
}
|
|
4576
4628
|
class StillnessStage {
|
|
4577
4629
|
constructor(config, script, input, bus) {
|
|
@@ -4791,6 +4843,7 @@ class RhythmStage {
|
|
|
4791
4843
|
this._lastPhaseCount = 0;
|
|
4792
4844
|
this._bestAccuracy = 0;
|
|
4793
4845
|
this._lastGuideTime = 0;
|
|
4846
|
+
this._lastGoodCycleAt = 0;
|
|
4794
4847
|
this.IDEAL_MOVE_MIN = 900;
|
|
4795
4848
|
this.IDEAL_MOVE_MAX = 4200;
|
|
4796
4849
|
this.IDEAL_PAUSE_MIN = 600;
|
|
@@ -4839,6 +4892,11 @@ class RhythmStage {
|
|
|
4839
4892
|
this._totalCycles++;
|
|
4840
4893
|
if (isGood) {
|
|
4841
4894
|
this._goodCycles++;
|
|
4895
|
+
this._lastGoodCycleAt = Date.now();
|
|
4896
|
+
this.bus.emit("rhythm:cycle", {
|
|
4897
|
+
progress: Math.min(1, this._goodCycles / requiredCycles),
|
|
4898
|
+
cycles: this._goodCycles
|
|
4899
|
+
});
|
|
4842
4900
|
} else {
|
|
4843
4901
|
this._goodCycles = Math.max(0, this._goodCycles - 0.5);
|
|
4844
4902
|
this._showReaction();
|
|
@@ -4851,6 +4909,9 @@ class RhythmStage {
|
|
|
4851
4909
|
const accuracy = this._totalCycles > 0 ? this._goodCycles / this._totalCycles : 0;
|
|
4852
4910
|
if (accuracy > this._bestAccuracy) this._bestAccuracy = accuracy;
|
|
4853
4911
|
const progress = Math.min(1, this._goodCycles / requiredCycles);
|
|
4912
|
+
const targetPhase = this._getTargetPhase();
|
|
4913
|
+
const phaseMatch = this.input.snapshot.isMoving === (targetPhase === "move");
|
|
4914
|
+
const cycleSignal = this._lastGoodCycleAt > 0 ? Math.max(0, 1 - (Date.now() - this._lastGoodCycleAt) / 2200) : 0;
|
|
4854
4915
|
this.bus.emit("stage:progress", progress);
|
|
4855
4916
|
this.bus.emit("rhythm:breathe", {
|
|
4856
4917
|
accuracy,
|
|
@@ -4858,7 +4919,10 @@ class RhythmStage {
|
|
|
4858
4919
|
progress,
|
|
4859
4920
|
moveDuration: this.input.moveDuration,
|
|
4860
4921
|
stillDuration: this.input.stillDuration,
|
|
4861
|
-
liveConfidence: this._getLiveConfidence()
|
|
4922
|
+
liveConfidence: this._getLiveConfidence(),
|
|
4923
|
+
targetPhase,
|
|
4924
|
+
phaseMatch,
|
|
4925
|
+
cycleSignal
|
|
4862
4926
|
});
|
|
4863
4927
|
if (accuracy === 0 && Date.now() - this._lastGuideTime > 12e3) {
|
|
4864
4928
|
this._lastGuideTime = Date.now();
|
|
@@ -4897,6 +4961,12 @@ class RhythmStage {
|
|
|
4897
4961
|
}
|
|
4898
4962
|
return this._scoreDuration(this.input.stillDuration, this.IDEAL_PAUSE_MIN, this.IDEAL_PAUSE_MAX);
|
|
4899
4963
|
}
|
|
4964
|
+
_getTargetPhase() {
|
|
4965
|
+
if (this.input.snapshot.isMoving) {
|
|
4966
|
+
return this.input.moveDuration >= this.IDEAL_MOVE_MIN ? "still" : "move";
|
|
4967
|
+
}
|
|
4968
|
+
return this.input.stillDuration >= this.IDEAL_PAUSE_MIN ? "move" : "still";
|
|
4969
|
+
}
|
|
4900
4970
|
_scoreDuration(duration, idealMin, idealMax) {
|
|
4901
4971
|
if (duration <= 0) return 0;
|
|
4902
4972
|
if (duration >= idealMin && duration <= idealMax) return 1;
|
|
@@ -5024,32 +5094,26 @@ class PageBreather {
|
|
|
5024
5094
|
this._targetIntensity = 0;
|
|
5025
5095
|
this._rafId = 0;
|
|
5026
5096
|
this._overlay = null;
|
|
5027
|
-
this._label = null;
|
|
5028
5097
|
this._startTime = 0;
|
|
5029
5098
|
this._isUserMoving = false;
|
|
5030
5099
|
this._inSync = false;
|
|
5031
5100
|
this._accuracy = 0;
|
|
5032
|
-
this.
|
|
5101
|
+
this._targetPhase = "move";
|
|
5102
|
+
this._phaseMatch = false;
|
|
5103
|
+
this._signal = 0;
|
|
5033
5104
|
this._tick = () => {
|
|
5034
5105
|
if (!this._active || !this._overlay) return;
|
|
5035
5106
|
this._rafId = requestAnimationFrame(this._tick);
|
|
5036
5107
|
this._intensity += (this._targetIntensity - this._intensity) * 0.06;
|
|
5037
5108
|
const elapsed = Date.now() - this._startTime;
|
|
5038
|
-
const
|
|
5039
|
-
const
|
|
5040
|
-
|
|
5041
|
-
|
|
5042
|
-
const
|
|
5043
|
-
const
|
|
5044
|
-
const opacity = this._intensity * (0.18 + baseOpacity + (1 - wave) * 0.12 + syncBonus);
|
|
5109
|
+
const wave = (Math.sin(elapsed * 4e-3) + 1) * 0.5;
|
|
5110
|
+
const shouldMove = this._targetPhase === "move";
|
|
5111
|
+
this._inSync = this._phaseMatch && this._signal > 0.15;
|
|
5112
|
+
const baseOpacity = shouldMove ? 0.12 + wave * 0.08 : 0.32 + wave * 0.18;
|
|
5113
|
+
const syncBonus = this._inSync ? 0.12 + this._signal * 0.18 : 0;
|
|
5114
|
+
const opacity = this._intensity * (baseOpacity + syncBonus);
|
|
5045
5115
|
this._overlay.style.opacity = String(Math.min(0.9, opacity));
|
|
5046
|
-
this._overlay.style.transform = `scale(${1 + wave * 0.
|
|
5047
|
-
if (this._label) {
|
|
5048
|
-
this._label.textContent = shouldMove ? "In" : "Out";
|
|
5049
|
-
this._label.style.opacity = shouldMove === this._isUserMoving ? "0.98" : "0.82";
|
|
5050
|
-
this._label.style.boxShadow = this._inSync ? "0 0 28px rgba(212,165,80,0.4)" : "0 0 20px rgba(255,255,255,0.08)";
|
|
5051
|
-
this._label.style.borderColor = this._inSync ? "rgba(212,165,80,0.55)" : "rgba(255,255,255,0.12)";
|
|
5052
|
-
}
|
|
5116
|
+
this._overlay.style.transform = `scale(${1 + wave * (shouldMove ? 0.01 : 0.02)})`;
|
|
5053
5117
|
};
|
|
5054
5118
|
}
|
|
5055
5119
|
start() {
|
|
@@ -5071,55 +5135,36 @@ class PageBreather {
|
|
|
5071
5135
|
].join(";");
|
|
5072
5136
|
document.body.appendChild(el);
|
|
5073
5137
|
this._overlay = el;
|
|
5074
|
-
const label = document.createElement("div");
|
|
5075
|
-
label.style.cssText = [
|
|
5076
|
-
"position:fixed",
|
|
5077
|
-
"left:50%",
|
|
5078
|
-
"top:18%",
|
|
5079
|
-
"transform:translateX(-50%)",
|
|
5080
|
-
"pointer-events:none",
|
|
5081
|
-
"z-index:999981",
|
|
5082
|
-
"padding:8px 14px",
|
|
5083
|
-
"border-radius:999px",
|
|
5084
|
-
"font-family:Georgia, Times New Roman, serif",
|
|
5085
|
-
"font-size:12px",
|
|
5086
|
-
"letter-spacing:0.12em",
|
|
5087
|
-
"text-transform:uppercase",
|
|
5088
|
-
"color:rgba(255,248,232,0.95)",
|
|
5089
|
-
"background:rgba(8,10,18,0.36)",
|
|
5090
|
-
"border:1px solid rgba(255,255,255,0.12)",
|
|
5091
|
-
"box-shadow:0 0 20px rgba(255,255,255,0.08)",
|
|
5092
|
-
"opacity:0.78"
|
|
5093
|
-
].join(";");
|
|
5094
|
-
label.textContent = "In";
|
|
5095
|
-
document.body.appendChild(label);
|
|
5096
|
-
this._label = label;
|
|
5097
5138
|
this._tick();
|
|
5098
5139
|
}
|
|
5099
|
-
update(accuracy, isMoving) {
|
|
5140
|
+
update(accuracy, isMoving, targetPhase, phaseMatch, signal) {
|
|
5100
5141
|
if (!this._active) return;
|
|
5101
5142
|
this._isUserMoving = isMoving;
|
|
5102
5143
|
this._accuracy = accuracy;
|
|
5103
|
-
|
|
5104
|
-
this.
|
|
5144
|
+
this._targetPhase = targetPhase;
|
|
5145
|
+
this._phaseMatch = phaseMatch;
|
|
5146
|
+
this._signal = signal;
|
|
5147
|
+
const raw = Math.max(0, Math.min(1, Math.max(accuracy, signal) / 0.8));
|
|
5148
|
+
this._targetIntensity = 0.38 + raw * 0.62;
|
|
5105
5149
|
}
|
|
5106
5150
|
/** Whether the user is currently in sync with the breathing rhythm. */
|
|
5107
5151
|
isInSync() {
|
|
5108
5152
|
return this._inSync;
|
|
5109
5153
|
}
|
|
5110
5154
|
stop() {
|
|
5111
|
-
var _a2
|
|
5155
|
+
var _a2;
|
|
5112
5156
|
if (!this._active) return;
|
|
5113
5157
|
this._active = false;
|
|
5114
5158
|
cancelAnimationFrame(this._rafId);
|
|
5115
5159
|
(_a2 = this._overlay) == null ? void 0 : _a2.remove();
|
|
5116
5160
|
this._overlay = null;
|
|
5117
|
-
(_b2 = this._label) == null ? void 0 : _b2.remove();
|
|
5118
|
-
this._label = null;
|
|
5119
5161
|
this._intensity = 0;
|
|
5120
5162
|
this._targetIntensity = 0;
|
|
5121
5163
|
this._inSync = false;
|
|
5122
5164
|
this._accuracy = 0;
|
|
5165
|
+
this._targetPhase = "move";
|
|
5166
|
+
this._phaseMatch = false;
|
|
5167
|
+
this._signal = 0;
|
|
5123
5168
|
}
|
|
5124
5169
|
destroy() {
|
|
5125
5170
|
this.stop();
|
|
@@ -5197,6 +5242,10 @@ class GameController {
|
|
|
5197
5242
|
// ─── Lifecycle ─────────────────────────────────────────────────────────
|
|
5198
5243
|
async init() {
|
|
5199
5244
|
var _a2, _b2;
|
|
5245
|
+
if (this.persistence.isCompleted()) {
|
|
5246
|
+
this._destroyed = true;
|
|
5247
|
+
return;
|
|
5248
|
+
}
|
|
5200
5249
|
this.fsm.onTransition((from, to) => this._onTransition(from, to));
|
|
5201
5250
|
this.bus.on("narrative:show", (text) => {
|
|
5202
5251
|
var _a3;
|
|
@@ -5217,14 +5266,21 @@ class GameController {
|
|
|
5217
5266
|
var _a3;
|
|
5218
5267
|
(_a3 = this.eggRenderer) == null ? void 0 : _a3.addTrail(data.x, data.y, data.velocity);
|
|
5219
5268
|
});
|
|
5269
|
+
this.bus.on("rhythm:cycle", (data) => {
|
|
5270
|
+
if (!this.threeRenderer) return;
|
|
5271
|
+
this.threeRenderer.burstRhythmParticles(data.progress, data.cycles);
|
|
5272
|
+
});
|
|
5220
5273
|
this.bus.on("rhythm:breathe", (data) => {
|
|
5221
5274
|
var _a3, _b3;
|
|
5222
5275
|
(_a3 = this.eggRenderer) == null ? void 0 : _a3.setBreathIntensity(data.accuracy);
|
|
5223
|
-
(_b3 = this.pageBreather) == null ? void 0 : _b3.update(
|
|
5224
|
-
|
|
5225
|
-
|
|
5226
|
-
|
|
5227
|
-
|
|
5276
|
+
(_b3 = this.pageBreather) == null ? void 0 : _b3.update(
|
|
5277
|
+
data.accuracy,
|
|
5278
|
+
data.isMoving,
|
|
5279
|
+
data.targetPhase,
|
|
5280
|
+
data.phaseMatch,
|
|
5281
|
+
Math.max(data.liveConfidence * 0.55, data.cycleSignal)
|
|
5282
|
+
);
|
|
5283
|
+
if (data.cycleSignal <= 0.12 && this.threeRenderer) {
|
|
5228
5284
|
this.threeRenderer.fadeOutAmbientParticles();
|
|
5229
5285
|
}
|
|
5230
5286
|
});
|
|
@@ -5406,6 +5462,7 @@ class GameController {
|
|
|
5406
5462
|
// ─── Entry ─────────────────────────────────────────────────────────────
|
|
5407
5463
|
async _startEntry() {
|
|
5408
5464
|
var _a2;
|
|
5465
|
+
if (this.persistence.isCompleted()) return;
|
|
5409
5466
|
try {
|
|
5410
5467
|
if (localStorage.getItem("eeq_optout") === "1") return;
|
|
5411
5468
|
} catch {
|
|
@@ -5655,7 +5712,9 @@ class GameController {
|
|
|
5655
5712
|
this.results.show(
|
|
5656
5713
|
score,
|
|
5657
5714
|
() => {
|
|
5715
|
+
this.persistence.markCompleted();
|
|
5658
5716
|
this.destroy();
|
|
5717
|
+
window.location.reload();
|
|
5659
5718
|
}
|
|
5660
5719
|
);
|
|
5661
5720
|
(_c = (_b2 = this.config.callbacks).onComplete) == null ? void 0 : _c.call(_b2, score);
|