Files
battle-royal-tetris/public/js/network.js
T
jozamudi 4a49c76cdc Add hold piece feature
- Added holdPiece and canHold state to TetrisGame class
- Implemented hold() method to swap current piece with held piece
- Added player-hold socket event on server
- Added HOLD preview canvas showing held piece (grayed when unavailable)
- Added C key keyboard shortcut and touch button for hold
- Fixed canHold reset on piece spawn for proper swap functionality

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-20 08:50:52 -07:00

150 lines
3.4 KiB
JavaScript

// Network module - Socket.io client handling
class NetworkManager {
constructor() {
this.socket = null;
this.currentPlayerId = null;
this.players = {};
this.gameState = {};
this.listeners = {
onPlayerJoined: null,
onPlayerLeft: null,
onGameStarted: null,
onStateUpdate: null,
onGameOver: null
};
}
connect() {
this.socket = io();
this.socket.on('connect', () => {
console.log('Connected to server');
this.currentPlayerId = this.socket.id;
});
this.socket.on('disconnect', () => {
console.log('Disconnected from server');
});
this.socket.on('player-joined', ({ player, players }) => {
this.updatePlayers(players);
if (this.listeners.onPlayerJoined) {
this.listeners.onPlayerJoined(player);
}
});
this.socket.on('player-left', ({ playerId, players }) => {
delete this.players[playerId];
this.updatePlayers(players);
if (this.listeners.onPlayerLeft) {
this.listeners.onPlayerLeft(playerId);
}
});
this.socket.on('game-started', ({ players, states }) => {
this.updatePlayers(players);
this.updateStates(states);
if (this.listeners.onGameStarted) {
this.listeners.onGameStarted(players, states);
}
});
this.socket.on('state-update', (states) => {
this.updateStates(states);
if (this.listeners.onStateUpdate) {
this.listeners.onStateUpdate(states);
}
});
this.socket.on('game-over', (data) => {
if (this.listeners.onGameOver) {
this.listeners.onGameOver(data);
}
});
}
joinLobby(playerName) {
if (!this.socket) return;
this.socket.emit('join-lobby', { playerName });
}
leaveLobby() {
if (!this.socket) return;
}
ready() {
if (!this.socket) return;
this.socket.emit('ready');
}
sendMove(direction) {
if (!this.socket || !this.currentPlayerId) return;
this.socket.emit('player-move', { playerId: this.currentPlayerId, direction });
}
sendRotate() {
if (!this.socket || !this.currentPlayerId) return;
this.socket.emit('player-rotate', { playerId: this.currentPlayerId });
}
sendDrop() {
if (!this.socket || !this.currentPlayerId) return;
this.socket.emit('player-drop', { playerId: this.currentPlayerId, hard: false });
}
sendHardDrop() {
if (!this.socket || !this.currentPlayerId) return;
this.socket.emit('player-drop', { playerId: this.currentPlayerId, hard: true });
}
sendHold() {
if (!this.socket || !this.currentPlayerId) return;
this.socket.emit('player-hold', { playerId: this.currentPlayerId });
}
updatePlayers(players) {
this.players = {};
players.forEach(p => {
this.players[p.id] = p;
});
}
updateStates(states) {
states.forEach(s => {
this.gameState[s.playerId] = s;
});
}
getPlayer(playerId) {
return this.players[playerId];
}
getGameState(playerId) {
return this.gameState[playerId];
}
getAllPlayers() {
return this.players;
}
getAllGameStates() {
return this.gameState;
}
setListener(event, callback) {
const listenerMap = {
'player-joined': 'onPlayerJoined',
'player-left': 'onPlayerLeft',
'game-started': 'onGameStarted',
'state-update': 'onStateUpdate',
'game-over': 'onGameOver'
};
if (callback) {
this.listeners[listenerMap[event]] = callback;
}
}
}
const network = new NetworkManager();