git » sparkle-arcade » commit 9694e80

apply micha's ideas

author Alan Dipert
2025-11-17 00:42:59 UTC
committer Alan Dipert
2025-11-17 00:42:59 UTC
parent 1845142d45318174d239698d495f1012d75ddac4

apply micha's ideas

fairy-finder/game.js +65 -3
fairy-finder/index.html +8 -0
fairy-finder/styles.css +62 -0

diff --git a/fairy-finder/game.js b/fairy-finder/game.js
index deacce0..12e957e 100644
--- a/fairy-finder/game.js
+++ b/fairy-finder/game.js
@@ -8,6 +8,9 @@
   const overlay = document.getElementById('overlay');
   const overlayButton = document.getElementById('overlay-btn');
   const overlayMessage = overlay.querySelector('p');
+  const collectionList = document.getElementById('collection-list');
+  const collectionCount = document.getElementById('collection-count');
+  const collectionEmpty = document.getElementById('collection-empty');
 
   const difficulties = {
     tiny: { size: 6, fairies: 5 },
@@ -25,7 +28,9 @@
     isActive: false,
     timer: 0,
     timerInterval: null,
+    overlayTimeout: null,
   };
+  const fairyCollection = [];
   const audio = createAudio();
 
   function stopTimer() {
@@ -165,6 +170,7 @@
     }
     if (game.revealedSafe === game.boardSize * game.boardSize - game.fairyTotal) {
       revealAllFairies(true);
+      collectFairies();
       finishGame(true);
     }
   }
@@ -253,10 +259,24 @@
     game.isActive = false;
     stopTimer();
     overlayMessage.textContent = hasWon
-      ? 'You found every fairy friend! Want to search again?'
+      ? 'You found every fairy friend! They fluttered into your satchel.'
       : 'A shy fairy fluttered out! Try again for a perfect sweep?';
-    overlayButton.textContent = hasWon ? 'Play again' : 'Try again';
-    overlay.classList.remove('hidden');
+    overlayButton.textContent = hasWon ? 'Search again' : 'Try again';
+    if (game.overlayTimeout) {
+      clearTimeout(game.overlayTimeout);
+      game.overlayTimeout = null;
+    }
+    const delay = hasWon ? 1400 : 0;
+    const showOverlay = () => {
+      overlay.classList.remove('hidden');
+      game.overlayTimeout = null;
+    };
+    if (delay === 0) {
+      showOverlay();
+    } else {
+      overlay.classList.add('hidden');
+      game.overlayTimeout = setTimeout(showOverlay, delay);
+    }
     playSound(hasWon ? 'win' : 'lose');
   }
 
@@ -285,6 +305,10 @@
 
   function startNewRound() {
     audio.resume();
+    if (game.overlayTimeout) {
+      clearTimeout(game.overlayTimeout);
+      game.overlayTimeout = null;
+    }
     prepareGame();
     overlay.classList.add('hidden');
   }
@@ -298,6 +322,44 @@
     if (audio) audio.play(type);
   }
 
+  function collectFairies() {
+    if (!collectionList) return;
+    const newFairies = [];
+    for (let r = 0; r < game.boardSize; r += 1) {
+      for (let c = 0; c < game.boardSize; c += 1) {
+        if (game.board[r][c].hasFairy) {
+          newFairies.push({ row: r, col: c });
+        }
+      }
+    }
+    if (!newFairies.length) return;
+    newFairies.forEach((fairy, index) => {
+      const fairyEl = document.createElement('span');
+      fairyEl.className = 'collection-fairy collection-fairy--new';
+      fairyEl.setAttribute('role', 'listitem');
+      fairyEl.textContent = getFairyEmoji();
+      fairyCollection.push(`${fairy.row}-${fairy.col}-${Date.now()}-${index}`);
+      collectionList.appendChild(fairyEl);
+      setTimeout(() => {
+        fairyEl.classList.remove('collection-fairy--new');
+      }, 700);
+    });
+    updateCollectionDisplay();
+  }
+
+  function updateCollectionDisplay() {
+    if (!collectionCount) return;
+    collectionCount.textContent = fairyCollection.length;
+    if (collectionEmpty) {
+      collectionEmpty.hidden = fairyCollection.length > 0;
+    }
+  }
+
+  function getFairyEmoji() {
+    const emojis = ['🧚', '🧚‍♀️', '🧚‍♂️'];
+    return emojis[Math.floor(Math.random() * emojis.length)];
+  }
+
   function createAudio() {
     const AudioCtx = window.AudioContext || window.webkitAudioContext;
     if (!AudioCtx) return null;
diff --git a/fairy-finder/index.html b/fairy-finder/index.html
index f7c9ec4..3f74f4b 100644
--- a/fairy-finder/index.html
+++ b/fairy-finder/index.html
@@ -39,6 +39,14 @@
       <aside class="instructions">
         <h2>How to play</h2>
         <p>Fairies are hiding in the garden! Tap a square to peek. Numbers show how many fairies are next door. Mark fairy spots with a flag using right-click or a long press.</p>
+        <div class="collection" aria-live="polite">
+          <div class="collection-header">
+            <h3>Fairy satchel</h3>
+            <span class="collection-count"><span id="collection-count">0</span> friends</span>
+          </div>
+          <p id="collection-empty" class="collection-empty">Win a round to invite the fairies you find.</p>
+          <div class="collection-list" id="collection-list" role="list"></div>
+        </div>
       </aside>
     </section>
   </main>
diff --git a/fairy-finder/styles.css b/fairy-finder/styles.css
index f8cba0e..9b8b9f4 100644
--- a/fairy-finder/styles.css
+++ b/fairy-finder/styles.css
@@ -204,6 +204,68 @@ select {
   margin: 0.2rem 0 0;
 }
 
+.collection {
+  margin-top: 1.5rem;
+  padding: 1rem 1.2rem;
+  border-radius: 1.2rem;
+  background: rgba(248, 218, 255, 0.45);
+  box-shadow: inset 0 0 15px rgba(255, 255, 255, 0.7);
+}
+
+.collection-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: baseline;
+  gap: 0.5rem;
+}
+
+.collection-header h3 {
+  margin: 0;
+  font-size: 1.1rem;
+  color: #f26dbd;
+}
+
+.collection-count {
+  font-weight: 700;
+  color: var(--text-dark);
+}
+
+.collection-empty {
+  margin: 0.5rem 0 0;
+  font-style: italic;
+  opacity: 0.85;
+}
+
+.collection-list {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 0.45rem;
+  margin-top: 0.7rem;
+  min-height: 1.8rem;
+}
+
+.collection-fairy {
+  font-size: 1.4rem;
+  filter: drop-shadow(0 4px 8px rgba(242, 109, 189, 0.4));
+  animation: fairy-bob 4s ease-in-out infinite;
+}
+
+.collection-fairy--new {
+  animation: fairy-collect 0.6s ease forwards;
+}
+
+@keyframes fairy-collect {
+  0% { transform: scale(0.3) translateY(12px); opacity: 0; }
+  60% { transform: scale(1.2) translateY(-4px); opacity: 1; }
+  100% { transform: scale(1) translateY(0); opacity: 1; }
+}
+
+@keyframes fairy-bob {
+  0% { transform: translateY(0); }
+  50% { transform: translateY(-3px); }
+  100% { transform: translateY(0); }
+}
+
 .overlay {
   position: fixed;
   inset: 0;