This commit is contained in:
gpt-engineer-app[bot]
2026-01-17 07:42:37 +00:00
parent 6ce51f7005
commit 0c515caee0
4 changed files with 66 additions and 43 deletions
+1 -1
View File
@@ -44,7 +44,7 @@ const games = [
id: 'breakout',
name: 'Breakout',
description: 'Break bricks with a bouncing ball',
hasMultiplayer: true,
hasMultiplayer: false,
ascii: `┌────────┐
│████████│
│▓▓▓▓▓▓▓▓│
+27 -9
View File
@@ -477,40 +477,58 @@ const Pacman = () => {
// Player 1 controls (WASD and Arrow keys) - immediate response
switch (e.key) {
case 'ArrowUp': case 'w': case 'W':
// In 2P mode, P1 uses WASD only; in 1P mode, use both WASD and Arrows
case 'ArrowUp':
e.preventDefault();
if (gameMode === '1p' && !player1Eliminated) setNextDirection('up');
break;
case 'ArrowDown':
e.preventDefault();
if (gameMode === '1p' && !player1Eliminated) setNextDirection('down');
break;
case 'ArrowLeft':
e.preventDefault();
if (gameMode === '1p' && !player1Eliminated) setNextDirection('left');
break;
case 'ArrowRight':
e.preventDefault();
if (gameMode === '1p' && !player1Eliminated) setNextDirection('right');
break;
case 'w': case 'W':
e.preventDefault();
if (!player1Eliminated) setNextDirection('up');
break;
case 'ArrowDown': case 's': case 'S':
case 's': case 'S':
e.preventDefault();
if (!player1Eliminated) setNextDirection('down');
break;
case 'ArrowLeft': case 'a': case 'A':
case 'a': case 'A':
e.preventDefault();
if (!player1Eliminated) setNextDirection('left');
break;
case 'ArrowRight': case 'd': case 'D':
case 'd': case 'D':
e.preventDefault();
if (!player1Eliminated) setNextDirection('right');
break;
}
// Player 2 controls (IJKL keys) - only in 2P mode
// Player 2 controls (Arrow keys) - only in 2P mode
// P1 uses WASD, P2 uses Arrow keys
if (gameMode === '2p') {
switch (e.key) {
case 'i': case 'I':
case 'ArrowUp':
e.preventDefault();
if (!player2Eliminated) setNextDirection2('up');
break;
case 'k': case 'K':
case 'ArrowDown':
e.preventDefault();
if (!player2Eliminated) setNextDirection2('down');
break;
case 'j': case 'J':
case 'ArrowLeft':
e.preventDefault();
if (!player2Eliminated) setNextDirection2('left');
break;
case 'l': case 'L':
case 'ArrowRight':
e.preventDefault();
if (!player2Eliminated) setNextDirection2('right');
break;
+21 -16
View File
@@ -45,7 +45,7 @@ const Snake = () => {
// Multiplayer state
const [players, setPlayers] = useState<[Player, Player]>([
{ snake: [{ x: 5, y: 10 }], direction: 'right', score: 0, alive: true, color: 'hsl(var(--primary))', name: 'P1' },
{ snake: [{ x: 15, y: 10 }], direction: 'left', score: 0, alive: true, color: 'hsl(280 70% 50%)', name: 'P2' },
{ snake: [{ x: 15, y: 10 }], direction: 'left', score: 0, alive: true, color: 'hsl(var(--secondary))', name: 'P2' },
]);
const [food2P, setFood2P] = useState<Position>({ x: 10, y: 10 });
const [winner, setWinner] = useState<string | null>(null);
@@ -198,7 +198,7 @@ const Snake = () => {
const p2Start = [{ x: 15, y: 10 }];
setPlayers([
{ snake: p1Start, direction: 'right', score: 0, alive: true, color: 'hsl(var(--primary))', name: 'P1' },
{ snake: p2Start, direction: 'left', score: 0, alive: true, color: 'hsl(280 70% 50%)', name: 'P2' },
{ snake: p2Start, direction: 'left', score: 0, alive: true, color: 'hsl(var(--secondary))', name: 'P2' },
]);
currentDirections2P.current = ['right', 'left'];
directionQueues2P.current = [[], []];
@@ -448,11 +448,11 @@ const Snake = () => {
style.backgroundColor = players[0].color;
style.opacity = 0.8;
} else if (isP2Head) {
bgClass = 'border-purple-500';
bgClass = 'border-secondary';
style.backgroundColor = players[1].color;
style.boxShadow = `0 0 10px ${players[1].color}`;
} else if (isP2 && players[1].alive) {
bgClass = 'border-purple-500/60';
bgClass = 'border-secondary/60';
style.backgroundColor = players[1].color;
style.opacity = 0.8;
} else if (isFood) {
@@ -507,10 +507,10 @@ const Snake = () => {
{!isMobile && (
<button
onClick={() => startGame('2p')}
className="flex flex-col items-center gap-2 p-6 border-2 border-purple-500/50 hover:border-purple-500 bg-background/50 hover:bg-purple-500/10 transition-all"
className="flex flex-col items-center gap-2 p-6 border-2 border-secondary/50 hover:border-secondary bg-background/50 hover:bg-secondary/10 transition-all"
>
<Users size={32} className="text-purple-400" />
<span className="font-minecraft text-lg text-purple-400">2 Players</span>
<Users size={32} className="text-secondary" />
<span className="font-minecraft text-lg text-secondary">2 Players</span>
<span className="font-pixel text-xs text-foreground/60">Local versus</span>
</button>
)}
@@ -543,7 +543,7 @@ const Snake = () => {
<div className="flex items-center gap-4">
<button onClick={() => { setGameMode(null); setGameStarted(false); }} className="font-pixel text-sm text-foreground/50 hover:text-primary transition-colors">{'<'} Back</button>
<h1 className="font-minecraft text-2xl md:text-3xl text-primary text-glow-strong">
Snake {gameMode === '2p' && <span className="text-purple-400">2P</span>}
Snake {gameMode === '2p' && <span className="text-secondary">2P</span>}
</h1>
</div>
<button onClick={toggleFullscreen} className="p-2 border border-primary/50 hover:bg-primary/20 transition-colors" title={isFullscreen ? 'Exit fullscreen' : 'Fullscreen'}>
@@ -615,13 +615,13 @@ const Snake = () => {
</div>
{/* Player 2 */}
<div className="border-2 border-purple-500/60 p-4 bg-background/60">
<div className="border-2 border-secondary/60 p-4 bg-background/60">
<div className="flex items-center gap-2 mb-2">
<div className="w-4 h-4 rounded-sm" style={{ backgroundColor: players[1].color, boxShadow: '0 0 8px ' + players[1].color }} />
<p className="font-pixel text-xs text-purple-400">Player 2</p>
<p className="font-pixel text-xs text-secondary">Player 2</p>
<span className="font-pixel text-[8px] text-foreground/40 ml-auto">Arrows</span>
</div>
<p className="font-minecraft text-2xl text-purple-400" style={{ textShadow: '0 0 10px hsl(280 70% 50%)' }}>{players[1].score}</p>
<p className="font-minecraft text-2xl text-secondary" style={{ textShadow: '0 0 10px hsl(var(--secondary))' }}>{players[1].score}</p>
{!players[1].alive && <p className="font-pixel text-[10px] text-destructive mt-1 animate-pulse"> ELIMINATED</p>}
</div>
@@ -634,7 +634,7 @@ const Snake = () => {
<p className="font-pixel text-[10px] text-foreground/60">W A S D</p>
</div>
<div>
<p className="font-pixel text-[8px] text-purple-400">P2</p>
<p className="font-pixel text-[8px] text-secondary">P2</p>
<p className="font-pixel text-[10px] text-foreground/60"> </p>
</div>
</div>
@@ -707,13 +707,18 @@ const Snake = () => {
</div>
<div>
<p className="font-pixel text-[10px] text-foreground/60">P2 Score</p>
<p className="font-minecraft text-lg text-purple-400">{players[1].score}</p>
<p className="font-minecraft text-lg text-secondary">{players[1].score}</p>
</div>
</div>
)}
<button onClick={(gameOver || gameComplete) ? () => startGame(gameMode!) : () => setIsPaused(false)} className="font-minecraft text-sm py-2 px-6 border border-primary bg-primary/20 text-primary hover:bg-primary/40 transition-all box-glow">
{(gameOver || gameComplete) ? 'PLAY AGAIN' : 'RESUME'}
</button>
<div className="flex gap-3 justify-center">
<Link to="/games" className="font-minecraft text-sm py-2 px-6 border border-foreground/30 bg-background/50 text-foreground/70 hover:bg-foreground/10 transition-all">
BACK
</Link>
<button onClick={(gameOver || gameComplete) ? () => startGame(gameMode!) : () => setIsPaused(false)} className="font-minecraft text-sm py-2 px-6 border border-primary bg-primary/20 text-primary hover:bg-primary/40 transition-all box-glow">
{(gameOver || gameComplete) ? 'PLAY AGAIN' : 'RESUME'}
</button>
</div>
</div>
</div>
)}
+17 -17
View File
@@ -29,13 +29,13 @@ const TETROMINOS = {
};
const TETROMINOS_P2 = {
I: { shape: [[1, 1, 1, 1]], color: 'hsl(280 70% 50%)' },
O: { shape: [[1, 1], [1, 1]], color: 'hsl(280 70% 50%)' },
T: { shape: [[0, 1, 0], [1, 1, 1]], color: 'hsl(280 70% 50%)' },
S: { shape: [[0, 1, 1], [1, 1, 0]], color: 'hsl(280 70% 50%)' },
Z: { shape: [[1, 1, 0], [0, 1, 1]], color: 'hsl(280 70% 50%)' },
J: { shape: [[1, 0, 0], [1, 1, 1]], color: 'hsl(280 70% 50%)' },
L: { shape: [[0, 0, 1], [1, 1, 1]], color: 'hsl(280 70% 50%)' },
I: { shape: [[1, 1, 1, 1]], color: 'hsl(var(--secondary))' },
O: { shape: [[1, 1], [1, 1]], color: 'hsl(var(--secondary))' },
T: { shape: [[0, 1, 0], [1, 1, 1]], color: 'hsl(var(--secondary))' },
S: { shape: [[0, 1, 1], [1, 1, 0]], color: 'hsl(var(--secondary))' },
Z: { shape: [[1, 1, 0], [0, 1, 1]], color: 'hsl(var(--secondary))' },
J: { shape: [[1, 0, 0], [1, 1, 1]], color: 'hsl(var(--secondary))' },
L: { shape: [[0, 0, 1], [1, 1, 1]], color: 'hsl(var(--secondary))' },
};
type TetrominoKey = keyof typeof TETROMINOS;
@@ -519,9 +519,9 @@ const Tetris = () => {
<span className="font-pixel text-xs text-foreground/60">Solo mode</span>
</button>
{!isMobile && (
<button onClick={() => startGame('2p')} className="flex flex-col items-center gap-2 p-6 border-2 border-purple-500/50 hover:border-purple-500 bg-background/50 hover:bg-purple-500/10 transition-all">
<Users size={32} className="text-purple-400" />
<span className="font-minecraft text-lg text-purple-400">2 Players</span>
<button onClick={() => startGame('2p')} className="flex flex-col items-center gap-2 p-6 border-2 border-secondary/50 hover:border-secondary bg-background/50 hover:bg-secondary/10 transition-all">
<Users size={32} className="text-secondary" />
<span className="font-minecraft text-lg text-secondary">2 Players</span>
<span className="font-pixel text-xs text-foreground/60">Local versus</span>
</button>
)}
@@ -542,7 +542,7 @@ const Tetris = () => {
<div className="flex items-center gap-4">
<button onClick={() => { setGameMode(null); setGameStarted(false); }} className="font-pixel text-sm text-foreground/50 hover:text-primary transition-colors">{'<'} Back</button>
<h1 className="font-minecraft text-2xl md:text-3xl text-primary text-glow-strong">
Tetris {gameMode === '2p' && <span className="text-purple-400">2P</span>}
Tetris {gameMode === '2p' && <span className="text-secondary">2P</span>}
</h1>
</div>
<button onClick={toggleFullscreen} className="p-2 border border-primary/50 hover:bg-primary/20 transition-colors" title={isFullscreen ? 'Exit fullscreen' : 'Fullscreen'}>
@@ -610,13 +610,13 @@ const Tetris = () => {
</div>
</div>
<div className="flex flex-col items-center gap-2">
<p className="font-minecraft text-lg text-purple-400">P2 <span className="text-xs text-foreground/60">(Arrows + /)</span></p>
<div className="border-2 border-purple-500 p-1 bg-background/80" style={{ boxShadow: '0 0 15px hsl(280 70% 50% / 0.5)' }}>{renderBoard2P(player2, 'border-purple-500/20')}</div>
<p className="font-minecraft text-lg text-secondary">P2 <span className="text-xs text-foreground/60">(Arrows + /)</span></p>
<div className="border-2 border-secondary p-1 bg-background/80" style={{ boxShadow: '0 0 15px hsl(var(--secondary) / 0.5)' }}>{renderBoard2P(player2, 'border-secondary/20')}</div>
<div className="flex gap-4 text-center">
<div><p className="font-pixel text-[10px] text-foreground/60">Score</p><p className="font-minecraft text-lg text-purple-400">{player2.score}</p></div>
<div><p className="font-pixel text-[10px] text-foreground/60">Lines</p><p className="font-minecraft text-lg text-purple-400">{player2.lines}</p></div>
<div><p className="font-pixel text-[10px] text-foreground/60">Score</p><p className="font-minecraft text-lg text-secondary">{player2.score}</p></div>
<div><p className="font-pixel text-[10px] text-foreground/60">Lines</p><p className="font-minecraft text-lg text-secondary">{player2.lines}</p></div>
</div>
<div className="border border-purple-500/30 p-2 bg-background/40">
<div className="border border-secondary/30 p-2 bg-background/40">
<p className="font-pixel text-[8px] text-foreground/40">Next</p>
{renderNextPiece(player2.nextPiece)}
</div>
@@ -662,7 +662,7 @@ const Tetris = () => {
{gameOver && gameMode === '2p' && (
<div className="flex gap-8 mb-4 justify-center">
<div><p className="font-pixel text-xs text-foreground/60">P1</p><p className="font-minecraft text-lg text-primary">{player1.score}</p></div>
<div><p className="font-pixel text-xs text-foreground/60">P2</p><p className="font-minecraft text-lg text-purple-400">{player2.score}</p></div>
<div><p className="font-pixel text-xs text-foreground/60">P2</p><p className="font-minecraft text-lg text-secondary">{player2.score}</p></div>
</div>
)}
<div className="flex gap-4 justify-center">