tejasashinde commited on
Commit
b817c43
·
verified ·
1 Parent(s): 16a0ee3

distractor sparkle and progressive peek limit

Browse files
Files changed (1) hide show
  1. app.py +136 -4
app.py CHANGED
@@ -7791,6 +7791,8 @@ class MatchWiseApp {
7791
  this.peekGlowTimer = null;
7792
  this.livesGlowTimer = null;
7793
  this.wrongMatchTimer = null;
 
 
7794
  this.challengeStarted = false;
7795
  this.challengeSelectionLocked = false;
7796
  this.challengeResultIndex = null;
@@ -7818,6 +7820,7 @@ class MatchWiseApp {
7818
  this.installViewportHandlers();
7819
  this.hideResetConfirm();
7820
  this.hideGameOverModal();
 
7821
  this.hideHowToPlayModal();
7822
  if (this.pageMode === 'start') {
7823
  if (this.startNote) {
@@ -7925,6 +7928,32 @@ class MatchWiseApp {
7925
  -webkit-text-fill-color: transparent;
7926
  }
7927
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7928
  @keyframes popBounceRuntime {
7929
  0% {
7930
  transform: translate(-50%, -50%) scale(0) rotate(-15deg);
@@ -8720,9 +8749,100 @@ class MatchWiseApp {
8720
  }, 850);
8721
  }
8722
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8723
  updateHintsDisplay() {
 
8724
  if (this.hintCountEl) {
8725
- this.hintCountEl.textContent = `${this.hints} / ${this.maxHints}`;
8726
  }
8727
 
8728
  const currentHints = Number(this.hints || 0);
@@ -9264,6 +9384,7 @@ class MatchWiseApp {
9264
  this.challengeCorrectIndex = null;
9265
  }
9266
 
 
9267
  this.renderCards();
9268
  this.updateHud();
9269
  this.updateCardSize();
@@ -9512,6 +9633,11 @@ class MatchWiseApp {
9512
  if (this.nextBtn) {
9513
  this.nextBtn.disabled = status !== 'complete';
9514
  }
 
 
 
 
 
9515
  this.updateHintsDisplay();
9516
  this.syncControlsVisibility();
9517
  }
@@ -9534,6 +9660,7 @@ class MatchWiseApp {
9534
  this.state.status = 'preview';
9535
  this.state.level_complete = false;
9536
  this.state.game_over = false;
 
9537
  this.applyStateStyle();
9538
  this.setStatus(
9539
  'Memorize the cards before they flip!',
@@ -9594,6 +9721,7 @@ class MatchWiseApp {
9594
  this.state.status = 'preview';
9595
  this.state.level_complete = false;
9596
  this.state.game_over = false;
 
9597
 
9598
  if (this.overlay) {
9599
  this.overlay.style.display = 'flex';
@@ -9871,7 +9999,8 @@ class MatchWiseApp {
9871
  this.state.performance_meter = 0;
9872
  this.state.challenge_due = false;
9873
  }
9874
- const shouldAwardPeek = this.hints < this.maxHints;
 
9875
  this.showFeedbackOverlay(
9876
  'LEVEL COMPLETE',
9877
  isChallengeBoard ? 'Level Complete!\n+1 Life!' : (shouldAwardPeek ? 'Level Complete!\n+1 Peek earned!' : 'Level Complete!'),
@@ -9896,7 +10025,7 @@ class MatchWiseApp {
9896
  pairFact || featuredFact || this.state.theme || `Bonus ready for level ${this.state.level}.`
9897
  );
9898
  if (!isChallengeBoard) {
9899
- this.hints = Math.min(this.maxHints, this.hints + 1);
9900
  this.state.hints = this.hints;
9901
  }
9902
  this.updateHud();
@@ -9913,7 +10042,7 @@ class MatchWiseApp {
9913
  }
9914
  const hintMessage = shouldAwardPeek
9915
  ? '+1 peek earned!'
9916
- : `Max peeks earned: ${this.hints} / ${this.maxHints}`;
9917
  const completionDetails = [featuredFact, hintMessage].filter(Boolean).join('\n');
9918
  this.setStatus(
9919
  this.state.victory_message || 'Level cleared. Click NEXT LEVEL to continue.',
@@ -9990,6 +10119,7 @@ class MatchWiseApp {
9990
 
9991
  async useHint() {
9992
  if (this.state.status !== 'playing') return;
 
9993
  if (this.hinting) return;
9994
  if (this.hints <= 0) {
9995
  this.setStatus('No peeks left!', 'error', '');
@@ -10072,6 +10202,7 @@ class MatchWiseApp {
10072
  : Number(this.state.previous_level_time_seconds || 0);
10073
  this.state.win_streak = 0;
10074
  this.resetComboFeedback();
 
10075
  this.locked = true;
10076
  this.applyStateStyle();
10077
  this.setStatus(
@@ -10242,6 +10373,7 @@ class MatchWiseApp {
10242
  await this.refreshLeaderboardData();
10243
  this.hideResetConfirm();
10244
  this.hideGameOverModal();
 
10245
  this.showTransitionOverlay('🤖 Starting New Game', 'Resetting the board and preparing a fresh run...');
10246
  if (this.nextBtn) this.nextBtn.disabled = true;
10247
 
 
7791
  this.peekGlowTimer = null;
7792
  this.livesGlowTimer = null;
7793
  this.wrongMatchTimer = null;
7794
+ this.distractorSparkleTimer = null;
7795
+ this.distractorSparkleClearTimer = null;
7796
  this.challengeStarted = false;
7797
  this.challengeSelectionLocked = false;
7798
  this.challengeResultIndex = null;
 
7820
  this.installViewportHandlers();
7821
  this.hideResetConfirm();
7822
  this.hideGameOverModal();
7823
+ this.stopDistractorSparkles();
7824
  this.hideHowToPlayModal();
7825
  if (this.pageMode === 'start') {
7826
  if (this.startNote) {
 
7928
  -webkit-text-fill-color: transparent;
7929
  }
7930
 
7931
+ .memory-card.distractor-sparkle .memory-front::after {
7932
+ content: '✦';
7933
+ position: absolute;
7934
+ right: 9%;
7935
+ top: 8%;
7936
+ width: 28%;
7937
+ height: 28%;
7938
+ display: flex;
7939
+ align-items: center;
7940
+ justify-content: center;
7941
+ border-radius: 999px;
7942
+ color: rgba(255, 255, 255, 0.95);
7943
+ background: radial-gradient(circle, rgba(255,255,255,0.78), rgba(255,255,255,0.12) 58%, transparent 72%);
7944
+ filter: drop-shadow(0 0 10px rgba(255,255,255,0.85));
7945
+ font-size: clamp(14px, calc(var(--card-fit-size, var(--card-size, 150px)) * 0.18), 28px);
7946
+ pointer-events: none;
7947
+ animation: distractorSparkleRuntime 680ms ease-out forwards;
7948
+ z-index: 4;
7949
+ }
7950
+
7951
+ @keyframes distractorSparkleRuntime {
7952
+ 0% { transform: scale(0.35) rotate(-18deg); opacity: 0; }
7953
+ 28% { transform: scale(1.18) rotate(10deg); opacity: 1; }
7954
+ 100% { transform: scale(0.65) rotate(24deg); opacity: 0; }
7955
+ }
7956
+
7957
  @keyframes popBounceRuntime {
7958
  0% {
7959
  transform: translate(-50%, -50%) scale(0) rotate(-15deg);
 
8749
  }, 850);
8750
  }
8751
 
8752
+ getEffectiveMaxHints() {
8753
+ const baseMax = Math.max(1, Number(this.maxHints || this.state.max_hints || 5));
8754
+ const level = Math.max(1, Number(this.levelNumber || this.state.level || 1));
8755
+
8756
+ // Hint cap gently tightens as levels progress.
8757
+ // L1-5: normal cap, L6-10: -1, L11-16: -2, L17+: -3.
8758
+ const reduction = level <= 5 ? 0 : level <= 10 ? 1 : level <= 16 ? 2 : 3;
8759
+ return Math.max(1, baseMax - reduction);
8760
+ }
8761
+
8762
+ syncProgressiveHintLimit(announce = false) {
8763
+ const effectiveMaxHints = this.getEffectiveMaxHints();
8764
+ const previousHints = Number(this.hints || 0);
8765
+ this.hints = Math.max(0, Math.min(effectiveMaxHints, previousHints));
8766
+ this.state.hints = this.hints;
8767
+
8768
+ if (announce && previousHints > this.hints) {
8769
+ this.setStatus(
8770
+ 'Harder level: peeks are rarer now.',
8771
+ 'show',
8772
+ `Peek limit for this level: ${this.hints} / ${effectiveMaxHints}`
8773
+ );
8774
+ }
8775
+
8776
+ return effectiveMaxHints;
8777
+ }
8778
+
8779
+ getDistractorSparkleIntervalMs() {
8780
+ const level = Math.max(1, Number(this.levelNumber || this.state.level || 1));
8781
+
8782
+ // Very light distraction: slower early, slightly quicker later.
8783
+ return Math.max(1700, 3900 - Math.min(1700, (level - 1) * 95));
8784
+ }
8785
+
8786
+ triggerDistractorSparkleOnce() {
8787
+ if (!this.grid || this.state.status !== 'playing' || this.locked || this.state.game_over || this.state.level_complete) {
8788
+ return;
8789
+ }
8790
+
8791
+ const candidates = Array.from(this.grid.querySelectorAll('.memory-card:not(.matched):not(.flipped):not(.preview-locked)'));
8792
+ if (!candidates.length) return;
8793
+
8794
+ const card = candidates[Math.floor(Math.random() * candidates.length)];
8795
+ if (!card) return;
8796
+
8797
+ card.classList.remove('distractor-sparkle');
8798
+ void card.offsetWidth;
8799
+ card.classList.add('distractor-sparkle');
8800
+
8801
+ if (this.distractorSparkleClearTimer) {
8802
+ clearTimeout(this.distractorSparkleClearTimer);
8803
+ this.distractorSparkleClearTimer = null;
8804
+ }
8805
+ this.distractorSparkleClearTimer = setTimeout(() => {
8806
+ card.classList.remove('distractor-sparkle');
8807
+ this.distractorSparkleClearTimer = null;
8808
+ }, 740);
8809
+ }
8810
+
8811
+ startDistractorSparkles() {
8812
+ if (this.distractorSparkleTimer || !this.grid || this.state.status !== 'playing') {
8813
+ return;
8814
+ }
8815
+
8816
+ const interval = this.getDistractorSparkleIntervalMs();
8817
+ this.distractorSparkleTimer = setInterval(() => {
8818
+ if (window.__activeMemoryLevelId !== this.levelId || this.state.status !== 'playing') {
8819
+ this.stopDistractorSparkles();
8820
+ return;
8821
+ }
8822
+ this.triggerDistractorSparkleOnce();
8823
+ }, interval);
8824
+ }
8825
+
8826
+ stopDistractorSparkles() {
8827
+ if (this.distractorSparkleTimer) {
8828
+ clearInterval(this.distractorSparkleTimer);
8829
+ this.distractorSparkleTimer = null;
8830
+ }
8831
+ if (this.distractorSparkleClearTimer) {
8832
+ clearTimeout(this.distractorSparkleClearTimer);
8833
+ this.distractorSparkleClearTimer = null;
8834
+ }
8835
+ if (this.grid) {
8836
+ this.grid.querySelectorAll('.memory-card.distractor-sparkle').forEach((card) => {
8837
+ card.classList.remove('distractor-sparkle');
8838
+ });
8839
+ }
8840
+ }
8841
+
8842
  updateHintsDisplay() {
8843
+ const effectiveMaxHints = this.syncProgressiveHintLimit(false);
8844
  if (this.hintCountEl) {
8845
+ this.hintCountEl.textContent = `${this.hints} / ${effectiveMaxHints}`;
8846
  }
8847
 
8848
  const currentHints = Number(this.hints || 0);
 
9384
  this.challengeCorrectIndex = null;
9385
  }
9386
 
9387
+ this.stopDistractorSparkles();
9388
  this.renderCards();
9389
  this.updateHud();
9390
  this.updateCardSize();
 
9633
  if (this.nextBtn) {
9634
  this.nextBtn.disabled = status !== 'complete';
9635
  }
9636
+ if (status === 'playing') {
9637
+ this.startDistractorSparkles();
9638
+ } else {
9639
+ this.stopDistractorSparkles();
9640
+ }
9641
  this.updateHintsDisplay();
9642
  this.syncControlsVisibility();
9643
  }
 
9660
  this.state.status = 'preview';
9661
  this.state.level_complete = false;
9662
  this.state.game_over = false;
9663
+ this.syncProgressiveHintLimit(true);
9664
  this.applyStateStyle();
9665
  this.setStatus(
9666
  'Memorize the cards before they flip!',
 
9721
  this.state.status = 'preview';
9722
  this.state.level_complete = false;
9723
  this.state.game_over = false;
9724
+ this.syncProgressiveHintLimit(true);
9725
 
9726
  if (this.overlay) {
9727
  this.overlay.style.display = 'flex';
 
9999
  this.state.performance_meter = 0;
10000
  this.state.challenge_due = false;
10001
  }
10002
+ const effectiveMaxHints = this.getEffectiveMaxHints();
10003
+ const shouldAwardPeek = this.hints < effectiveMaxHints;
10004
  this.showFeedbackOverlay(
10005
  'LEVEL COMPLETE',
10006
  isChallengeBoard ? 'Level Complete!\n+1 Life!' : (shouldAwardPeek ? 'Level Complete!\n+1 Peek earned!' : 'Level Complete!'),
 
10025
  pairFact || featuredFact || this.state.theme || `Bonus ready for level ${this.state.level}.`
10026
  );
10027
  if (!isChallengeBoard) {
10028
+ this.hints = Math.min(effectiveMaxHints, this.hints + 1);
10029
  this.state.hints = this.hints;
10030
  }
10031
  this.updateHud();
 
10042
  }
10043
  const hintMessage = shouldAwardPeek
10044
  ? '+1 peek earned!'
10045
+ : `Max peeks earned for this level: ${this.hints} / ${effectiveMaxHints}`;
10046
  const completionDetails = [featuredFact, hintMessage].filter(Boolean).join('\n');
10047
  this.setStatus(
10048
  this.state.victory_message || 'Level cleared. Click NEXT LEVEL to continue.',
 
10119
 
10120
  async useHint() {
10121
  if (this.state.status !== 'playing') return;
10122
+ this.syncProgressiveHintLimit(false);
10123
  if (this.hinting) return;
10124
  if (this.hints <= 0) {
10125
  this.setStatus('No peeks left!', 'error', '');
 
10202
  : Number(this.state.previous_level_time_seconds || 0);
10203
  this.state.win_streak = 0;
10204
  this.resetComboFeedback();
10205
+ this.stopDistractorSparkles();
10206
  this.locked = true;
10207
  this.applyStateStyle();
10208
  this.setStatus(
 
10373
  await this.refreshLeaderboardData();
10374
  this.hideResetConfirm();
10375
  this.hideGameOverModal();
10376
+ this.stopDistractorSparkles();
10377
  this.showTransitionOverlay('🤖 Starting New Game', 'Resetting the board and preparing a fresh run...');
10378
  if (this.nextBtn) this.nextBtn.disabled = true;
10379