Changes
This commit is contained in:
+1
-1
@@ -44,7 +44,7 @@ const games = [
|
|||||||
id: 'breakout',
|
id: 'breakout',
|
||||||
name: 'Breakout',
|
name: 'Breakout',
|
||||||
description: 'Break bricks with a bouncing ball',
|
description: 'Break bricks with a bouncing ball',
|
||||||
hasMultiplayer: true,
|
hasMultiplayer: false,
|
||||||
ascii: `┌────────┐
|
ascii: `┌────────┐
|
||||||
│████████│
|
│████████│
|
||||||
│▓▓▓▓▓▓▓▓│
|
│▓▓▓▓▓▓▓▓│
|
||||||
|
|||||||
+27
-9
@@ -477,40 +477,58 @@ const Pacman = () => {
|
|||||||
|
|
||||||
// Player 1 controls (WASD and Arrow keys) - immediate response
|
// Player 1 controls (WASD and Arrow keys) - immediate response
|
||||||
switch (e.key) {
|
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();
|
e.preventDefault();
|
||||||
if (!player1Eliminated) setNextDirection('up');
|
if (!player1Eliminated) setNextDirection('up');
|
||||||
break;
|
break;
|
||||||
case 'ArrowDown': case 's': case 'S':
|
case 's': case 'S':
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (!player1Eliminated) setNextDirection('down');
|
if (!player1Eliminated) setNextDirection('down');
|
||||||
break;
|
break;
|
||||||
case 'ArrowLeft': case 'a': case 'A':
|
case 'a': case 'A':
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (!player1Eliminated) setNextDirection('left');
|
if (!player1Eliminated) setNextDirection('left');
|
||||||
break;
|
break;
|
||||||
case 'ArrowRight': case 'd': case 'D':
|
case 'd': case 'D':
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (!player1Eliminated) setNextDirection('right');
|
if (!player1Eliminated) setNextDirection('right');
|
||||||
break;
|
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') {
|
if (gameMode === '2p') {
|
||||||
switch (e.key) {
|
switch (e.key) {
|
||||||
case 'i': case 'I':
|
case 'ArrowUp':
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (!player2Eliminated) setNextDirection2('up');
|
if (!player2Eliminated) setNextDirection2('up');
|
||||||
break;
|
break;
|
||||||
case 'k': case 'K':
|
case 'ArrowDown':
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (!player2Eliminated) setNextDirection2('down');
|
if (!player2Eliminated) setNextDirection2('down');
|
||||||
break;
|
break;
|
||||||
case 'j': case 'J':
|
case 'ArrowLeft':
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (!player2Eliminated) setNextDirection2('left');
|
if (!player2Eliminated) setNextDirection2('left');
|
||||||
break;
|
break;
|
||||||
case 'l': case 'L':
|
case 'ArrowRight':
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (!player2Eliminated) setNextDirection2('right');
|
if (!player2Eliminated) setNextDirection2('right');
|
||||||
break;
|
break;
|
||||||
|
|||||||
+21
-16
@@ -45,7 +45,7 @@ const Snake = () => {
|
|||||||
// Multiplayer state
|
// Multiplayer state
|
||||||
const [players, setPlayers] = useState<[Player, Player]>([
|
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: 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 [food2P, setFood2P] = useState<Position>({ x: 10, y: 10 });
|
||||||
const [winner, setWinner] = useState<string | null>(null);
|
const [winner, setWinner] = useState<string | null>(null);
|
||||||
@@ -198,7 +198,7 @@ const Snake = () => {
|
|||||||
const p2Start = [{ x: 15, y: 10 }];
|
const p2Start = [{ x: 15, y: 10 }];
|
||||||
setPlayers([
|
setPlayers([
|
||||||
{ snake: p1Start, direction: 'right', score: 0, alive: true, color: 'hsl(var(--primary))', name: 'P1' },
|
{ 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'];
|
currentDirections2P.current = ['right', 'left'];
|
||||||
directionQueues2P.current = [[], []];
|
directionQueues2P.current = [[], []];
|
||||||
@@ -448,11 +448,11 @@ const Snake = () => {
|
|||||||
style.backgroundColor = players[0].color;
|
style.backgroundColor = players[0].color;
|
||||||
style.opacity = 0.8;
|
style.opacity = 0.8;
|
||||||
} else if (isP2Head) {
|
} else if (isP2Head) {
|
||||||
bgClass = 'border-purple-500';
|
bgClass = 'border-secondary';
|
||||||
style.backgroundColor = players[1].color;
|
style.backgroundColor = players[1].color;
|
||||||
style.boxShadow = `0 0 10px ${players[1].color}`;
|
style.boxShadow = `0 0 10px ${players[1].color}`;
|
||||||
} else if (isP2 && players[1].alive) {
|
} else if (isP2 && players[1].alive) {
|
||||||
bgClass = 'border-purple-500/60';
|
bgClass = 'border-secondary/60';
|
||||||
style.backgroundColor = players[1].color;
|
style.backgroundColor = players[1].color;
|
||||||
style.opacity = 0.8;
|
style.opacity = 0.8;
|
||||||
} else if (isFood) {
|
} else if (isFood) {
|
||||||
@@ -507,10 +507,10 @@ const Snake = () => {
|
|||||||
{!isMobile && (
|
{!isMobile && (
|
||||||
<button
|
<button
|
||||||
onClick={() => startGame('2p')}
|
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" />
|
<Users size={32} className="text-secondary" />
|
||||||
<span className="font-minecraft text-lg text-purple-400">2 Players</span>
|
<span className="font-minecraft text-lg text-secondary">2 Players</span>
|
||||||
<span className="font-pixel text-xs text-foreground/60">Local versus</span>
|
<span className="font-pixel text-xs text-foreground/60">Local versus</span>
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
@@ -543,7 +543,7 @@ const Snake = () => {
|
|||||||
<div className="flex items-center gap-4">
|
<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>
|
<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">
|
<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>
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
<button onClick={toggleFullscreen} className="p-2 border border-primary/50 hover:bg-primary/20 transition-colors" title={isFullscreen ? 'Exit fullscreen' : 'Fullscreen'}>
|
<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>
|
</div>
|
||||||
|
|
||||||
{/* Player 2 */}
|
{/* 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="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 }} />
|
<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>
|
<span className="font-pixel text-[8px] text-foreground/40 ml-auto">Arrows</span>
|
||||||
</div>
|
</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>}
|
{!players[1].alive && <p className="font-pixel text-[10px] text-destructive mt-1 animate-pulse">☠ ELIMINATED</p>}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -634,7 +634,7 @@ const Snake = () => {
|
|||||||
<p className="font-pixel text-[10px] text-foreground/60">W A S D</p>
|
<p className="font-pixel text-[10px] text-foreground/60">W A S D</p>
|
||||||
</div>
|
</div>
|
||||||
<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>
|
<p className="font-pixel text-[10px] text-foreground/60">↑ ↓ ← →</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -707,13 +707,18 @@ const Snake = () => {
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p className="font-pixel text-[10px] text-foreground/60">P2 Score</p>
|
<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>
|
||||||
</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">
|
<div className="flex gap-3 justify-center">
|
||||||
{(gameOver || gameComplete) ? 'PLAY AGAIN' : 'RESUME'}
|
<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">
|
||||||
</button>
|
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>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
+17
-17
@@ -29,13 +29,13 @@ const TETROMINOS = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const TETROMINOS_P2 = {
|
const TETROMINOS_P2 = {
|
||||||
I: { shape: [[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(280 70% 50%)' },
|
O: { shape: [[1, 1], [1, 1]], color: 'hsl(var(--secondary))' },
|
||||||
T: { shape: [[0, 1, 0], [1, 1, 1]], color: 'hsl(280 70% 50%)' },
|
T: { shape: [[0, 1, 0], [1, 1, 1]], color: 'hsl(var(--secondary))' },
|
||||||
S: { shape: [[0, 1, 1], [1, 1, 0]], color: 'hsl(280 70% 50%)' },
|
S: { shape: [[0, 1, 1], [1, 1, 0]], color: 'hsl(var(--secondary))' },
|
||||||
Z: { shape: [[1, 1, 0], [0, 1, 1]], color: 'hsl(280 70% 50%)' },
|
Z: { shape: [[1, 1, 0], [0, 1, 1]], color: 'hsl(var(--secondary))' },
|
||||||
J: { shape: [[1, 0, 0], [1, 1, 1]], color: 'hsl(280 70% 50%)' },
|
J: { shape: [[1, 0, 0], [1, 1, 1]], color: 'hsl(var(--secondary))' },
|
||||||
L: { shape: [[0, 0, 1], [1, 1, 1]], color: 'hsl(280 70% 50%)' },
|
L: { shape: [[0, 0, 1], [1, 1, 1]], color: 'hsl(var(--secondary))' },
|
||||||
};
|
};
|
||||||
|
|
||||||
type TetrominoKey = keyof typeof TETROMINOS;
|
type TetrominoKey = keyof typeof TETROMINOS;
|
||||||
@@ -519,9 +519,9 @@ const Tetris = () => {
|
|||||||
<span className="font-pixel text-xs text-foreground/60">Solo mode</span>
|
<span className="font-pixel text-xs text-foreground/60">Solo mode</span>
|
||||||
</button>
|
</button>
|
||||||
{!isMobile && (
|
{!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">
|
<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-purple-400" />
|
<Users size={32} className="text-secondary" />
|
||||||
<span className="font-minecraft text-lg text-purple-400">2 Players</span>
|
<span className="font-minecraft text-lg text-secondary">2 Players</span>
|
||||||
<span className="font-pixel text-xs text-foreground/60">Local versus</span>
|
<span className="font-pixel text-xs text-foreground/60">Local versus</span>
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
@@ -542,7 +542,7 @@ const Tetris = () => {
|
|||||||
<div className="flex items-center gap-4">
|
<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>
|
<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">
|
<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>
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
<button onClick={toggleFullscreen} className="p-2 border border-primary/50 hover:bg-primary/20 transition-colors" title={isFullscreen ? 'Exit fullscreen' : 'Fullscreen'}>
|
<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>
|
</div>
|
||||||
<div className="flex flex-col items-center gap-2">
|
<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>
|
<p className="font-minecraft text-lg text-secondary">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>
|
<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 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">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-purple-400">{player2.lines}</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>
|
||||||
<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>
|
<p className="font-pixel text-[8px] text-foreground/40">Next</p>
|
||||||
{renderNextPiece(player2.nextPiece)}
|
{renderNextPiece(player2.nextPiece)}
|
||||||
</div>
|
</div>
|
||||||
@@ -662,7 +662,7 @@ const Tetris = () => {
|
|||||||
{gameOver && gameMode === '2p' && (
|
{gameOver && gameMode === '2p' && (
|
||||||
<div className="flex gap-8 mb-4 justify-center">
|
<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">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>
|
||||||
)}
|
)}
|
||||||
<div className="flex gap-4 justify-center">
|
<div className="flex gap-4 justify-center">
|
||||||
|
|||||||
Reference in New Issue
Block a user