"use client"; import { useState, useEffect, useCallback, useRef } from "react"; import { Shield, Mail, X, AlertTriangle, Trophy, RotateCcw, Inbox, MailCheck } from "lucide-react"; import { Button } from "@/components/ui/button"; const GAME_WIDTH = 400; const GAME_HEIGHT = 520; const FORTRESS_Y = GAME_HEIGHT - 48; const SPAWN_INTERVAL_START = 850; const SPAWN_INTERVAL_MIN = 320; const GAME_DURATION = 30; const ENEMY_SPEED_START = 1.2; const ENEMY_SPEED_INCREASE = 0.04; interface Enemy { id: number; x: number; y: number; speed: number; type: "spam" | "phishing" | "legit"; } type GameState = "idle" | "playing" | "won" | "lost"; export function SpamSiegeGame({ onClose }: { onClose: () => void }) { const [gameState, setGameState] = useState("idle"); const [enemies, setEnemies] = useState([]); const [score, setScore] = useState(0); const [timeLeft, setTimeLeft] = useState(GAME_DURATION); const [shieldHealth, setShieldHealth] = useState(3); const [hitEffects, setHitEffects] = useState<{ id: number; x: number; y: number; color: string }[]>([]); const [destroyEffects, setDestroyEffects] = useState<{ id: number; x: number; y: number }[]>([]); const [deliverEffects, setDeliverEffects] = useState<{ id: number; x: number; y: number }[]>([]); const nextId = useRef(0); const animFrameRef = useRef(0); const lastTimeRef = useRef(0); const spawnTimerRef = useRef(0); const gameStateRef = useRef("idle"); const elapsedRef = useRef(0); const destroyedRef = useRef(new Set()); useEffect(() => { gameStateRef.current = gameState; }, [gameState]); const startGame = useCallback(() => { setGameState("playing"); setEnemies([]); setScore(0); setTimeLeft(GAME_DURATION); setShieldHealth(3); setHitEffects([]); setDestroyEffects([]); setDeliverEffects([]); nextId.current = 0; spawnTimerRef.current = 0; elapsedRef.current = 0; destroyedRef.current = new Set(); lastTimeRef.current = performance.now(); }, []); const spawnEnemy = useCallback(() => { const id = nextId.current++; const rand = Math.random(); const type = rand > 0.7 ? "legit" : rand > 0.45 ? "phishing" : "spam"; const x = 20 + Math.random() * (GAME_WIDTH - 60); const elapsed = elapsedRef.current; const speed = ENEMY_SPEED_START + (elapsed / 1000) * ENEMY_SPEED_INCREASE; setEnemies((prev) => [...prev, { id, x, y: -30, speed, type }]); }, []); const handleHover = useCallback((enemy: Enemy) => { if (destroyedRef.current.has(enemy.id)) return; destroyedRef.current.add(enemy.id); if (enemy.type === "legit") { // Penalty for blocking legit mail setShieldHealth((prev) => { const nh = prev - 1; if (nh <= 0) setGameState("lost"); return Math.max(0, nh); }); setScore((prev) => Math.max(0, prev - 15)); const effectId = nextId.current++; setHitEffects((p) => [...p, { id: effectId, x: enemy.x, y: enemy.y, color: "rgba(34, 197, 94, 0.5)" }]); setTimeout(() => setHitEffects((p) => p.filter((h) => h.id !== effectId)), 500); } else { setScore((prev) => prev + 10); const effectId = nextId.current++; setDestroyEffects((prev) => [...prev, { id: effectId, x: enemy.x, y: enemy.y }]); setTimeout(() => setDestroyEffects((prev) => prev.filter((e) => e.id !== effectId)), 400); } setEnemies((prev) => prev.filter((e) => e.id !== enemy.id)); }, []); // Game loop useEffect(() => { if (gameState !== "playing") return; const tick = (now: number) => { if (gameStateRef.current !== "playing") return; const dt = now - lastTimeRef.current; lastTimeRef.current = now; elapsedRef.current += dt; // Timer const newTimeLeft = GAME_DURATION - Math.floor(elapsedRef.current / 1000); setTimeLeft(Math.max(0, newTimeLeft)); if (newTimeLeft <= 0) { setGameState("won"); return; } // Spawn spawnTimerRef.current += dt; const spawnInterval = Math.max( SPAWN_INTERVAL_MIN, SPAWN_INTERVAL_START - (elapsedRef.current / 1000) * 35 ); if (spawnTimerRef.current >= spawnInterval) { spawnTimerRef.current = 0; spawnEnemy(); } // Move enemies setEnemies((prev) => { const next: Enemy[] = []; let spamBreached = false; for (const e of prev) { const ny = e.y + e.speed * (dt / 16); if (ny >= FORTRESS_Y) { if (e.type === "legit") { // Legit mail delivered — bonus setScore((s) => s + 5); const effectId = nextId.current++; setDeliverEffects((p) => [...p, { id: effectId, x: e.x, y: FORTRESS_Y }]); setTimeout(() => setDeliverEffects((p) => p.filter((d) => d.id !== effectId)), 500); } else { spamBreached = true; const effectId = nextId.current++; setHitEffects((p) => [...p, { id: effectId, x: e.x, y: FORTRESS_Y, color: "rgba(219, 45, 84, 0.3)" }]); setTimeout(() => setHitEffects((p) => p.filter((h) => h.id !== effectId)), 500); } } else { next.push({ ...e, y: ny }); } } if (spamBreached) { setShieldHealth((prev) => { const nh = prev - 1; if (nh <= 0) setGameState("lost"); return Math.max(0, nh); }); } return next; }); animFrameRef.current = requestAnimationFrame(tick); }; animFrameRef.current = requestAnimationFrame(tick); return () => cancelAnimationFrame(animFrameRef.current); }, [gameState, spawnEnemy]); const getEnemyStyle = (type: Enemy["type"]) => { switch (type) { case "phishing": return { bg: "rgba(234, 179, 8, 0.15)", border: "rgba(234, 179, 8, 0.4)", color: "rgb(234, 179, 8)" }; case "legit": return { bg: "rgba(34, 197, 94, 0.12)", border: "rgba(34, 197, 94, 0.4)", color: "rgb(34, 197, 94)" }; default: return { bg: "rgba(219, 45, 84, 0.1)", border: "rgba(219, 45, 84, 0.3)", color: "rgb(219, 45, 84)" }; } }; return (
{/* Header */}
Spam Siege
{/* HUD */}
Score: {score} Time: {timeLeft}s
{[...Array(3)].map((_, i) => ( ))}
{/* Game area */}
{/* Grid lines for depth */}
{/* Fortress wall */}
{/* Shield centered above the line */}
0 ? "rgb(219, 45, 84)" : "rgb(100, 100, 100)" }} fill={shieldHealth > 0 ? "rgba(219, 45, 84, 0.2)" : "none"} />
{/* Solid line */}
0 ? "rgba(219, 45, 84, 0.35)" : "rgba(100, 100, 100, 0.3)" }} />
{/* Subtle gradient fill below */}
0 ? "linear-gradient(to bottom, rgba(219, 45, 84, 0.06), transparent)" : "linear-gradient(to bottom, rgba(100, 100, 100, 0.04), transparent)", }} />
{/* Enemies */} {enemies.map((e) => { const style = getEnemyStyle(e.type); return (
handleHover(e)} > {e.type === "phishing" ? ( ) : e.type === "legit" ? ( ) : ( )}
); })} {/* Destroy effects */} {destroyEffects.map((e) => (
))} {/* Deliver effects (legit mail arrived) */} {deliverEffects.map((e) => (
))} {/* Hit effects on fortress */} {hitEffects.map((e) => (
))} {/* Idle overlay */} {gameState === "idle" && (

Spam Siege

Hover over threats to block them. Let legitimate mail through. Survive {GAME_DURATION} seconds.

Spam Phishing Legit
)} {/* Won overlay */} {gameState === "won" && (

Fortress Secured

Score: {score}

)} {/* Lost overlay */} {gameState === "lost" && (

Fortress Breached

Score: {score}

)}
); }