easter-egg-quest 1.0.19 → 1.0.21

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.
@@ -65,10 +65,11 @@ const DEFAULT_SCRIPT = {
65
65
  "Every living thing knows it. You’ve been doing it your whole life without thinking."
66
66
  ],
67
67
  stage3Reactions: [
68
- "move for a few seconds",
69
- "then hold still for a moment",
70
- "don’t rush it",
71
- "alternate move and pause",
68
+ "one truth alone is not enough",
69
+ "the other alone is not enough either",
70
+ "what do all living things share?",
71
+ "you have known it since the beginning",
72
+ "listen to yourself",
72
73
  "in",
73
74
  "out"
74
75
  ],
@@ -1764,6 +1765,7 @@ class ThreeRenderer {
1764
1765
  this._greetingOverlay = null;
1765
1766
  this._trailParticles = [];
1766
1767
  this._ambientParticles = [];
1768
+ this._lastAmbientSpawnAt = 0;
1767
1769
  this._revealParticles = [];
1768
1770
  this._disposed = false;
1769
1771
  this._interactiveEgg = null;
@@ -2130,32 +2132,43 @@ class ThreeRenderer {
2130
2132
  }
2131
2133
  }
2132
2134
  }
2133
- /** Spawn gentle ambient particles to show stage progress. */
2134
- showProgressParticles(progress) {
2135
+ /** Spawn ambient particles to show stage progress. */
2136
+ showProgressParticles(progress, source = "default") {
2135
2137
  if (!this.THREE || !this.scene || progress < 0.05) return;
2136
2138
  const T = this.THREE;
2137
- if (Math.random() > progress * 0.3) return;
2138
- const angle = Math.random() * Math.PI * 2;
2139
- const radius = 2 + Math.random() * 2;
2140
- const x = Math.cos(angle) * radius;
2141
- const y = -2 + Math.random() * 4;
2142
- const size = 0.02 + Math.random() * 0.025;
2143
- const geo = this._createMiniEggGeo(T, size);
2144
- const mat = this._createMiniEggMat(T, 0);
2145
- const p = new T.Mesh(geo, mat);
2146
- p.position.set(x, y, -1 + Math.random() * 0.5);
2147
- p.rotation.set(
2148
- Math.random() * Math.PI,
2149
- Math.random() * Math.PI,
2150
- Math.random() * Math.PI
2151
- );
2152
- this.scene.add(p);
2153
- this._ambientParticles.push({
2154
- mesh: p,
2155
- life: 1,
2156
- vy: 0.2 + Math.random() * 0.3,
2157
- fadeIn: true
2158
- });
2139
+ const now = performance.now();
2140
+ const isRhythm = source === "rhythm";
2141
+ if (isRhythm) {
2142
+ const minGap = Math.max(80, 180 - progress * 120);
2143
+ if (now - this._lastAmbientSpawnAt < minGap) return;
2144
+ } else if (Math.random() > progress * 0.3) {
2145
+ return;
2146
+ }
2147
+ this._lastAmbientSpawnAt = now;
2148
+ const spawnCount = isRhythm && progress > 0.6 ? 2 : 1;
2149
+ for (let i = 0; i < spawnCount; i++) {
2150
+ const angle = Math.random() * Math.PI * 2;
2151
+ const radius = isRhythm ? 1.1 + Math.random() * 1.8 : 2 + Math.random() * 2;
2152
+ const x = Math.cos(angle) * radius;
2153
+ const y = isRhythm ? -2.8 - Math.random() * 0.9 : -2 + Math.random() * 4;
2154
+ const size = isRhythm ? 0.03 + Math.random() * 0.03 : 0.02 + Math.random() * 0.025;
2155
+ const geo = this._createMiniEggGeo(T, size);
2156
+ const mat = this._createMiniEggMat(T, 0);
2157
+ const p = new T.Mesh(geo, mat);
2158
+ p.position.set(x, y, -1 + Math.random() * 0.5);
2159
+ p.rotation.set(
2160
+ Math.random() * Math.PI,
2161
+ Math.random() * Math.PI,
2162
+ Math.random() * Math.PI
2163
+ );
2164
+ this.scene.add(p);
2165
+ this._ambientParticles.push({
2166
+ mesh: p,
2167
+ life: isRhythm ? 1.25 : 1,
2168
+ vy: isRhythm ? 0.45 + Math.random() * 0.35 : 0.2 + Math.random() * 0.3,
2169
+ fadeIn: true
2170
+ });
2171
+ }
2159
2172
  if (this._ambientParticles.length > 60) {
2160
2173
  const old = this._ambientParticles.shift();
2161
2174
  if (old) {
@@ -3652,7 +3665,7 @@ const _ResultsRenderer = class _ResultsRenderer {
3652
3665
  display: "flex",
3653
3666
  alignItems: "center",
3654
3667
  justifyContent: "center",
3655
- pointerEvents: "none"
3668
+ pointerEvents: "auto"
3656
3669
  });
3657
3670
  document.body.appendChild(this.host);
3658
3671
  this.shadow = this.host.attachShadow({ mode: "closed" });
@@ -4778,10 +4791,10 @@ class RhythmStage {
4778
4791
  this._lastPhaseCount = 0;
4779
4792
  this._bestAccuracy = 0;
4780
4793
  this._lastGuideTime = 0;
4781
- this.IDEAL_MOVE_MIN = 1800;
4782
- this.IDEAL_MOVE_MAX = 5e3;
4783
- this.IDEAL_PAUSE_MIN = 1200;
4784
- this.IDEAL_PAUSE_MAX = 4e3;
4794
+ this.IDEAL_MOVE_MIN = 900;
4795
+ this.IDEAL_MOVE_MAX = 4200;
4796
+ this.IDEAL_PAUSE_MIN = 600;
4797
+ this.IDEAL_PAUSE_MAX = 2800;
4785
4798
  this.config = config;
4786
4799
  this.script = script;
4787
4800
  this.input = input;
@@ -4792,7 +4805,7 @@ class RhythmStage {
4792
4805
  this._attempts = 1;
4793
4806
  this._goodCycles = 0;
4794
4807
  this._totalCycles = 0;
4795
- this.input.configurePhaseDetection({ stillThresholdMs: 900, minPhaseMs: 500 });
4808
+ this.input.configurePhaseDetection({ stillThresholdMs: 450, minPhaseMs: 350 });
4796
4809
  this.input.resetPhases();
4797
4810
  this.input.resetCounts();
4798
4811
  this.bus.emit("narrative:clear");
@@ -4812,7 +4825,6 @@ class RhythmStage {
4812
4825
  this.input.resetPhases();
4813
4826
  this._lastPhaseCount = this.input.phases.length;
4814
4827
  this._introPlayed = true;
4815
- this.bus.emit("narrative:show", "move for a few seconds, then hold still");
4816
4828
  this._lastGuideTime = Date.now();
4817
4829
  }
4818
4830
  update(_dt) {
@@ -4828,7 +4840,7 @@ class RhythmStage {
4828
4840
  if (isGood) {
4829
4841
  this._goodCycles++;
4830
4842
  } else {
4831
- this._goodCycles = Math.max(0, this._goodCycles - 1);
4843
+ this._goodCycles = Math.max(0, this._goodCycles - 0.5);
4832
4844
  this._showReaction();
4833
4845
  }
4834
4846
  this._lastPhaseCount += 2;
@@ -4845,11 +4857,12 @@ class RhythmStage {
4845
4857
  isMoving: this.input.snapshot.isMoving,
4846
4858
  progress,
4847
4859
  moveDuration: this.input.moveDuration,
4848
- stillDuration: this.input.stillDuration
4860
+ stillDuration: this.input.stillDuration,
4861
+ liveConfidence: this._getLiveConfidence()
4849
4862
  });
4850
4863
  if (accuracy === 0 && Date.now() - this._lastGuideTime > 12e3) {
4851
4864
  this._lastGuideTime = Date.now();
4852
- this.bus.emit("narrative:show", "try this: move 2-4s, then stay still 1-3s");
4865
+ this._showReaction();
4853
4866
  }
4854
4867
  if (this._goodCycles >= requiredCycles) {
4855
4868
  this._status = "complete";
@@ -4878,6 +4891,21 @@ class RhythmStage {
4878
4891
  const stillOk = stillPhase.duration >= this.IDEAL_PAUSE_MIN && stillPhase.duration <= this.IDEAL_PAUSE_MAX;
4879
4892
  return moveOk && stillOk;
4880
4893
  }
4894
+ _getLiveConfidence() {
4895
+ if (this.input.snapshot.isMoving) {
4896
+ return this._scoreDuration(this.input.moveDuration, this.IDEAL_MOVE_MIN, this.IDEAL_MOVE_MAX);
4897
+ }
4898
+ return this._scoreDuration(this.input.stillDuration, this.IDEAL_PAUSE_MIN, this.IDEAL_PAUSE_MAX);
4899
+ }
4900
+ _scoreDuration(duration, idealMin, idealMax) {
4901
+ if (duration <= 0) return 0;
4902
+ if (duration >= idealMin && duration <= idealMax) return 1;
4903
+ const tolerance = Math.max(300, Math.round((idealMax - idealMin) * 0.35));
4904
+ if (duration < idealMin) {
4905
+ return Math.max(0, 1 - (idealMin - duration) / tolerance);
4906
+ }
4907
+ return Math.max(0, 1 - (duration - idealMax) / tolerance);
4908
+ }
4881
4909
  _showReaction() {
4882
4910
  const now = Date.now();
4883
4911
  if (now - this._lastNarrativeTime > 12e3) {
@@ -5017,7 +5045,7 @@ class PageBreather {
5017
5045
  this._overlay.style.opacity = String(Math.min(0.9, opacity));
5018
5046
  this._overlay.style.transform = `scale(${1 + wave * 0.015})`;
5019
5047
  if (this._label) {
5020
- this._label.textContent = shouldMove ? "Move" : "Hold still";
5048
+ this._label.textContent = shouldMove ? "In" : "Out";
5021
5049
  this._label.style.opacity = shouldMove === this._isUserMoving ? "0.98" : "0.82";
5022
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)";
5023
5051
  this._label.style.borderColor = this._inSync ? "rgba(212,165,80,0.55)" : "rgba(255,255,255,0.12)";
@@ -5063,7 +5091,7 @@ class PageBreather {
5063
5091
  "box-shadow:0 0 20px rgba(255,255,255,0.08)",
5064
5092
  "opacity:0.78"
5065
5093
  ].join(";");
5066
- label.textContent = "Move";
5094
+ label.textContent = "In";
5067
5095
  document.body.appendChild(label);
5068
5096
  this._label = label;
5069
5097
  this._tick();
@@ -5193,10 +5221,9 @@ class GameController {
5193
5221
  var _a3, _b3;
5194
5222
  (_a3 = this.eggRenderer) == null ? void 0 : _a3.setBreathIntensity(data.accuracy);
5195
5223
  (_b3 = this.pageBreather) == null ? void 0 : _b3.update(data.accuracy, data.isMoving);
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);
5224
+ const rhythmSignal = Math.max(data.accuracy, data.progress * 0.75, data.liveConfidence * 0.85);
5225
+ if (rhythmSignal > 0.18 && this.threeRenderer) {
5226
+ this.threeRenderer.showProgressParticles(0.3 + rhythmSignal * 0.7, "rhythm");
5200
5227
  } else if (this.threeRenderer) {
5201
5228
  this.threeRenderer.fadeOutAmbientParticles();
5202
5229
  }