Initial commit: Tetris Battle Royale multiplayer game
Features: - 2-8 player multiplayer via Socket.io WebSocket - Real-time board synchronization - all players see all boards - Battle royale mechanic: clearing rows sends garbage to opponents - Classic Tetris gameplay with all 7 tetrominoes - Retro visual styling with CRT scanlines and pixel font - Automatic level progression and speed increase - Player elimination and winner announcement Files: - server/index.js: Node.js + Socket.io game server - public/js/: Frontend game logic, rendering, network, and UI - public/css/style.css: Retro Tetris styling - README.md: Setup and usage instructions - PLAN.md: Implementation plan with all phases completed
This commit is contained in:
+138
@@ -0,0 +1,138 @@
|
||||
// UI Module - Handle screens and user interactions
|
||||
|
||||
class UIManager {
|
||||
constructor() {
|
||||
this.screens = {
|
||||
room: document.getElementById('room-screen'),
|
||||
lobby: document.getElementById('lobby-screen'),
|
||||
game: document.getElementById('game-screen'),
|
||||
gameover: document.getElementById('gameover-screen')
|
||||
};
|
||||
|
||||
this.inputs = {
|
||||
roomName: document.getElementById('room-name'),
|
||||
playerName: document.getElementById('player-name')
|
||||
};
|
||||
|
||||
this.buttons = {
|
||||
join: document.getElementById('join-btn'),
|
||||
ready: document.getElementById('ready-btn'),
|
||||
leave: document.getElementById('leave-btn'),
|
||||
backToLobby: document.getElementById('back-to-lobby')
|
||||
};
|
||||
|
||||
this.displays = {
|
||||
lobbyRoomName: document.getElementById('lobby-room-name'),
|
||||
gameRoomName: document.getElementById('game-room-name'),
|
||||
playerList: document.getElementById('player-list'),
|
||||
battleGrid: document.getElementById('battle-grid'),
|
||||
gameStatus: document.getElementById('game-status'),
|
||||
winnerDisplay: document.getElementById('winner-display'),
|
||||
finalScores: document.getElementById('final-scores')
|
||||
};
|
||||
|
||||
this.bindEvents();
|
||||
}
|
||||
|
||||
bindEvents() {
|
||||
this.buttons.join.addEventListener('click', () => this.handleJoin());
|
||||
this.buttons.ready.addEventListener('click', () => this.handleReady());
|
||||
this.buttons.leave.addEventListener('click', () => this.handleLeave());
|
||||
this.buttons.backToLobby.addEventListener('click', () => this.handleBackToLobby());
|
||||
|
||||
// Allow Enter key to submit forms
|
||||
this.inputs.roomName.addEventListener('keypress', (e) => {
|
||||
if (e.key === 'Enter') this.handleJoin();
|
||||
});
|
||||
this.inputs.playerName.addEventListener('keypress', (e) => {
|
||||
if (e.key === 'Enter') this.handleJoin();
|
||||
});
|
||||
}
|
||||
|
||||
showScreen(screenName) {
|
||||
Object.values(this.screens).forEach(screen => screen.classList.remove('active'));
|
||||
this.screens[screenName].classList.add('active');
|
||||
}
|
||||
|
||||
handleJoin() {
|
||||
const roomName = this.inputs.roomName.value.trim();
|
||||
const playerName = this.inputs.playerName.value.trim();
|
||||
|
||||
if (!roomName || !playerName) {
|
||||
this.showMessage('Please enter room name and your name');
|
||||
return;
|
||||
}
|
||||
|
||||
// Emit join event
|
||||
network.setListener('player-joined', () => {
|
||||
this.showScreen('lobby');
|
||||
this.displays.lobbyRoomName.textContent = roomName;
|
||||
});
|
||||
|
||||
network.joinRoom(roomName, playerName);
|
||||
}
|
||||
|
||||
handleReady() {
|
||||
network.ready();
|
||||
this.buttons.ready.textContent = 'READY!';
|
||||
this.buttons.ready.disabled = true;
|
||||
}
|
||||
|
||||
handleLeave() {
|
||||
network.leaveRoom();
|
||||
this.showScreen('room');
|
||||
}
|
||||
|
||||
handleBackToLobby() {
|
||||
this.showScreen('room');
|
||||
}
|
||||
|
||||
updatePlayerList(players) {
|
||||
this.displays.playerList.innerHTML = '';
|
||||
Object.values(players).forEach(player => {
|
||||
const item = document.createElement('div');
|
||||
item.className = 'player-item';
|
||||
const statusClass = player.ready ? 'ready' : '';
|
||||
item.innerHTML = `
|
||||
<span class="name">${this.escapeHtml(player.name)}</span>
|
||||
<span class="status ${statusClass}">${player.ready ? 'READY' : 'WAITING'}</span>
|
||||
`;
|
||||
this.displays.playerList.appendChild(item);
|
||||
});
|
||||
}
|
||||
|
||||
showMessage(message) {
|
||||
this.displays.gameStatus.textContent = message;
|
||||
setTimeout(() => {
|
||||
this.displays.gameStatus.textContent = '';
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
escapeHtml(text) {
|
||||
const div = document.createElement('div');
|
||||
div.textContent = text;
|
||||
return div.innerHTML;
|
||||
}
|
||||
|
||||
showGameOver(winner, scores) {
|
||||
this.displays.winnerDisplay.textContent = winner
|
||||
? `Winner: ${winner}!`
|
||||
: 'Game Over!';
|
||||
this.displays.winnerDisplay.style.color = winner ? '#0f0' : '#fff';
|
||||
|
||||
this.displays.finalScores.innerHTML = '';
|
||||
Object.entries(scores).forEach(([name, score], index) => {
|
||||
const item = document.createElement('div');
|
||||
item.className = 'score-item' + (index === 0 ? ' winner' : '');
|
||||
item.innerHTML = `
|
||||
<span>${this.escapeHtml(name)}</span>
|
||||
<span>${score}</span>
|
||||
`;
|
||||
this.displays.finalScores.appendChild(item);
|
||||
});
|
||||
|
||||
this.showScreen('gameover');
|
||||
}
|
||||
}
|
||||
|
||||
window.ui = new UIManager();
|
||||
Reference in New Issue
Block a user