Signed-off-by: ale <ale@manalejandro.com>
Este commit está contenido en:
ale
2025-12-05 23:40:41 +01:00
padre 8234530ed7
commit 98b4bf92c6
Se han modificado 7 ficheros con 49 adiciones y 35 borrados

Ver fichero

@@ -57,12 +57,14 @@ function startGame(roomId: string): GameState {
} }
export async function GET(req: NextRequest) { export async function GET(req: NextRequest) {
// @ts-ignore // @ts-expect-error - NextRequest extended with socket server
const res = req.res || req.nextUrl; const res = req.res || req.nextUrl;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
if (!(res as any).socket?.server?.io) { if (!(res as any).socket?.server?.io) {
console.log('Initializing Socket.IO server...'); console.log('Initializing Socket.IO server...');
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const httpServer: HTTPServer = (res as any).socket.server; const httpServer: HTTPServer = (res as any).socket.server;
const io = new SocketIOServer(httpServer, { const io = new SocketIOServer(httpServer, {
path: '/api/socket', path: '/api/socket',
@@ -167,7 +169,7 @@ export async function GET(req: NextRequest) {
gameRooms.set(roomId, newGameState); gameRooms.set(roomId, newGameState);
io.to(roomId).emit('game-state-updated', newGameState); io.to(roomId).emit('game-state-updated', newGameState);
} catch (error) { } catch {
socket.emit('invalid-move', 'Invalid move'); socket.emit('invalid-move', 'Invalid move');
} }
}); });
@@ -220,6 +222,7 @@ export async function GET(req: NextRequest) {
}); });
}); });
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(res as any).socket.server.io = io; (res as any).socket.server.io = io;
} }

Ver fichero

@@ -1,6 +1,6 @@
'use client'; 'use client';
import React, { useRef, useEffect, useState } from 'react'; import React, { useRef, useEffect, useState, useMemo } from 'react';
import { PlacedTile, Position } from '@/lib/types'; import { PlacedTile, Position } from '@/lib/types';
interface GameBoardProps { interface GameBoardProps {
@@ -17,7 +17,7 @@ export function GameBoard({ placedTiles, width = 1200, height = 700, className =
const [isDragging, setIsDragging] = useState(false); const [isDragging, setIsDragging] = useState(false);
const [dragStart, setDragStart] = useState<Position>({ x: 0, y: 0 }); const [dragStart, setDragStart] = useState<Position>({ x: 0, y: 0 });
const [canvasSize, setCanvasSize] = useState({ width, height }); const [canvasSize, setCanvasSize] = useState({ width, height });
const [zoom, setZoom] = useState(1); const initialZoom = 1;
// Handle responsive canvas sizing // Handle responsive canvas sizing
useEffect(() => { useEffect(() => {
@@ -37,8 +37,8 @@ export function GameBoard({ placedTiles, width = 1200, height = 700, className =
}, [width, height]); }, [width, height]);
// Auto-zoom and auto-center to fit all tiles // Auto-zoom and auto-center to fit all tiles
useEffect(() => { const autoFitValues = useMemo(() => {
if (placedTiles.length === 0) return; if (placedTiles.length === 0) return null;
// Calculate bounding box of all tiles // Calculate bounding box of all tiles
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity; let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
@@ -65,7 +65,7 @@ export function GameBoard({ placedTiles, width = 1200, height = 700, className =
const contentHeight = maxY - minY; const contentHeight = maxY - minY;
// Calculate zoom to fit all tiles in viewport // Calculate zoom to fit all tiles in viewport
const isMobile = window.innerWidth < 640; const isMobile = typeof window !== 'undefined' && window.innerWidth < 640;
const zoomX = canvasSize.width / contentWidth; const zoomX = canvasSize.width / contentWidth;
const zoomY = canvasSize.height / contentHeight; const zoomY = canvasSize.height / contentHeight;
@@ -83,10 +83,13 @@ export function GameBoard({ placedTiles, width = 1200, height = 700, className =
const newOffsetX = canvasSize.width / 2 - centerX * newZoom; const newOffsetX = canvasSize.width / 2 - centerX * newZoom;
const newOffsetY = canvasSize.height / 2 - centerY * newZoom; const newOffsetY = canvasSize.height / 2 - centerY * newZoom;
setZoom(newZoom); return { zoom: newZoom, offset: { x: newOffsetX, y: newOffsetY } };
setOffset({ x: newOffsetX, y: newOffsetY });
}, [placedTiles, canvasSize.width, canvasSize.height]); }, [placedTiles, canvasSize.width, canvasSize.height]);
// Derive effective zoom and offset values
const effectiveZoom = autoFitValues?.zoom ?? initialZoom;
const effectiveOffset = autoFitValues?.offset ?? offset;
useEffect(() => { useEffect(() => {
const canvas = canvasRef.current; const canvas = canvasRef.current;
if (!canvas) return; if (!canvas) return;
@@ -118,27 +121,27 @@ export function GameBoard({ placedTiles, width = 1200, height = 700, className =
} }
// Draw placed tiles // Draw placed tiles
placedTiles.forEach((placedTile, index) => { placedTiles.forEach((placedTile) => {
const { tile, position, orientation } = placedTile; const { tile, position, orientation } = placedTile;
const tileWidth = orientation === 'horizontal' ? 60 : 30; const tileWidth = orientation === 'horizontal' ? 60 : 30;
const tileHeight = orientation === 'horizontal' ? 30 : 60; const tileHeight = orientation === 'horizontal' ? 30 : 60;
const x = position.x * zoom + offset.x; const x = position.x * effectiveZoom + effectiveOffset.x;
const y = position.y * zoom + offset.y; const y = position.y * effectiveZoom + effectiveOffset.y;
const scaledWidth = tileWidth * zoom; const scaledWidth = tileWidth * effectiveZoom;
const scaledHeight = tileHeight * zoom; const scaledHeight = tileHeight * effectiveZoom;
// Draw tile background with shadow // Draw tile background with shadow
ctx.shadowColor = 'rgba(0, 0, 0, 0.2)'; ctx.shadowColor = 'rgba(0, 0, 0, 0.2)';
ctx.shadowBlur = 5 * zoom; ctx.shadowBlur = 5 * effectiveZoom;
ctx.shadowOffsetX = 2 * zoom; ctx.shadowOffsetX = 2 * effectiveZoom;
ctx.shadowOffsetY = 2 * zoom; ctx.shadowOffsetY = 2 * effectiveZoom;
ctx.fillStyle = '#ffffff'; ctx.fillStyle = '#ffffff';
ctx.strokeStyle = '#1f2937'; ctx.strokeStyle = '#1f2937';
ctx.lineWidth = 2 * zoom; ctx.lineWidth = 2 * effectiveZoom;
const radius = 4 * zoom; const radius = 4 * effectiveZoom;
ctx.beginPath(); ctx.beginPath();
ctx.moveTo(x + radius, y); ctx.moveTo(x + radius, y);
ctx.lineTo(x + scaledWidth - radius, y); ctx.lineTo(x + scaledWidth - radius, y);
@@ -158,7 +161,7 @@ export function GameBoard({ placedTiles, width = 1200, height = 700, className =
// Draw center divider // Draw center divider
ctx.strokeStyle = '#6b7280'; ctx.strokeStyle = '#6b7280';
ctx.lineWidth = 1 * zoom; ctx.lineWidth = 1 * effectiveZoom;
ctx.beginPath(); ctx.beginPath();
if (orientation === 'horizontal') { if (orientation === 'horizontal') {
ctx.moveTo(x + scaledWidth / 2, y); ctx.moveTo(x + scaledWidth / 2, y);
@@ -170,7 +173,7 @@ export function GameBoard({ placedTiles, width = 1200, height = 700, className =
ctx.stroke(); ctx.stroke();
// Draw dots // Draw dots
const dotRadius = Math.max(2, 2.5 * zoom); const dotRadius = Math.max(2, 2.5 * effectiveZoom);
ctx.fillStyle = '#1f2937'; ctx.fillStyle = '#1f2937';
const drawDots = (value: number, dotX: number, dotY: number, size: number) => { const drawDots = (value: number, dotX: number, dotY: number, size: number) => {
@@ -186,7 +189,7 @@ export function GameBoard({ placedTiles, width = 1200, height = 700, className =
const leftX = x + scaledWidth / 4; const leftX = x + scaledWidth / 4;
const rightX = x + (scaledWidth * 3) / 4; const rightX = x + (scaledWidth * 3) / 4;
const centerY = y + scaledHeight / 2; const centerY = y + scaledHeight / 2;
const dotAreaSize = (scaledWidth / 2 - 6 * zoom); const dotAreaSize = (scaledWidth / 2 - 6 * effectiveZoom);
drawDots(tile.left, leftX, centerY, dotAreaSize); drawDots(tile.left, leftX, centerY, dotAreaSize);
drawDots(tile.right, rightX, centerY, dotAreaSize); drawDots(tile.right, rightX, centerY, dotAreaSize);
@@ -194,14 +197,14 @@ export function GameBoard({ placedTiles, width = 1200, height = 700, className =
const topY = y + scaledHeight / 4; const topY = y + scaledHeight / 4;
const bottomY = y + (scaledHeight * 3) / 4; const bottomY = y + (scaledHeight * 3) / 4;
const centerX = x + scaledWidth / 2; const centerX = x + scaledWidth / 2;
const dotAreaSize = (scaledHeight / 2 - 6 * zoom); const dotAreaSize = (scaledHeight / 2 - 6 * effectiveZoom);
drawDots(tile.left, centerX, topY, dotAreaSize); drawDots(tile.left, centerX, topY, dotAreaSize);
drawDots(tile.right, centerX, bottomY, dotAreaSize); drawDots(tile.right, centerX, bottomY, dotAreaSize);
} }
}); });
}, [placedTiles, offset, zoom, canvasSize.width, canvasSize.height]); }, [placedTiles, effectiveOffset, effectiveZoom, canvasSize.width, canvasSize.height]);
const handleMouseDown = (e: React.MouseEvent<HTMLCanvasElement>) => { const handleMouseDown = (e: React.MouseEvent<HTMLCanvasElement>) => {
setIsDragging(true); setIsDragging(true);

Ver fichero

@@ -7,10 +7,10 @@ interface LobbyProps {
onCreateRoom: (playerName: string) => void; onCreateRoom: (playerName: string) => void;
onJoinRoom: (roomId: string, playerName: string) => void; onJoinRoom: (roomId: string, playerName: string) => void;
onStartAI: (playerName: string) => void; onStartAI: (playerName: string) => void;
roomId: string | null; roomId?: string | null;
} }
export function Lobby({ onCreateRoom, onJoinRoom, onStartAI, roomId }: LobbyProps) { export function Lobby({ onCreateRoom, onJoinRoom, onStartAI }: LobbyProps) {
const [playerName, setPlayerName] = useState(''); const [playerName, setPlayerName] = useState('');
const [joinRoomId, setJoinRoomId] = useState(''); const [joinRoomId, setJoinRoomId] = useState('');
const [mode, setMode] = useState<'menu' | 'create' | 'join' | 'ai'>('menu'); const [mode, setMode] = useState<'menu' | 'create' | 'join' | 'ai'>('menu');

Ver fichero

@@ -151,7 +151,7 @@ export function calculateTilePosition(
const lastTile = side === 'right' ? board[board.length - 1] : board[0]; const lastTile = side === 'right' ? board[board.length - 1] : board[0];
let position: Position; let position: Position;
let orientation: 'horizontal' | 'vertical' = 'horizontal'; let orientation: 'horizontal' | 'vertical' = 'horizontal';
let rotation = 0; const rotation = 0;
if (side === 'right') { if (side === 'right') {
const offset = lastTile.orientation === 'horizontal' ? tileWidth : tileHeight; const offset = lastTile.orientation === 'horizontal' ? tileWidth : tileHeight;

Ver fichero

@@ -163,7 +163,7 @@ io.on('connection', (socket) => {
gameRooms.set(roomId, newGameState); gameRooms.set(roomId, newGameState);
io.to(roomId).emit('game-state-updated', newGameState); io.to(roomId).emit('game-state-updated', newGameState);
} catch (error) { } catch {
socket.emit('invalid-move', 'Invalid move'); socket.emit('invalid-move', 'Invalid move');
} }
}); });

Ver fichero

@@ -11,9 +11,9 @@
}, },
"dependencies": { "dependencies": {
"framer-motion": "^12.23.24", "framer-motion": "^12.23.24",
"next": "16.0.1", "next": "15.4.8",
"react": "19.2.0", "react": "19.1.2",
"react-dom": "19.2.0", "react-dom": "19.1.2",
"socket.io": "^4.8.1", "socket.io": "^4.8.1",
"socket.io-client": "^4.8.1", "socket.io-client": "^4.8.1",
"zustand": "^5.0.8" "zustand": "^5.0.8"

Ver fichero

@@ -1,7 +1,11 @@
{ {
"compilerOptions": { "compilerOptions": {
"target": "ES2017", "target": "ES2017",
"lib": ["dom", "dom.iterable", "esnext"], "lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true, "allowJs": true,
"skipLibCheck": true, "skipLibCheck": true,
"strict": true, "strict": true,
@@ -11,7 +15,7 @@
"moduleResolution": "bundler", "moduleResolution": "bundler",
"resolveJsonModule": true, "resolveJsonModule": true,
"isolatedModules": true, "isolatedModules": true,
"jsx": "react-jsx", "jsx": "preserve",
"incremental": true, "incremental": true,
"plugins": [ "plugins": [
{ {
@@ -19,7 +23,9 @@
} }
], ],
"paths": { "paths": {
"@/*": ["./*"] "@/*": [
"./*"
]
} }
}, },
"include": [ "include": [
@@ -30,5 +36,7 @@
".next/dev/types/**/*.ts", ".next/dev/types/**/*.ts",
"**/*.mts" "**/*.mts"
], ],
"exclude": ["node_modules"] "exclude": [
"node_modules"
]
} }