Changes
This commit is contained in:
+54
-9
@@ -68,6 +68,10 @@ const Pacman = () => {
|
|||||||
const [player1Eliminated, setPlayer1Eliminated] = useState(false);
|
const [player1Eliminated, setPlayer1Eliminated] = useState(false);
|
||||||
const [player2Eliminated, setPlayer2Eliminated] = useState(false);
|
const [player2Eliminated, setPlayer2Eliminated] = useState(false);
|
||||||
const [showEliminationPrompt, setShowEliminationPrompt] = useState(false);
|
const [showEliminationPrompt, setShowEliminationPrompt] = useState(false);
|
||||||
|
const [p1Invulnerable, setP1Invulnerable] = useState(false);
|
||||||
|
const [p2Invulnerable, setP2Invulnerable] = useState(false);
|
||||||
|
const invulnerableTimerRef = useRef<NodeJS.Timeout | null>(null);
|
||||||
|
const invulnerable2TimerRef = useRef<NodeJS.Timeout | null>(null);
|
||||||
const [ghosts, setGhosts] = useState<{ pos: Position; dir: Direction; eaten: boolean }[]>([
|
const [ghosts, setGhosts] = useState<{ pos: Position; dir: Direction; eaten: boolean }[]>([
|
||||||
{ pos: { x: 9, y: 9 }, dir: 'left', eaten: false },
|
{ pos: { x: 9, y: 9 }, dir: 'left', eaten: false },
|
||||||
{ pos: { x: 10, y: 9 }, dir: 'up', eaten: false },
|
{ pos: { x: 10, y: 9 }, dir: 'up', eaten: false },
|
||||||
@@ -211,10 +215,38 @@ const Pacman = () => {
|
|||||||
setGameMode('1p');
|
setGameMode('1p');
|
||||||
setShowEliminationPrompt(false);
|
setShowEliminationPrompt(false);
|
||||||
setIsPaused(false);
|
setIsPaused(false);
|
||||||
|
// If P1 was eliminated, remaining player continues as P1
|
||||||
|
if (player1Eliminated && !player2Eliminated) {
|
||||||
|
// P2 becomes P1
|
||||||
|
setPacman(pacman2);
|
||||||
|
setDirection(direction2);
|
||||||
|
setNextDirection(nextDirection2);
|
||||||
|
setScore(score2);
|
||||||
|
setLives(lives2);
|
||||||
|
setPlayer1Eliminated(false);
|
||||||
|
setPacman2({ x: -1, y: -1 });
|
||||||
|
}
|
||||||
|
// If P2 was eliminated, P1 just continues normally
|
||||||
playSound('success');
|
playSound('success');
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => { return () => { if (powerTimerRef.current) clearTimeout(powerTimerRef.current); }; }, []);
|
const makeP1Invulnerable = () => {
|
||||||
|
if (invulnerableTimerRef.current) clearTimeout(invulnerableTimerRef.current);
|
||||||
|
setP1Invulnerable(true);
|
||||||
|
invulnerableTimerRef.current = setTimeout(() => setP1Invulnerable(false), 3000);
|
||||||
|
};
|
||||||
|
|
||||||
|
const makeP2Invulnerable = () => {
|
||||||
|
if (invulnerable2TimerRef.current) clearTimeout(invulnerable2TimerRef.current);
|
||||||
|
setP2Invulnerable(true);
|
||||||
|
invulnerable2TimerRef.current = setTimeout(() => setP2Invulnerable(false), 3000);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => { return () => {
|
||||||
|
if (powerTimerRef.current) clearTimeout(powerTimerRef.current);
|
||||||
|
if (invulnerableTimerRef.current) clearTimeout(invulnerableTimerRef.current);
|
||||||
|
if (invulnerable2TimerRef.current) clearTimeout(invulnerable2TimerRef.current);
|
||||||
|
}; }, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!gameStarted || gameOver || gameComplete || isPaused) return;
|
if (!gameStarted || gameOver || gameComplete || isPaused) return;
|
||||||
@@ -260,8 +292,12 @@ const Pacman = () => {
|
|||||||
});
|
});
|
||||||
playSound('success');
|
playSound('success');
|
||||||
} else if (!ghostAtTarget.eaten) {
|
} else if (!ghostAtTarget.eaten) {
|
||||||
|
// Check invulnerability
|
||||||
|
const isPlayer1 = setPlayer === setPacman;
|
||||||
|
const isInvulnerable = isPlayer1 ? p1Invulnerable : p2Invulnerable;
|
||||||
|
|
||||||
|
if (!isInvulnerable) {
|
||||||
// Hit by ghost - lose life without moving
|
// Hit by ghost - lose life without moving
|
||||||
const isPlayer1 = player === pacman;
|
|
||||||
const currentLives = isPlayer1 ? lives : lives2;
|
const currentLives = isPlayer1 ? lives : lives2;
|
||||||
const setLivesFunc = isPlayer1 ? setLives : setLives2;
|
const setLivesFunc = isPlayer1 ? setLives : setLives2;
|
||||||
|
|
||||||
@@ -269,8 +305,10 @@ const Pacman = () => {
|
|||||||
setLivesFunc(newLives);
|
setLivesFunc(newLives);
|
||||||
|
|
||||||
if (newLives > 0) {
|
if (newLives > 0) {
|
||||||
// Respawn at start
|
// Respawn at start with invulnerability
|
||||||
setPlayer({ x: 10, y: 15 });
|
setPlayer({ x: 10, y: 15 });
|
||||||
|
if (isPlayer1) makeP1Invulnerable();
|
||||||
|
else makeP2Invulnerable();
|
||||||
playSound('error');
|
playSound('error');
|
||||||
} else {
|
} else {
|
||||||
// Player eliminated
|
// Player eliminated
|
||||||
@@ -290,6 +328,10 @@ const Pacman = () => {
|
|||||||
setGameOver(true);
|
setGameOver(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// Invulnerable - just move through
|
||||||
|
setPlayer(newPos);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// No ghost at target, safe to move
|
// No ghost at target, safe to move
|
||||||
@@ -337,12 +379,13 @@ const Pacman = () => {
|
|||||||
return ns;
|
return ns;
|
||||||
});
|
});
|
||||||
playSound('success');
|
playSound('success');
|
||||||
} else if (!ghost.eaten) {
|
} else if (!ghost.eaten && !p1Invulnerable) {
|
||||||
// Player 1 dies
|
// Player 1 dies
|
||||||
const newLives = lives - 1;
|
const newLives = lives - 1;
|
||||||
setLives(newLives);
|
setLives(newLives);
|
||||||
if (newLives > 0) {
|
if (newLives > 0) {
|
||||||
setPacman({ x: 10, y: 15 });
|
setPacman({ x: 10, y: 15 });
|
||||||
|
makeP1Invulnerable();
|
||||||
playSound('error');
|
playSound('error');
|
||||||
} else {
|
} else {
|
||||||
setPlayer1Eliminated(true);
|
setPlayer1Eliminated(true);
|
||||||
@@ -402,12 +445,13 @@ const Pacman = () => {
|
|||||||
});
|
});
|
||||||
playSound('success');
|
playSound('success');
|
||||||
return currentGhosts.map((g, idx) => idx === i ? { ...g, eaten: true, pos: { x: 10, y: 9 } } : g);
|
return currentGhosts.map((g, idx) => idx === i ? { ...g, eaten: true, pos: { x: 10, y: 9 } } : g);
|
||||||
} else if (!ghost.eaten) {
|
} else if (!ghost.eaten && !p1Invulnerable) {
|
||||||
// Ghost kills player
|
// Ghost kills player
|
||||||
const newLives = lives - 1;
|
const newLives = lives - 1;
|
||||||
setLives(newLives);
|
setLives(newLives);
|
||||||
if (newLives > 0) {
|
if (newLives > 0) {
|
||||||
setPacman({ x: 10, y: 15 });
|
setPacman({ x: 10, y: 15 });
|
||||||
|
makeP1Invulnerable();
|
||||||
playSound('error');
|
playSound('error');
|
||||||
} else {
|
} else {
|
||||||
setPlayer1Eliminated(true);
|
setPlayer1Eliminated(true);
|
||||||
@@ -443,12 +487,13 @@ const Pacman = () => {
|
|||||||
});
|
});
|
||||||
playSound('success');
|
playSound('success');
|
||||||
return currentGhosts.map((g, idx) => idx === i ? { ...g, eaten: true, pos: { x: 10, y: 9 } } : g);
|
return currentGhosts.map((g, idx) => idx === i ? { ...g, eaten: true, pos: { x: 10, y: 9 } } : g);
|
||||||
} else if (!ghost.eaten) {
|
} else if (!ghost.eaten && !p2Invulnerable) {
|
||||||
// Ghost kills player
|
// Ghost kills player
|
||||||
const newLives = lives2 - 1;
|
const newLives = lives2 - 1;
|
||||||
setLives2(newLives);
|
setLives2(newLives);
|
||||||
if (newLives > 0) {
|
if (newLives > 0) {
|
||||||
setPacman2({ x: 10, y: 15 });
|
setPacman2({ x: 10, y: 15 });
|
||||||
|
makeP2Invulnerable();
|
||||||
playSound('error');
|
playSound('error');
|
||||||
} else {
|
} else {
|
||||||
setPlayer2Eliminated(true);
|
setPlayer2Eliminated(true);
|
||||||
@@ -469,7 +514,7 @@ const Pacman = () => {
|
|||||||
}
|
}
|
||||||
}, TICK_SPEED);
|
}, TICK_SPEED);
|
||||||
return () => clearInterval(interval);
|
return () => clearInterval(interval);
|
||||||
}, [gameStarted, gameOver, gameComplete, isPaused, pacman, direction, nextDirection, pacman2, direction2, nextDirection2, dots, powerPellets, highScore, isPowered, playSound, gameMode, player1Eliminated, player2Eliminated]);
|
}, [gameStarted, gameOver, gameComplete, isPaused, pacman, direction, nextDirection, pacman2, direction2, nextDirection2, dots, powerPellets, highScore, isPowered, playSound, gameMode, player1Eliminated, player2Eliminated, p1Invulnerable, p2Invulnerable, lives, lives2]);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -604,8 +649,8 @@ const Pacman = () => {
|
|||||||
const isPowerPellet = powerPellets.has(`${x},${y}`);
|
const isPowerPellet = powerPellets.has(`${x},${y}`);
|
||||||
cells.push(
|
cells.push(
|
||||||
<div key={`${x}-${y}`} className={`flex items-center justify-center transition-all duration-200 ease-out ${isWall ? 'bg-primary/20 border border-primary/40' : isTunnel ? 'bg-background/30' : 'bg-background/50 border border-primary/5'}`} style={{ width: cellSize, height: cellSize }}>
|
<div key={`${x}-${y}`} className={`flex items-center justify-center transition-all duration-200 ease-out ${isWall ? 'bg-primary/20 border border-primary/40' : isTunnel ? 'bg-background/30' : 'bg-background/50 border border-primary/5'}`} style={{ width: cellSize, height: cellSize }}>
|
||||||
{isPacmanHere && <div className="drop-shadow-lg" style={{ width: spriteSize, height: spriteSize, transition: 'all 0.2s ease-out', filter: 'drop-shadow(0 0 4px hsl(var(--primary)))' }}>{renderPacman()}</div>}
|
{isPacmanHere && <div className={`drop-shadow-lg ${p1Invulnerable ? 'animate-pulse' : ''}`} style={{ width: spriteSize, height: spriteSize, transition: 'all 0.2s ease-out', filter: 'drop-shadow(0 0 4px hsl(var(--primary)))', opacity: p1Invulnerable ? 0.5 : 1 }}>{renderPacman()}</div>}
|
||||||
{isPacman2Here && <div className="drop-shadow-lg" style={{ width: spriteSize, height: spriteSize, transition: 'all 0.2s ease-out', filter: 'drop-shadow(0 0 4px hsl(120 70% 50%))' }}>{renderPacman2()}</div>}
|
{isPacman2Here && <div className={`drop-shadow-lg ${p2Invulnerable ? 'animate-pulse' : ''}`} style={{ width: spriteSize, height: spriteSize, transition: 'all 0.2s ease-out', filter: 'drop-shadow(0 0 4px hsl(120 70% 50%))', opacity: p2Invulnerable ? 0.5 : 1 }}>{renderPacman2()}</div>}
|
||||||
{ghostIndex !== -1 && !isPacmanHere && !isPacman2Here && <div style={{ width: spriteSize, height: spriteSize, transition: 'all 0.2s ease-out' }}>{renderGhost(ghostIndex, ghosts[ghostIndex].eaten)}</div>}
|
{ghostIndex !== -1 && !isPacmanHere && !isPacman2Here && <div style={{ width: spriteSize, height: spriteSize, transition: 'all 0.2s ease-out' }}>{renderGhost(ghostIndex, ghosts[ghostIndex].eaten)}</div>}
|
||||||
{isDot && !isPacmanHere && !isPacman2Here && ghostIndex === -1 && <div className="w-1.5 h-1.5 bg-primary/80 rounded-full transition-all duration-200 ease-out animate-pulse" style={{ animationDuration: '2s' }} />}
|
{isDot && !isPacmanHere && !isPacman2Here && ghostIndex === -1 && <div className="w-1.5 h-1.5 bg-primary/80 rounded-full transition-all duration-200 ease-out animate-pulse" style={{ animationDuration: '2s' }} />}
|
||||||
{isPowerPellet && !isPacmanHere && !isPacman2Here && ghostIndex === -1 && <div className="w-3 h-3 bg-primary rounded-full animate-pulse box-glow transition-all duration-200 ease-out" />}
|
{isPowerPellet && !isPacmanHere && !isPacman2Here && ghostIndex === -1 && <div className="w-3 h-3 bg-primary rounded-full animate-pulse box-glow transition-all duration-200 ease-out" />}
|
||||||
|
|||||||
Reference in New Issue
Block a user