diff --git a/server/index.js b/server/index.js index 96c5b81..e04a721 100644 --- a/server/index.js +++ b/server/index.js @@ -405,6 +405,7 @@ function spawnPiece(player) { function lockPiece(player) { if (!player.currentPiece) return; + // Lock piece into board for (let row = 0; row < player.currentPiece.shape.length; row++) { for (let col = 0; col < player.currentPiece.shape[row].length; col++) { if (player.currentPiece.shape[row][col]) { @@ -419,10 +420,29 @@ function lockPiece(player) { const rowsCleared = clearRows(player); + // Check if board overflowed (top 2 rows have blocks) - this means player is eliminated + let boardOverflowed = false; + for (let row = 0; row < 2; row++) { + for (let col = 0; col < BOARD_WIDTH; col++) { + if (player.board[row][col] !== 0) { + boardOverflowed = true; + break; + } + } + if (boardOverflowed) break; + } + if (boardOverflowed) { + player.eliminated = true; + console.log(`[ELIMINATION] ${player.name} eliminated - board overflowed`); + broadcastState(); + checkGameOver(); + return; + } + const spawnResult = spawnPiece(player); if (!spawnResult) { player.eliminated = true; - console.log(`[ELIMINATION] ${player.name} eliminated - piece could not spawn (board full at y=${player.currentPiece?.y})`); + console.log(`[ELIMINATION] ${player.name} eliminated - piece could not spawn`); } // Reset canHold for the new piece @@ -456,20 +476,23 @@ function clearRows(player) { function sendGarbage(sender, rowsCleared) { // Number of garbage rows equals number of lines cleared - const opponents = Array.from(lobby.players.values()).filter(p => p.id !== sender.id && !p.eliminated); - if (opponents.length === 0) { - console.log(`[GARBAGE] ${sender.name} cleared ${rowsCleared} row(s) but no opponents to send garbage to`); - return; - } - const garbageLog = []; for (let i = 0; i < rowsCleared; i++) { + // Re-compute opponents each iteration to exclude players eliminated by previous garbage + const opponents = Array.from(lobby.players.values()).filter(p => p.id !== sender.id && !p.eliminated); + if (opponents.length === 0) { + break; + } const target = opponents[Math.floor(Math.random() * opponents.length)]; garbageLog.push(target.name); addGarbageToPlayer(target, sender.name); } - console.log(`[GARBAGE] ${sender.name} cleared ${rowsCleared} row(s) -> sent garbage to: ${garbageLog.join(', ')}`); + if (garbageLog.length > 0) { + console.log(`[GARBAGE] ${sender.name} cleared ${rowsCleared} row(s) -> sent garbage to: ${garbageLog.join(', ')}`); + } else { + console.log(`[GARBAGE] ${sender.name} cleared ${rowsCleared} row(s) but no opponents to send garbage to`); + } } function addGarbageToPlayer(player, senderName) { @@ -484,17 +507,9 @@ function addGarbageToPlayer(player, senderName) { player.garbageReceived.push({ rows: 1, sender: senderName }); // Push current piece up by 1 row if it exists (y decreases when moving up) + // Don't eliminate here - let the piece lock and check board overflow then if (player.currentPiece) { - const oldY = player.currentPiece.y; player.currentPiece.y--; - // Eliminate if the piece is pushed above the board or collides - if (player.currentPiece.y < 0) { - player.eliminated = true; - console.log(`[ELIMINATION] ${player.name} eliminated by ${senderName} - piece pushed above board (y=${oldY} -> ${player.currentPiece.y})`); - } else if (!isValidPosition(player.currentPiece, player.currentPiece.x, player.currentPiece.y, player.board)) { - player.eliminated = true; - console.log(`[ELIMINATION] ${player.name} eliminated by ${senderName} - piece collision after garbage (piece at x=${player.currentPiece.x}, y=${player.currentPiece.y})`); - } } } @@ -514,6 +529,7 @@ function startGame() { // Initialize shared piece queue lobby.pieceQueue = createPieceQueue(14); // 98 pieces (~14 bags) + // Reset all player variables for the new game for (const player of lobby.players.values()) { player.board = createEmptyBoard(); player.score = 0; @@ -521,9 +537,12 @@ function startGame() { player.level = 1; player.eliminated = false; player.dropInterval = 1000; + player.dropCounter = 0; player.holdPiece = null; player.canHold = true; player.garbageReceived = []; + player.currentPiece = null; + player.nextPiece = null; lobby.playerSequenceIndex.set(player.id, 0); } @@ -593,13 +612,19 @@ function checkGameOver() { console.log(`[GAMEOVER CHECK] Active players: ${activePlayers.length} (${activePlayers.map(p => p.name).join(', ')})`); if (activePlayers.length <= 1) { console.log(`[GAME OVER] ${activePlayers.length === 1 ? `Winner: ${activePlayers[0].name}` : 'No winner (all eliminated)'}`); - io.to(LOBBY_ROOM).emit('game-over', { states: getStates() }); + + // Stop the game interval but keep game state visible for viewing if (lobby.gameInterval) { clearInterval(lobby.gameInterval); lobby.gameInterval = null; } lobby.gameStarted = false; + // Set all players to not ready for next game (but keep their final state) + for (const player of lobby.players.values()) { + player.ready = false; + } + // Move spectators to players for next round for (const [id, spectator] of lobby.spectators.entries()) { const player = { @@ -611,12 +636,16 @@ function checkGameOver() { board: createEmptyBoard(), currentPiece: null, nextPiece: null, + holdPiece: null, + canHold: true, eliminated: false, ready: false, dropCounter: 0, - dropInterval: 1000 + dropInterval: 1000, + garbageReceived: [] }; lobby.players.set(id, player); + lobby.playerSequenceIndex.set(id, 0); spectator.socket = io.sockets.sockets.get(id); if (spectator.socket) { spectator.socket.data.playerName = spectator.name; @@ -624,13 +653,16 @@ function checkGameOver() { } lobby.spectators.clear(); + // Broadcast game over with final states + io.to(LOBBY_ROOM).emit('game-over', { states: getStates() }); + // Broadcast updated player list io.to(LOBBY_ROOM).emit('player-joined', { player: null, players: getPlayersList() }); - console.log(`Game over. Moved ${activePlayers.length === 1 ? 0 : lobby.players.size} players to next round`); + console.log(`Game over. Reset ${lobby.players.size} players for next round`); } }