import React, { useState, useEffect, useRef } from 'react';
import { Play, RotateCcw, Shield, Heart, Zap, Star, ChevronRight, AlertCircle, Search, Navigation, Coffee, Beaker, Crosshair } from 'lucide-react';
// --- GAME DATA & STORY ---
const CHARACTERS = {
WHISKERS: { name: 'Capt. Whiskers', icon: '๐ผ', color: 'text-orange-400' },
LUNA: { name: 'Luna', icon: '๐โโฌ', color: 'text-purple-400', role: 'Tech' },
REX: { name: 'Rex', icon: '๐
', color: 'text-yellow-500', role: 'Weapons' },
CLEO: { name: 'Cleo', icon: '๐', color: 'text-green-400', role: 'Medic' },
COMPUTER: { name: 'Ship AI', icon: '๐ฅ๏ธ', color: 'text-blue-300' },
// Bosses
BARKOR: { name: 'Lord Barkor', icon: '๐บ', color: 'text-red-500' },
PUG: { name: 'Private Pug', icon: '๐ฆฎ', color: 'text-stone-400' },
SCHNAUZER: { name: 'Sgt. Schnauzer', icon: '๐โ๐ฆบ', color: 'text-gray-400' },
POODLE: { name: 'Admiral Poodle', icon: '๐ฉ', color: 'text-pink-400' },
CORGI: { name: 'Cmdr. Corgi', icon: '๐', color: 'text-orange-300' },
BEAGLE: { name: 'Baron Beagle', icon: '๐ถ', color: 'text-yellow-600' },
HUSKY: { name: 'The Husky Twins', icon: '๐บ๐บ', color: 'text-cyan-300' },
DALMATIAN: { name: 'Duchess Dalmatian', icon: '๐พ', color: 'text-white' },
GOLDEN: { name: 'General Golden', icon: '๐ฆฎ', color: 'text-yellow-400' },
MASTIFF: { name: 'Emperor Mastiff', icon: '๐โ๐ฆบ', color: 'text-red-700' },
MECHA_BARKOR: { name: 'Mecha-Barkor', icon: '๐ค๐บ', color: 'text-red-600' }
};
const LEVELS = [
{
boss: { ...CHARACTERS.BARKOR, hp: 100, seqLen: 3, speed: 800, damage: 1 },
shooterTarget: 10,
story: [
{ char: CHARACTERS.COMPUTER, text: "ALERT. HOSTILE CANINE FLEET DETECTED." },
{ char: CHARACTERS.WHISKERS, text: "Red alert! Crew to stations. Barkor stole the Sacred Yarn!" },
{ char: CHARACTERS.REX, text: "Guns are hot, Captain. I'll clear a path through his fighter drones!" },
{ char: CHARACTERS.LUNA, text: "Once we break the blockade, I'll need to hack his color-frequency shields!" }
]
},
{
boss: { ...CHARACTERS.PUG, hp: 125, seqLen: 3, speed: 750, damage: 1 },
shooterTarget: 12,
story: [
{ char: CHARACTERS.WHISKERS, text: "Barkor fled into hyperspace, but he left a blockade." },
{ char: CHARACTERS.CLEO, text: "It's Private Pug's frigate. He's deploying heavy fighters." },
{ char: CHARACTERS.PUG, text: "Y-you won't get past me! The Yarn belongs to the dogs!" },
{ char: CHARACTERS.REX, text: "I'll make him play dead. Engaging enemy fighters!" }
]
},
{
boss: { ...CHARACTERS.SCHNAUZER, hp: 150, seqLen: 4, speed: 700, damage: 1 },
shooterTarget: 14,
story: [
{ char: CHARACTERS.SCHNAUZER, text: "Achtung! This asteroid belt is restricted canine territory!" },
{ char: CHARACTERS.LUNA, text: "Captain, it's Sgt. Schnauzer. His drone swarm is intercepting us." },
{ char: CHARACTERS.CLEO, text: "Stay calm, everyone. Keep your tails steady and dodge their fire." }
]
},
{
boss: { ...CHARACTERS.POODLE, hp: 175, seqLen: 4, speed: 650, damage: 1 },
shooterTarget: 16,
story: [
{ char: CHARACTERS.POODLE, text: "Ugh, the smell of wet cat is ruining my upholstery. Fire the luxury lasers!" },
{ char: CHARACTERS.WHISKERS, text: "Admiral Poodle. She's sending her elite poodle-drones." },
{ char: CHARACTERS.REX, text: "Her ships are so shiny. I can't wait to put dents in them." }
]
},
{
boss: { ...CHARACTERS.CORGI, hp: 200, seqLen: 4, speed: 600, damage: 1 },
shooterTarget: 18,
story: [
{ char: CHARACTERS.COMPUTER, text: "WARNING. INCOMING SWARM." },
{ char: CHARACTERS.CORGI, text: "Our legs may be short, but our fleet is endless! Yip yip!" },
{ char: CHARACTERS.LUNA, text: "They're fast, Captain! We need to thin their numbers before we can reach the Commander!" }
]
},
{
boss: { ...CHARACTERS.BEAGLE, hp: 225, seqLen: 5, speed: 550, damage: 1 },
shooterTarget: 20,
story: [
{ char: CHARACTERS.BEAGLE, text: "Aroooo! My tracking sensors found you, felines. Deploying seekers!" },
{ char: CHARACTERS.WHISKERS, text: "Baron Beagle. Evasive maneuvers! Blast those seekers!" },
{ char: CHARACTERS.CLEO, text: "His acoustic weapons are hurting my ears. Please take his shields down, Luna!" }
]
},
{
boss: { ...CHARACTERS.HUSKY, hp: 250, seqLen: 5, speed: 500, damage: 2 },
shooterTarget: 22,
story: [
{ char: CHARACTERS.HUSKY, text: "We are the storm! We are the snow! You cannot defeat both of us!" },
{ char: CHARACTERS.REX, text: "Two of them? And a massive drone escort. Good, twice the target practice." },
{ char: CHARACTERS.LUNA, text: "Careful! Their dual-core shield generators deal double damage if we fail the hack!" }
]
},
{
boss: { ...CHARACTERS.DALMATIAN, hp: 275, seqLen: 5, speed: 450, damage: 2 },
shooterTarget: 24,
story: [
{ char: CHARACTERS.DALMATIAN, text: "I have 101 ways to destroy your little litter box." },
{ char: CHARACTERS.CLEO, text: "Duchess Dalmatian... her stealth drones are decloaking all around us." },
{ char: CHARACTERS.WHISKERS, text: "Focus up, team. Shoot them down, then we hack her core." }
]
},
{
boss: { ...CHARACTERS.GOLDEN, hp: 300, seqLen: 6, speed: 450, damage: 2 },
shooterTarget: 26,
story: [
{ char: CHARACTERS.GOLDEN, text: "I just wanted to be a good boy, but orders are orders! Launching heavy fighters." },
{ char: CHARACTERS.LUNA, text: "General Golden's dreadnought has reinforced kinetic barriers. Six-tier sequences!" },
{ char: CHARACTERS.REX, text: "He's too friendly. It's making me angry. Fire at will!" }
]
},
{
boss: { ...CHARACTERS.MASTIFF, hp: 350, seqLen: 6, speed: 400, damage: 2 },
shooterTarget: 28,
story: [
{ char: CHARACTERS.MASTIFF, text: "BOW BEFORE THE EMPEROR OF THE CANINE WORLDS. MY ARMADA WILL CRUSH YOU." },
{ char: CHARACTERS.WHISKERS, text: "Emperor Mastiff. The biggest dog in the galaxy. His fleet is massive." },
{ char: CHARACTERS.CLEO, text: "We have to survive this wave to even get close!" },
{ char: CHARACTERS.LUNA, text: "I'll route auxiliary power to the hacking terminals. Get ready!" }
]
},
{
boss: { ...CHARACTERS.MECHA_BARKOR, hp: 500, seqLen: 7, speed: 300, damage: 3 },
shooterTarget: 35,
story: [
{ char: CHARACTERS.WHISKERS, text: "We finally caught you, Barkor! Hand over the Yarn!" },
{ char: CHARACTERS.MECHA_BARKOR, text: "FOOLS! I HAVE MERGED WITH THE SHIP! I AM UNSTOPPABLE!" },
{ char: CHARACTERS.REX, text: "He's firing everything he has! It's a bullet hell out there!" },
{ char: CHARACTERS.CLEO, text: "Captain, his power levels are off the charts. Mistakes will be fatal." },
{ char: CHARACTERS.LUNA, text: "Seven-tier cipher. Ultra-fast frequency. Break his vanguard, then we end this. For the universe!" }
]
}
];
const ROOMS = [
{ id: 'bridge', name: 'The Bridge', icon: , desc: 'The command center. Lots of blinking lights.' },
{ id: 'engine', name: 'Engine Room', icon: , desc: 'Warm and vibrating. A great place for a nap.' },
{ id: 'quarters', name: 'Crew Quarters', icon: , desc: 'Cozy beds and half-eaten tuna cans.' },
{ id: 'medbay', name: 'Medbay', icon: , desc: 'Smells sterile. Cleo keeps her supplies here.' }
];
const LORE_SNIPPETS = [
"You found a crumpled note: 'Don't let Rex near the laser calibration console.'",
"Luna's terminal has a game of Space-Tetris paused on a high score.",
"You discover a stash of illegal ultra-premium salmon treats.",
"There is a poster of 'The Great Catzby' on the wall.",
"You see a dog-shaped voodoo doll covered in claw marks.",
"Cleo's medical log: 'Whiskers furball incident #42 successfully contained.'",
"The warp core hums softly, sounding exactly like a giant purring cat."
];
// --- COMPONENTS ---
const TypewriterText = ({ text, onComplete, speed = 20 }) => {
const [displayedText, setDisplayedText] = useState('');
useEffect(() => {
setDisplayedText('');
let i = 0;
const interval = setInterval(() => {
setDisplayedText(text.substring(0, i + 1));
i++;
if (i >= text.length) {
clearInterval(interval);
if (onComplete) onComplete();
}
}, speed);
return () => clearInterval(interval);
}, [text, speed]);
return {displayedText} ;
};
const Cutscene = ({ sceneData, onFinish }) => {
const [dialogueIndex, setDialogueIndex] = useState(0);
const [isTyping, setIsTyping] = useState(true);
const currentLine = sceneData[dialogueIndex];
const handleNext = () => {
if (isTyping) {
setIsTyping(false);
} else {
if (dialogueIndex < sceneData.length - 1) {
setDialogueIndex(prev => prev + 1);
setIsTyping(true);
} else {
onFinish();
}
}
};
return (
{currentLine.char.name}
{currentLine.char.role && {currentLine.char.role} }
{isTyping ? (
setIsTyping(false)} />
) : (
currentLine.text
)}
Click to continue
);
};
const ExploreShip = ({ onStartMission, catnip, setCatnip, level, shipHp, setShipHp, maxShipHp }) => {
const [searchedRooms, setSearchedRooms] = useState([]);
const [modalContent, setModalContent] = useState(null);
// Guaranteed Catnip in one random room per level
const [catnipRoom] = useState(() => ROOMS[Math.floor(Math.random() * ROOMS.length)].id);
const handleSearch = (roomId) => {
if (searchedRooms.includes(roomId)) return;
setSearchedRooms(prev => [...prev, roomId]);
if (roomId === catnipRoom) {
setCatnip(c => c + 1);
setModalContent({
title: "Secret Found!",
text: "You sniffed out some highly potent Space Catnip! This can heal the ship during combat or right now.",
icon: "๐ฟ",
color: "text-green-400"
});
} else {
const randomLore = LORE_SNIPPETS[Math.floor(Math.random() * LORE_SNIPPETS.length)];
setModalContent({
title: "Nothing useful...",
text: randomLore,
icon: "๐",
color: "text-blue-300"
});
}
};
const useCatnip = () => {
if (catnip > 0 && shipHp < maxShipHp) {
setCatnip(c => c - 1);
setShipHp(p => Math.min(p + 2, maxShipHp));
}
};
return (
{/* Header Info */}
Astro-Paws Cruiser
Status: Orbiting Sector {level + 1}
๐ฟ
x {catnip}
{[...Array(maxShipHp)].map((_, i) => (
))}
{shipHp < maxShipHp && catnip > 0 && (
Heal Ship (+2 HP)
)}
{/* Rooms */}
{ROOMS.map(room => {
const isSearched = searchedRooms.includes(room.id);
return (
handleSearch(room.id)}
disabled={isSearched}
className={`
flex flex-col items-center justify-center p-4 rounded-xl border-2 transition-all
${isSearched
? 'bg-slate-800 border-slate-700 opacity-50 cursor-not-allowed'
: 'bg-slate-700 border-slate-500 hover:bg-slate-600 hover:scale-105 active:scale-95 shadow-lg'}
`}
>
{room.icon}
{room.name}
{room.desc}
{isSearched && SEARCHED }
)
})}
START MISSION {level + 1}
{/* Modal */}
{modalContent && (
{modalContent.icon}
{modalContent.title}
{modalContent.text}
setModalContent(null)}
className="w-full py-3 bg-blue-600 hover:bg-blue-500 text-white font-bold rounded-lg transition-colors"
>
Continue
)}
);
};
const SpaceShooter = ({ levelData, levelIndex, shipHp, setShipHp, maxShipHp, onWin, onLose }) => {
const canvasRef = useRef(null);
// Local state for UI rendering only, actual logic runs in ref
const [uiScore, setUiScore] = useState(0);
const [uiHp, setUiHp] = useState(shipHp);
const targetScore = levelData.shooterTarget;
const stateRef = useRef({
player: { x: 50, y: 80, size: 30, lastShot: 0 },
bullets: [],
enemies: [],
particles: [],
lastEnemyTime: 0,
score: 0,
hp: shipHp,
isActive: true
});
useEffect(() => {
const canvas = canvasRef.current;
const ctx = canvas.getContext('2d');
let animationFrameId;
const resize = () => {
const parent = canvas.parentElement;
canvas.width = parent.clientWidth;
canvas.height = parent.clientHeight;
stateRef.current.player.y = canvas.height - 60;
if (stateRef.current.player.x > canvas.width) {
stateRef.current.player.x = canvas.width / 2;
}
};
window.addEventListener('resize', resize);
resize();
stateRef.current.player.x = canvas.width / 2;
const createParticle = (x, y, color) => {
for(let i=0; i<6; i++) {
stateRef.current.particles.push({
x, y,
vx: (Math.random() - 0.5) * 6,
vy: (Math.random() - 0.5) * 6,
life: 1,
color
});
}
};
const enemyIcons = ['๐ถ', '๐', '๐ฉ', '๐บ', '๐ธ', '๐ค'];
const update = (time) => {
if (!stateRef.current.isActive) return;
const state = stateRef.current;
// Auto Fire
if (time - state.player.lastShot > 250) {
state.bullets.push({ x: state.player.x - 10, y: state.player.y - state.player.size, speed: 12 });
state.bullets.push({ x: state.player.x + 10, y: state.player.y - state.player.size, speed: 12 });
state.player.lastShot = time;
}
// Spawn Enemies based on Level difficulty
const spawnRate = Math.max(300, 1000 - (levelIndex * 60)); // gets faster
if (time - state.lastEnemyTime > spawnRate) {
const baseSpeed = 2 + (levelIndex * 0.2);
state.enemies.push({
x: Math.random() * (canvas.width - 40) + 20,
y: -30,
speed: baseSpeed + Math.random() * 2,
hp: levelIndex > 5 && Math.random() > 0.8 ? 2 : 1, // occasional tough enemies
icon: enemyIcons[Math.floor(Math.random() * (levelIndex > 5 ? 6 : 4))]
});
state.lastEnemyTime = time;
}
// Update Bullets
state.bullets.forEach(b => b.y -= b.speed);
state.bullets = state.bullets.filter(b => b.y > -10);
// Update Particles
state.particles.forEach(p => {
p.x += p.vx;
p.y += p.vy;
p.life -= 0.05;
});
state.particles = state.particles.filter(p => p.life > 0);
// Update Enemies & Collisions
for (let i = state.enemies.length - 1; i >= 0; i--) {
const e = state.enemies[i];
e.y += e.speed;
// Enemy escapes out of bounds -> player takes damage
if (e.y > canvas.height + 30) {
state.enemies.splice(i, 1);
state.hp -= 1;
setUiHp(state.hp);
setShipHp(state.hp);
if (state.hp <= 0) {
state.isActive = false;
onLose();
}
continue;
}
// Bullet hitting enemy
for (let j = state.bullets.length - 1; j >= 0; j--) {
const b = state.bullets[j];
const dist = Math.hypot(b.x - e.x, b.y - e.y);
if (dist < 30) {
state.bullets.splice(j, 1);
e.hp -= 1;
if (e.hp <= 0) {
state.enemies.splice(i, 1);
createParticle(e.x, e.y, '#fb923c');
state.score += 1;
setUiScore(state.score);
if (state.score >= targetScore) {
state.isActive = false;
onWin();
}
} else {
createParticle(e.x, e.y, '#9ca3af'); // armor spark
}
break;
}
}
// Player colliding with enemy
if (state.isActive) {
const distToPlayer = Math.hypot(state.player.x - e.x, state.player.y - e.y);
if (distToPlayer < 35) {
state.enemies.splice(i, 1);
createParticle(e.x, e.y, '#ef4444');
state.hp -= 1;
setUiHp(state.hp);
setShipHp(state.hp);
if (state.hp <= 0) {
state.isActive = false;
onLose();
}
}
}
}
// -- DRAWING --
ctx.fillStyle = '#0f172a'; // space bg
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Starfield
ctx.fillStyle = 'rgba(255, 255, 255, 0.4)';
for(let s=0; s<25; s++) {
const speed = 1 + (levelIndex * 0.1);
ctx.fillRect(
Math.random()*canvas.width,
((time * speed)/10 + s*80) % canvas.height,
Math.random() > 0.8 ? 3 : 1,
Math.random()*15 + 5
);
}
// Draw Player
ctx.font = "40px Arial";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillText("๐", state.player.x, state.player.y);
ctx.fillText("๐ผ", state.player.x, state.player.y - 10);
// Draw Bullets
ctx.fillStyle = '#facc15'; // yellow lasers
ctx.shadowBlur = 8;
ctx.shadowColor = '#facc15';
state.bullets.forEach(b => {
ctx.beginPath();
ctx.arc(b.x, b.y, 4, 0, Math.PI * 2);
ctx.fill();
});
ctx.shadowBlur = 0;
// Draw Enemies
state.enemies.forEach(e => {
ctx.font = e.hp > 1 ? "50px Arial" : "35px Arial"; // bigger if tough
ctx.fillText(e.icon, e.x, e.y);
});
// Draw Particles
state.particles.forEach(p => {
ctx.fillStyle = p.color;
ctx.globalAlpha = p.life;
ctx.beginPath();
ctx.arc(p.x, p.y, p.life * 4, 0, Math.PI * 2);
ctx.fill();
});
ctx.globalAlpha = 1.0;
animationFrameId = requestAnimationFrame(update);
};
animationFrameId = requestAnimationFrame(update);
// Controls
const handleMove = (x) => {
if(!stateRef.current.isActive) return;
const rect = canvas.getBoundingClientRect();
let newX = x - rect.left;
newX = Math.max(20, Math.min(newX, canvas.width - 20));
stateRef.current.player.x = newX;
};
const onMouseMove = (e) => handleMove(e.clientX);
const onTouchMove = (e) => {
e.preventDefault();
handleMove(e.touches[0].clientX);
};
canvas.addEventListener('mousemove', onMouseMove);
canvas.addEventListener('touchmove', onTouchMove, { passive: false });
return () => {
window.removeEventListener('resize', resize);
canvas.removeEventListener('mousemove', onMouseMove);
canvas.removeEventListener('touchmove', onTouchMove);
cancelAnimationFrame(animationFrameId);
};
}, [levelIndex, targetScore, setShipHp, onWin, onLose]);
return (
{/* Header UI */}
VANGUARD FLEET
{uiScore} / {targetScore} DESTROYED
SHIP HULL
{[...Array(maxShipHp)].map((_, i) => (
))}
{/* Mobile instruction overlay */}
);
};
const BossBattle = ({ bossData, onWin, onLose, catnip, setCatnip, shipHp, setShipHp, maxShipHp }) => {
const [bossHp, setBossHp] = useState(bossData.hp);
const maxBossHp = bossData.hp;
const [sequence, setSequence] = useState([]);
const [playerInput, setPlayerInput] = useState([]);
const [isShowingSequence, setIsShowingSequence] = useState(false);
const [message, setMessage] = useState("Vanguard cleared! Hack the core!");
const colors = ['bg-red-500', 'bg-blue-500', 'bg-green-500', 'bg-yellow-400'];
const symbols = ['๐ฅ', 'โก', '๐ฟ', 'โญ'];
const generateSequence = (length) => {
return Array.from({ length }, () => Math.floor(Math.random() * 4));
};
const startRound = () => {
const newSeq = generateSequence(bossData.seqLen);
setSequence(newSeq);
setPlayerInput([]);
setIsShowingSequence(true);
setMessage("Memorize the frequency!");
let i = 0;
const interval = setInterval(() => {
if (i < newSeq.length) {
i++;
} else {
clearInterval(interval);
setTimeout(() => {
setIsShowingSequence(false);
setMessage("Hack the shields!");
}, 300);
}
}, bossData.speed);
};
useEffect(() => {
const timer = setTimeout(() => startRound(), 2000);
return () => clearTimeout(timer);
}, []);
const handleColorClick = (idx) => {
if (isShowingSequence) return;
const newInput = [...playerInput, idx];
setPlayerInput(newInput);
const isCorrect = newInput.every((val, index) => val === sequence[index]);
if (!isCorrect) {
setMessage("HACK FAILED!");
const newHp = shipHp - bossData.damage;
setShipHp(newHp);
if (newHp <= 0) {
setTimeout(onLose, 1000);
} else {
setTimeout(() => startRound(), 1500);
}
} else if (newInput.length === sequence.length) {
setMessage("SHIELDS DOWN! Firing Lasers!");
const damage = 35; // increased damage to make phase shorter since there's a shooter phase too
const newBossHp = bossHp - damage;
setBossHp(newBossHp);
if (newBossHp <= 0) {
setTimeout(onWin, 1500);
} else {
setTimeout(() => startRound(), 2000);
}
}
};
const useCatnip = () => {
if (catnip > 0 && shipHp < maxShipHp) {
setCatnip(c => c - 1);
setShipHp(p => Math.min(p + 2, maxShipHp));
setMessage("Used Catnip! Healed 2 HP!");
}
};
const [activeSeqIdx, setActiveSeqIdx] = useState(-1);
useEffect(() => {
if(isShowingSequence) {
let i = 0;
const int = setInterval(() => {
setActiveSeqIdx(sequence[i]);
setTimeout(() => setActiveSeqIdx(-1), bossData.speed * 0.6);
i++;
if(i >= sequence.length) clearInterval(int);
}, bossData.speed);
return () => clearInterval(int);
} else {
setActiveSeqIdx(-1);
}
}, [isShowingSequence, sequence, bossData.speed]);
return (
= 500 ? 'border-red-600 shadow-[0_0_50px_rgba(255,0,0,0.5)]' : 'border-purple-900 shadow-[0_0_40px_rgba(150,0,255,0.3)]'}`}>
{/* Header Stats */}
{bossData.name}
{Math.max(0, bossHp)} / {maxBossHp}
Astro-Paws Hull
{[...Array(maxShipHp)].map((_, i) => (
))}
{/* Battlefield */}
{/* Boss Visual */}
{bossData.icon}
{/* Shields visual */}
{bossHp > 0 && (
)}
{/* Status Message */}
{message}
{/* Action Bar (Catnip) */}
= maxShipHp || isShowingSequence}
className={`
flex items-center gap-2 px-4 py-2 sm:px-6 sm:py-3 rounded-full font-bold transition-all shadow-lg border-2
${(catnip > 0 && shipHp < maxShipHp && !isShowingSequence)
? 'bg-green-900/80 border-green-500 text-green-300 hover:bg-green-800 hover:scale-105 active:scale-95'
: 'bg-gray-900 border-gray-700 text-gray-500 cursor-not-allowed opacity-70'}
`}
>
๐ฟ
Use Catnip
{catnip}
{/* Hack Pad */}
{colors.map((color, idx) => {
const isFlashing = isShowingSequence && activeSeqIdx === idx;
const isPressed = !isShowingSequence && playerInput[playerInput.length - 1] === idx && message.includes("Hack");
return (
handleColorClick(idx)}
disabled={isShowingSequence || bossHp <= 0}
className={`
h-20 sm:h-28 rounded-2xl flex items-center justify-center text-4xl sm:text-5xl shadow-lg transition-all duration-150
${color}
${isFlashing ? 'brightness-150 scale-105 shadow-[0_0_40px_rgba(255,255,255,1)] ring-4 ring-white' : 'brightness-75'}
${(!isShowingSequence && bossHp > 0) ? 'hover:brightness-110 active:scale-95 active:brightness-150 active:ring-2 active:ring-white' : ''}
border-b-8 border-black/40 active:border-b-0 active:translate-y-2
`}
>
{symbols[idx]}
)
})}
);
};
// --- MAIN APP ---
export default function App() {
const [gameState, setGameState] = useState('MENU'); // MENU, EXPLORE, CUTSCENE, SHOOTER, BOSS, WIN, LOSE
const [currentLevel, setCurrentLevel] = useState(0);
// Global Ship Stats
const [catnip, setCatnip] = useState(0);
const [shipHp, setShipHp] = useState(5);
const maxShipHp = 5;
const resetGame = () => {
setGameState('MENU');
setCurrentLevel(0);
setCatnip(0);
setShipHp(maxShipHp);
};
const advanceLevel = () => {
if (currentLevel + 1 >= LEVELS.length) {
setGameState('WIN');
} else {
setCurrentLevel(prev => prev + 1);
setGameState('EXPLORE');
}
};
const levelData = LEVELS[currentLevel];
return (
{/* Main Menu */}
{gameState === 'MENU' && (
โ๏ธ
๐ช
Astro-Paws
The Galaxy Defenders
{CHARACTERS.WHISKERS.icon}
{CHARACTERS.LUNA.icon}
{CHARACTERS.REX.icon}
{CHARACTERS.CLEO.icon}
setGameState('EXPLORE')}
className="group relative px-10 py-5 bg-gradient-to-r from-orange-600 to-orange-400 hover:from-orange-500 hover:to-orange-300 text-white font-black rounded-full text-2xl sm:text-3xl transition-all hover:scale-105 active:scale-95 shadow-[0_0_30px_rgba(249,115,22,0.6)] z-10 flex items-center gap-4 uppercase"
>
BEGIN CAMPAIGN
Mission Briefing:
Barkor and his 10 generals have stolen the Sacred Yarn! Explore the ship to find healing Catnip. Blast through enemy drone waves, then hack the boss shields. Save the galaxy!
)}
{/* Core Loop */}
{gameState === 'EXPLORE' && (
setGameState('CUTSCENE')}
/>
)}
{gameState === 'CUTSCENE' && (
setGameState('SHOOTER')}
/>
)}
{gameState === 'SHOOTER' && (
setGameState('BOSS')}
onLose={() => setGameState('LOSE')}
/>
)}
{gameState === 'BOSS' && (
setGameState('LOSE')}
/>
)}
{/* Game Over States */}
{gameState === 'LOSE' && (
Mission Failed
The Sacred Yarn was unraveled...
You made it to Sector {currentLevel + 1} / 11
๐ฟ
Restart Campaign
)}
{gameState === 'WIN' && (
Universe Saved!
Mecha-Barkor has been dismantled and the 10 Generals are in the galactic pound.
The Sacred Yarn of Destiny is safe. The crew earned a well-deserved nap.
๐ผ๐โโฌ๐
๐๐งถ
Play Again
)}
);
}