This commit is contained in:
gpt-engineer-app[bot]
2026-01-22 23:52:02 +00:00
parent ccca90c504
commit 07a2fe0c17
4 changed files with 34 additions and 36 deletions
+4 -9
View File
@@ -7,18 +7,13 @@ const MatrixCursor = () => {
const [isTouchDevice, setIsTouchDevice] = useState(false); const [isTouchDevice, setIsTouchDevice] = useState(false);
useEffect(() => { useEffect(() => {
// Detect touch device - check for touch capability and if primary input is touch // Detect touch device - only consider it a touch device if primary pointer is coarse (touch)
const checkTouchDevice = () => { const checkTouchDevice = () => {
const hasTouchScreen = 'ontouchstart' in window || // Check if the device has a fine pointer (mouse) as primary input
navigator.maxTouchPoints > 0 ||
// @ts-ignore - msMaxTouchPoints is IE-specific
navigator.msMaxTouchPoints > 0;
// Also check if the device has a fine pointer (mouse)
const hasFinePrimary = window.matchMedia('(pointer: fine)').matches; const hasFinePrimary = window.matchMedia('(pointer: fine)').matches;
// Consider it a touch device if it has touch AND doesn't have fine pointer as primary // Consider it a touch device only if it doesn't have a fine pointer as primary
setIsTouchDevice(hasTouchScreen && !hasFinePrimary); setIsTouchDevice(!hasFinePrimary);
}; };
checkTouchDevice(); checkTouchDevice();
+11 -1
View File
@@ -261,6 +261,16 @@ const Breakout = () => {
paddleXRef.current = Math.max(0, Math.min(canvasSize.width - paddleWidth, x - paddleWidth / 2)); paddleXRef.current = Math.max(0, Math.min(canvasSize.width - paddleWidth, x - paddleWidth / 2));
}, [gameStarted, gameOver, isPaused, canvasSize.width, paddleWidth]); }, [gameStarted, gameOver, isPaused, canvasSize.width, paddleWidth]);
// Mouse move handler for desktop
const handleMouseMove = useCallback((e: React.MouseEvent) => {
if (!gameStarted || gameOver || isPaused || isMobile || isTwoPlayer) return;
const canvas = canvasRef.current;
if (!canvas) return;
const rect = canvas.getBoundingClientRect();
const x = e.clientX - rect.left;
paddleXRef.current = Math.max(0, Math.min(canvasSize.width - paddleWidth, x - paddleWidth / 2));
}, [gameStarted, gameOver, isPaused, canvasSize.width, paddleWidth, isMobile, isTwoPlayer]);
const moveLeft = useCallback(() => { paddleXRef.current = Math.max(0, paddleXRef.current - 20); }, []); const moveLeft = useCallback(() => { paddleXRef.current = Math.max(0, paddleXRef.current - 20); }, []);
const moveRight = useCallback(() => { paddleXRef.current = Math.min(canvasSize.width - paddleWidth, paddleXRef.current + 20); }, [canvasSize.width, paddleWidth]); const moveRight = useCallback(() => { paddleXRef.current = Math.min(canvasSize.width - paddleWidth, paddleXRef.current + 20); }, [canvasSize.width, paddleWidth]);
@@ -547,7 +557,7 @@ const Breakout = () => {
<div className={`flex ${isTwoPlayer ? 'gap-4' : ''}`}> <div className={`flex ${isTwoPlayer ? 'gap-4' : ''}`}>
{/* P1 Board */} {/* P1 Board */}
<div className="border-2 border-primary box-glow bg-background/80"> <div className="border-2 border-primary box-glow bg-background/80">
<canvas ref={canvasRef} width={canvasSize.width} height={canvasSize.height} onTouchMove={handleTouchMove} className="block" /> <canvas ref={canvasRef} width={canvasSize.width} height={canvasSize.height} onTouchMove={handleTouchMove} onMouseMove={handleMouseMove} className="block cursor-none" />
</div> </div>
{/* P2 Board */} {/* P2 Board */}
-10
View File
@@ -8,7 +8,6 @@ const games = [
id: 'tetris', id: 'tetris',
name: 'Tetris', name: 'Tetris',
description: 'Classic block-stacking puzzle game', description: 'Classic block-stacking puzzle game',
hasMultiplayer: true,
ascii: `┌────────┐ ascii: `┌────────┐
│ ▓▓ │ │ ▓▓ │
│ ▓▓ ██ │ │ ▓▓ ██ │
@@ -20,7 +19,6 @@ const games = [
id: 'pacman', id: 'pacman',
name: 'Pac-Man', name: 'Pac-Man',
description: 'Navigate the maze, eat dots, avoid ghosts', description: 'Navigate the maze, eat dots, avoid ghosts',
hasMultiplayer: true,
ascii: `┌────────┐ ascii: `┌────────┐
│· · ᗣ · │ │· · ᗣ · │
│ ┌─┐ ┌─┐│ │ ┌─┐ ┌─┐│
@@ -32,7 +30,6 @@ const games = [
id: 'snake', id: 'snake',
name: 'Snake', name: 'Snake',
description: 'Eat food, grow longer, dont hit yourself', description: 'Eat food, grow longer, dont hit yourself',
hasMultiplayer: true,
ascii: `┌────────┐ ascii: `┌────────┐
│ │ │ │
│ ●■■■ │ │ ●■■■ │
@@ -44,7 +41,6 @@ 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,
ascii: `┌────────┐ ascii: `┌────────┐
│████████│ │████████│
│▓▓▓▓▓▓▓▓│ │▓▓▓▓▓▓▓▓│
@@ -110,12 +106,6 @@ const Games = () => {
</p> </p>
</div> </div>
{/* Multiplayer Badge - using primary colors instead of purple */}
{game.hasMultiplayer && (
<span className="absolute top-3 right-3 font-pixel text-[10px] text-primary border border-primary/60 bg-primary/10 px-2 py-1 rounded-sm">
2P
</span>
)}
</div> </div>
</Link> </Link>
</motion.div> </motion.div>
+19 -16
View File
@@ -339,31 +339,26 @@ const Tetris = () => {
const movePlayer = useCallback((playerNum: 1 | 2, action: 'left' | 'right' | 'down' | 'rotate' | 'drop') => { const movePlayer = useCallback((playerNum: 1 | 2, action: 'left' | 'right' | 'down' | 'rotate' | 'drop') => {
if (gameOver || isPaused || !gameStarted || gameMode !== '2p') return; if (gameOver || isPaused || !gameStarted || gameMode !== '2p') return;
const setPlayer = playerNum === 1 ? setPlayer1 : setPlayer2; const setPlayer = playerNum === 1 ? setPlayer1 : setPlayer2;
const player = playerNum === 1 ? player1 : player2;
const isP2 = playerNum === 2; const isP2 = playerNum === 2;
if (player.gameOver) return;
setPlayer(prev => { setPlayer(prev => {
if (prev.gameOver) return prev;
let newPiece = { ...prev.piece }; let newPiece = { ...prev.piece };
switch (action) { switch (action) {
case 'left': case 'left':
newPiece.x--; newPiece.x--;
if (!isValidMove(newPiece, prev.board)) return prev; if (!isValidMove(newPiece, prev.board)) return prev;
playSound('hover');
return { ...prev, piece: newPiece }; return { ...prev, piece: newPiece };
case 'right': case 'right':
newPiece.x++; newPiece.x++;
if (!isValidMove(newPiece, prev.board)) return prev; if (!isValidMove(newPiece, prev.board)) return prev;
playSound('hover');
return { ...prev, piece: newPiece }; return { ...prev, piece: newPiece };
case 'rotate': case 'rotate':
newPiece.shape = rotate(prev.piece.shape); newPiece.shape = rotate(prev.piece.shape);
if (!isValidMove(newPiece, prev.board)) return prev; if (!isValidMove(newPiece, prev.board)) return prev;
playSound('hover');
return { ...prev, piece: newPiece }; return { ...prev, piece: newPiece };
case 'drop': case 'drop':
while (isValidMove({ ...newPiece, y: newPiece.y + 1 }, prev.board)) newPiece.y++; while (isValidMove({ ...newPiece, y: newPiece.y + 1 }, prev.board)) newPiece.y++;
playSound('click');
return { ...prev, piece: newPiece }; return { ...prev, piece: newPiece };
case 'down': case 'down':
newPiece.y++; newPiece.y++;
@@ -373,12 +368,9 @@ const Tetris = () => {
const { board: clearedBoard, cleared } = clearLines(mergedBoard); const { board: clearedBoard, cleared } = clearLines(mergedBoard);
const newScore = prev.score + (cleared > 0 ? cleared * 100 * cleared : 0); const newScore = prev.score + (cleared > 0 ? cleared * 100 * cleared : 0);
const newLines = prev.lines + cleared; const newLines = prev.lines + cleared;
if (cleared > 0) playSound('success');
else playSound('click');
const newTetromino = prev.nextPiece; const newTetromino = prev.nextPiece;
const nextNext = randomTetromino(isP2, prev.nextPiece.shape); const nextNext = randomTetromino(isP2, prev.nextPiece.shape);
if (!isValidMove(newTetromino, clearedBoard)) { if (!isValidMove(newTetromino, clearedBoard)) {
playSound('error');
return { ...prev, board: clearedBoard, score: newScore, lines: newLines, gameOver: true }; return { ...prev, board: clearedBoard, score: newScore, lines: newLines, gameOver: true };
} }
return { ...prev, board: clearedBoard, piece: newTetromino, nextPiece: nextNext, score: newScore, lines: newLines }; return { ...prev, board: clearedBoard, piece: newTetromino, nextPiece: nextNext, score: newScore, lines: newLines };
@@ -386,7 +378,7 @@ const Tetris = () => {
} }
return prev; return prev;
}); });
}, [gameOver, isPaused, gameStarted, gameMode, player1, player2, isValidMove, mergePiece, clearLines, playSound]); }, [gameOver, isPaused, gameStarted, gameMode, isValidMove, mergePiece, clearLines]);
useEffect(() => { useEffect(() => {
if (gameMode !== '2p' || !gameStarted) return; if (gameMode !== '2p' || !gameStarted) return;
@@ -599,7 +591,16 @@ const Tetris = () => {
)} )}
</> </>
) : ( ) : (
<div className="flex gap-4 items-start"> <div className="flex gap-6 items-start">
{/* P1 Next Piece - Left side */}
<div className="flex flex-col items-center gap-2 min-w-[60px]">
<p className="font-pixel text-[10px] text-primary/70">P1 Next</p>
<div className="border border-primary/30 p-2 bg-background/40">
{renderNextPiece(player1.nextPiece)}
</div>
</div>
{/* P1 Board */}
<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-primary">P1 <span className="text-xs text-foreground/60">(WASD + Q)</span></p> <p className="font-minecraft text-lg text-primary">P1 <span className="text-xs text-foreground/60">(WASD + Q)</span></p>
<div className="border-2 border-primary box-glow p-1 bg-background/80">{renderBoard2P(player1, 'border-primary/20')}</div> <div className="border-2 border-primary box-glow p-1 bg-background/80">{renderBoard2P(player1, 'border-primary/20')}</div>
@@ -607,11 +608,9 @@ const Tetris = () => {
<div><p className="font-pixel text-[10px] text-foreground/60">Score</p><p className="font-minecraft text-lg text-primary">{player1.score}</p></div> <div><p className="font-pixel text-[10px] text-foreground/60">Score</p><p className="font-minecraft text-lg text-primary">{player1.score}</p></div>
<div><p className="font-pixel text-[10px] text-foreground/60">Lines</p><p className="font-minecraft text-lg text-primary">{player1.lines}</p></div> <div><p className="font-pixel text-[10px] text-foreground/60">Lines</p><p className="font-minecraft text-lg text-primary">{player1.lines}</p></div>
</div> </div>
<div className="border border-primary/30 p-2 bg-background/40">
<p className="font-pixel text-[8px] text-foreground/40">Next</p>
{renderNextPiece(player1.nextPiece)}
</div>
</div> </div>
{/* P2 Board */}
<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-secondary">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-secondary p-1 bg-background/80" style={{ boxShadow: '0 0 15px hsl(var(--secondary) / 0.5)' }}>{renderBoard2P(player2, 'border-secondary/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>
@@ -619,8 +618,12 @@ const Tetris = () => {
<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">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><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>
{/* P2 Next Piece - Right side */}
<div className="flex flex-col items-center gap-2 min-w-[60px]">
<p className="font-pixel text-[10px] text-secondary/70">P2 Next</p>
<div className="border border-secondary/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)} {renderNextPiece(player2.nextPiece)}
</div> </div>
</div> </div>