"use client"; import { useState, useRef, useEffect, useCallback } from "react"; import { motion, AnimatePresence } from "framer-motion"; type GameState = "upload" | "ready" | "playing" | "gameover"; type BossState = "idle" | "attacking" | "stunned" | "dizzy"; const powerUps: Record = { double: { name: "2x Damage", icon: "⚔️", duration: 5000 }, freeze: { name: "Stun Boss", icon: "❄️", duration: 3000 }, bomb: { name: "Bomb", icon: "💣", duration: 0 }, none: { name: "", icon: "", duration: 0 }, }; const playSound = (type: "punch" | "combo" | "powerup" | "ko" | "gameover" | "bossAttack" | "hit") => { try { const audioCtx = new (window.AudioContext || (window as any).webkitAudioContext)(); const oscillator = audioCtx.createOscillator(); const gainNode = audioCtx.createGain(); oscillator.connect(gainNode); gainNode.connect(audioCtx.destination); switch (type) { case "punch": oscillator.frequency.setValueAtTime(150, audioCtx.currentTime); oscillator.frequency.exponentialRampToValueAtTime(50, audioCtx.currentTime + 0.1); gainNode.gain.setValueAtTime(0.5, audioCtx.currentTime); gainNode.gain.exponentialRampToValueAtTime(0.01, audioCtx.currentTime + 0.1); oscillator.start(audioCtx.currentTime); oscillator.stop(audioCtx.currentTime + 0.1); break; case "hit": oscillator.frequency.setValueAtTime(200, audioCtx.currentTime); oscillator.frequency.exponentialRampToValueAtTime(80, audioCtx.currentTime + 0.15); gainNode.gain.setValueAtTime(0.4, audioCtx.currentTime); gainNode.gain.exponentialRampToValueAtTime(0.01, audioCtx.currentTime + 0.15); oscillator.start(audioCtx.currentTime); oscillator.stop(audioCtx.currentTime + 0.15); break; case "bossAttack": oscillator.frequency.setValueAtTime(80, audioCtx.currentTime); oscillator.frequency.setValueAtTime(120, audioCtx.currentTime + 0.1); oscillator.frequency.setValueAtTime(80, audioCtx.currentTime + 0.2); gainNode.gain.setValueAtTime(0.3, audioCtx.currentTime); gainNode.gain.exponentialRampToValueAtTime(0.01, audioCtx.currentTime + 0.3); oscillator.start(audioCtx.currentTime); oscillator.stop(audioCtx.currentTime + 0.3); break; case "combo": oscillator.frequency.setValueAtTime(300, audioCtx.currentTime); oscillator.frequency.setValueAtTime(400, audioCtx.currentTime + 0.1); oscillator.frequency.setValueAtTime(500, audioCtx.currentTime + 0.2); gainNode.gain.setValueAtTime(0.3, audioCtx.currentTime); gainNode.gain.exponentialRampToValueAtTime(0.01, audioCtx.currentTime + 0.3); oscillator.start(audioCtx.currentTime); oscillator.stop(audioCtx.currentTime + 0.3); break; case "powerup": oscillator.frequency.setValueAtTime(200, audioCtx.currentTime); oscillator.frequency.exponentialRampToValueAtTime(800, audioCtx.currentTime + 0.3); gainNode.gain.setValueAtTime(0.3, audioCtx.currentTime); gainNode.gain.exponentialRampToValueAtTime(0.01, audioCtx.currentTime + 0.3); oscillator.start(audioCtx.currentTime); oscillator.stop(audioCtx.currentTime + 0.3); break; case "ko": oscillator.frequency.setValueAtTime(100, audioCtx.currentTime); oscillator.frequency.exponentialRampToValueAtTime(30, audioCtx.currentTime + 0.5); gainNode.gain.setValueAtTime(0.8, audioCtx.currentTime); gainNode.gain.exponentialRampToValueAtTime(0.01, audioCtx.currentTime + 0.5); oscillator.start(audioCtx.currentTime); oscillator.stop(audioCtx.currentTime + 0.5); break; case "gameover": oscillator.frequency.setValueAtTime(400, audioCtx.currentTime); oscillator.frequency.setValueAtTime(300, audioCtx.currentTime + 0.2); oscillator.frequency.setValueAtTime(200, audioCtx.currentTime + 0.4); gainNode.gain.setValueAtTime(0.3, audioCtx.currentTime); gainNode.gain.exponentialRampToValueAtTime(0.01, audioCtx.currentTime + 0.6); oscillator.start(audioCtx.currentTime); oscillator.stop(audioCtx.currentTime + 0.6); break; } } catch (e) {} }; export default function BossPunch() { const [bossImage, setBossImage] = useState(null); const [gameState, setGameState] = useState("upload"); const [score, setScore] = useState(0); const [bossHealth, setBossHealth] = useState(100); const [playerHealth, setPlayerHealth] = useState(100); const [combo, setCombo] = useState(0); const [maxCombo, setMaxCombo] = useState(0); const [lastHit, setLastHit] = useState<"left" | "right" | null>(null); const [playerX, setPlayerX] = useState(0); const [bossX, setBossX] = useState(0); const [bossRotation, setBossRotation] = useState(0); const [bossState, setBossState] = useState("idle"); const [isPaid, setIsPaid] = useState(false); const [showPayModal, setShowPayModal] = useState(false); const [activePowerUp, setActivePowerUp] = useState("none"); const [powerUpCooldown, setPowerUpCooldown] = useState(0); const [floatingTexts, setFloatingTexts] = useState<{ id: number; text: string; x: number; y: number; type: string }[]>([]); const [screenShake, setScreenShake] = useState(false); const [particles, setParticles] = useState<{ id: number; x: number; y: number; vx: number; vy: number; color: string; life: number }[]>([]); const [winQuote, setWinQuote] = useState("I am sorry Haitham!"); const [showWinQuoteInput, setShowWinQuoteInput] = useState(false); const fileInputRef = useRef(null); const floatingIdRef = useRef(0); const particleIdRef = useRef(0); const attackIntervalRef = useRef(null); const addFloatingText = (text: string, x: number, y: number, type: string = "damage") => { const id = floatingIdRef.current++; setFloatingTexts(prev => [...prev, { id, text, x, y, type }]); setTimeout(() => { setFloatingTexts(prev => prev.filter(t => t.id !== id)); }, 1500); }; const addParticle = (x: number, y: number, color: string) => { const id = particleIdRef.current++; setParticles(prev => [...prev, { id, x, y, vx: (Math.random() - 0.5) * 10, vy: (Math.random() - 0.5) * 10 - 5, color, life: 1 }]); }; useEffect(() => { if (particles.length > 0) { const timer = setInterval(() => { setParticles(prev => prev.map(p => ({ ...p, x: p.x + p.vx, y: p.y + p.vy, vy: p.vy + 0.5, life: p.life - 0.05 })).filter(p => p.life > 0)); }, 16); return () => clearInterval(timer); } }, [particles.length]); useEffect(() => { if (gameState === "playing" && bossState !== "stunned" && bossState !== "dizzy") { attackIntervalRef.current = setInterval(() => { if (bossState === "idle" && Math.random() < 0.3) { bossAttack(); } }, 2000); } return () => { if (attackIntervalRef.current) clearInterval(attackIntervalRef.current); }; }, [gameState, bossState]); const bossAttack = () => { if (bossState !== "idle") return; setBossState("attacking"); playSound("bossAttack"); setBossX(-30); setTimeout(() => { setBossX(30); setTimeout(() => { const damage = 5 + Math.floor(Math.random() * 10); setPlayerHealth(prev => Math.max(0, prev - damage)); playSound("hit"); setScreenShake(true); setTimeout(() => setScreenShake(false), 200); addFloatingText(`-${damage}`, 30, 60, "boss"); setBossX(0); setBossState("idle"); if (playerHealth - damage <= 0) { setGameState("gameover"); playSound("gameover"); } }, 150); }, 150); }; const handleImageUpload = (e: React.ChangeEvent) => { const file = e.target.files?.[0]; if (file) { const reader = new FileReader(); reader.onload = (event) => { setBossImage(event.target?.result as string); setGameState("ready"); }; reader.readAsDataURL(file); } }; const startGame = () => { setGameState("playing"); setScore(0); setBossHealth(100); setPlayerHealth(100); setCombo(0); setMaxCombo(0); setActivePowerUp("none"); setBossState("idle"); }; const activatePowerUp = (type: string) => { if (powerUpCooldown > 0 || gameState !== "playing") return; playSound("powerup"); setActivePowerUp(type); if (type === "bomb") { setBossHealth(prev => Math.max(0, prev - 30)); addFloatingText("-30! 💣", 70, 30, "powerup"); setScreenShake(true); setTimeout(() => setScreenShake(false), 300); } else if (type === "freeze") { setBossState("stunned"); setTimeout(() => setBossState("idle"), 3000); } else { setTimeout(() => setActivePowerUp("none"), powerUps[type]?.duration || 5000); } setPowerUpCooldown(10); }; useEffect(() => { if (powerUpCooldown > 0) { const timer = setInterval(() => { setPowerUpCooldown(prev => Math.max(0, prev - 1)); }, 1000); return () => clearInterval(timer); } }, [powerUpCooldown]); const punch = useCallback((hand: "left" | "right") => { if (gameState !== "playing" || bossState === "stunned") return; if (Math.random() < 0.15 && bossState === "idle") { setBossState("dizzy"); addFloatingText("Miss!", 70, 40, "miss"); setTimeout(() => setBossState("idle"), 500); return; } setLastHit(hand); setCombo(c => { const newCombo = c + 1; if (newCombo > maxCombo) setMaxCombo(newCombo); if (newCombo % 10 === 0) { playSound("combo"); addFloatingText(`${newCombo} COMBO! 🔥`, 50, 50, "combo"); } return newCombo; }); let damage = 5 + Math.floor(combo / 5); damage = Math.min(damage, 20); if (activePowerUp === "double") damage *= 2; playSound("punch"); const newHealth = Math.max(0, bossHealth - damage); setBossHealth(newHealth); setScore(s => s + damage * 10); setBossRotation(hand === "left" ? -20 - (damage * 2) : 20 + (damage * 2)); setBossX(hand === "left" ? -20 : 20); setTimeout(() => { setBossRotation(0); setBossX(0); }, 150); setPlayerX(hand === "left" ? 40 : -40); setTimeout(() => setPlayerX(0), 100); for (let i = 0; i < 5; i++) { addParticle(70, 40, damage > 15 ? "#ff0000" : "#ffffff"); } addFloatingText(`-${damage}`, 60 + (Math.random() * 20 - 10), 30 + (Math.random() * 20 - 10), "damage"); if (damage > 10) { setScreenShake(true); setTimeout(() => setScreenShake(false), 100); } setTimeout(() => setLastHit(null), 150); if (newHealth <= 0) { playSound("ko"); setGameState("gameover"); } }, [gameState, combo, bossHealth, activePowerUp, maxCombo, bossState]); const resetGame = () => { setGameState("ready"); setScore(0); setBossHealth(100); setPlayerHealth(100); setCombo(0); setMaxCombo(0); setActivePowerUp("none"); setBossState("idle"); }; const shakeClass = screenShake ? "animate-pulse" : ""; return (
{particles.map(p => ( ))} {floatingTexts.map(ft => ( {ft.text} ))}

BOSSPUNCH

{gameState === "playing" && (
Score: {score} Combo: {combo}x
)}
{gameState === "playing" && (
BOSS
YOU
)} {gameState === "playing" && (
{(["double", "freeze", "bomb"] as string[]).map(pu => ( ))} {powerUpCooldown > 0 && {powerUpCooldown}s}
)}
{bossImage && ( 0 ? 1 : 0.8, opacity: bossState === "stunned" ? 0.7 : 1 }} transition={{ type: "spring", stiffness: 300 }} >
L
R
Boss
{bossState === "stunned" &&
💫
} {bossState === "dizzy" &&
😵
}
)}
{gameState === "playing" && (
punch("right")}>👊 punch("left")}>👊
)} {lastHit && ( Pow! )}
{gameState === "upload" && (
👔

Upload Your Boss!

Take out your frustrations!

Free with ads • $9.99 for ad-free

)} {gameState === "ready" && bossImage && (
{showWinQuoteInput && ( setWinQuote(e.target.value)} placeholder="What does boss say when u win?" className="w-full mt-2 bg-white/20 border border-white/30 rounded-lg px-3 py-2 text-white placeholder-white/50 text-sm" /> )}

Ready to Fight!

Boss fights back! Don't lose!

)} {gameState === "playing" && (

Tap to punch!

)} {gameState === "gameover" && (
{bossHealth <= 0 ? "🏆" : "💀"}

{bossHealth <= 0 ? "KNOCKOUT!" : "YOU LOSE!"}

{bossHealth <= 0 && (

Boss says:

"{winQuote}"

)}

Score: {score}

Max Combo: {maxCombo}x

{!isPaid && ( )}
)}
{/* Stripe Payment Modal */} {showPayModal && (

Remove Ads

Get BossPunch ad-free forever!

$9.99

)} {!isPaid && gameState !== "upload" && (

Advertisement

)}
); }