import React, { useState, useEffect } from 'react'; import { motion, AnimatePresence } from 'framer-motion'; import { GameShell } from '../components/ui/GameShell'; import { HelpCircle } from 'lucide-react'; interface GameProps { onExit: () => void; } // Extended Item Pool const ALL_ITEMS = [ '🐚', 'πŸ¦€', '🐠', 'πŸ™', '🐒', '🐳', '🐬', '🐑', '🦈', '🦐', 'πŸ¦‘', '🦞', 'πŸ‹', '🐊', 'πŸ¦†', 'βš“', 'β›΅', '🏝️', 'πŸ₯₯', '🍹', '🌊', 'πŸ„', '🏊', 'πŸ›Ÿ', 'πŸ—ΊοΈ', '🐟', 'πŸ–οΈ', 'πŸŒ‹', 'πŸ›Ά', '🚀', '🐑', '🦠', 'πŸͺΈ', 'πŸͺΌ', 'πŸ§‚' ]; const WIN_CONDITION = 30; interface ClickEffect { id: number; x: number; y: number; val: string; } export const MemoryShore: React.FC = ({ onExit }) => { return ( {({ onGameOver }) => ( )} ); }; const MemoryShoreGame = ({ onGameOver }: { onGameOver: (score: number) => void }) => { const [score, setScore] = useState(0); const [level, setLevel] = useState(1); const [collectedItems, setCollectedItems] = useState>(new Set()); const [currentPool, setCurrentPool] = useState([]); const [isShuffling, setIsShuffling] = useState(false); // Visual FX State const [tideActive, setTideActive] = useState(false); const [clickEffects, setClickEffects] = useState([]); const [shake, setShake] = useState(false); useEffect(() => { // Start game: Pick 2 random items const shuffled = [...ALL_ITEMS].sort(() => 0.5 - Math.random()); setCurrentPool(shuffled.slice(0, 2)); }, []); const addClickEffect = (e: React.MouseEvent) => { const newEffect = { id: Date.now(), x: e.clientX, y: e.clientY, val: "+100" }; setClickEffects(prev => [...prev, newEffect]); setTimeout(() => { setClickEffects(prev => prev.filter(ef => ef.id !== newEffect.id)); }, 800); }; const proceedToNextRound = (currentCollected: Set, triggerTide: boolean) => { setIsShuffling(true); // Prepare next pool const uncollected = ALL_ITEMS.filter(i => !currentCollected.has(i)); let nextPool = [...currentPool]; if (uncollected.length > 0) { const newItem = uncollected[Math.floor(Math.random() * uncollected.length)]; nextPool.push(newItem); // Shuffle for (let i = nextPool.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [nextPool[i], nextPool[j]] = [nextPool[j], nextPool[i]]; } } if (triggerTide) { setTideActive(true); setTimeout(() => { setCurrentPool(nextPool); }, 600); setTimeout(() => { setTideActive(false); setIsShuffling(false); }, 1200); } else { setTimeout(() => { setCurrentPool(nextPool); setIsShuffling(false); }, 300); } }; const handleItemClick = (item: string, e: React.MouseEvent) => { if (isShuffling) return; if (collectedItems.has(item)) { setShake(true); setTimeout(() => onGameOver(score), 500); } else { addClickEffect(e); const newCollected = new Set(collectedItems); newCollected.add(item); setCollectedItems(newCollected); const newScore = newCollected.size; setScore(newScore); setLevel(Math.floor(newScore / 5) + 1); if (newScore >= WIN_CONDITION) { onGameOver(newScore); } else { const triggerTide = newScore % 3 === 0; proceedToNextRound(newCollected, triggerTide); } } }; const getGridSizeClass = (count: number) => { if (count <= 4) return "w-28 h-28 md:w-32 md:h-32 text-5xl"; if (count <= 9) return "w-20 h-20 md:w-24 md:h-24 text-4xl"; return "w-16 h-16 md:w-20 md:h-20 text-3xl"; }; const renderBubbles = () => { return (
{[...Array(15)].map((_, i) => (
))}
); }; return (
{renderBubbles()} {/* Tide Wave Overlay */} {tideActive && (
)}
{/* Click Feedback Particles */} {clickEffects.map(ef => ( {ef.val} ))} {/* Game Area */}
{/* Header Info */}
Collection {score} / {WIN_CONDITION}
Lvl {level}
{/* Grid */}
{currentPool.map((item) => ( handleItemClick(item, e)} className={`${getGridSizeClass(currentPool.length)} bg-white/10 backdrop-blur-lg rounded-full flex items-center justify-center shadow-[0_8px_32px_rgba(0,0,0,0.2)] border border-white/20 cursor-pointer hover:bg-white/20 transition-colors relative group`} > {item}
))}

Don't click duplicates

); };