mirror of
https://github.com/DeNNiiInc/Connect-5.git
synced 2026-04-17 22:46:00 +00:00
Add surrender and rematch functionality - Part 2 (Complete)
Client-side (multiplayer.js): - Add socket listeners for rematch events - Update startMultiplayerGame to show surrender button and track opponent ID - Update handleGameEnded to show game-over modal with stats - Add handleRematchRequest method - Add global helper functions for rematch acceptance/decline Server-side (gameManager.js): - Add rematches Map to track rematch requests - Add handleSurrender method - Add sendRematch, acceptRematch, declineRematch methods - Handle surrender stats updates and game cleanup Server (server.js): - Add socket listeners for: surrender, send_rematch, accept_rematch, decline_rematch Features now fully functional: - Players can surrender during active games - Players can request rematches after games end - Opponents receive rematch notifications - Game-over modal shows stats and rematch option
This commit is contained in:
115
multiplayer.js
115
multiplayer.js
@@ -173,6 +173,20 @@ class MultiplayerClient {
|
||||
this.handleGameEnded(data);
|
||||
});
|
||||
|
||||
socket.on('rematch_request', (data) => {
|
||||
this.handleRematchRequest(data);
|
||||
});
|
||||
|
||||
socket.on('rematch_accepted', (data) => {
|
||||
this.startMultiplayerGame(data);
|
||||
});
|
||||
|
||||
socket.on('rematch_declined', (data) => {
|
||||
this.showMessage(`${data.by} declined the rematch`, 'error');
|
||||
document.getElementById('gameOverModal').classList.remove('active');
|
||||
this.returnToLobby();
|
||||
});
|
||||
|
||||
socket.on('opponent_disconnected', (data) => {
|
||||
this.showMessage(data.message + '. Waiting for reconnection...', 'warning');
|
||||
});
|
||||
@@ -354,8 +368,15 @@ class MultiplayerClient {
|
||||
this.currentGameId = data.gameId;
|
||||
this.mySymbol = data.yourSymbol;
|
||||
this.opponent = data.opponent;
|
||||
this.opponentId = data.opponentId; // Store for rematch
|
||||
this.myTurn = data.yourTurn;
|
||||
|
||||
// Show surrender button
|
||||
const surrenderBtn = document.getElementById('surrenderBtn');
|
||||
if (surrenderBtn) {
|
||||
surrenderBtn.style.display = 'flex';
|
||||
}
|
||||
|
||||
// Update UI - hide multiplayer lobby, show game board
|
||||
document.getElementById('multiplayerPanel').style.display = 'none';
|
||||
document.getElementById('gameControls').style.display = 'grid';
|
||||
@@ -495,13 +516,44 @@ class MultiplayerClient {
|
||||
document.getElementById('playerDraws').textContent = data.stats.draws;
|
||||
}
|
||||
|
||||
this.showMessage(message, 'success');
|
||||
// Hide surrender button
|
||||
const surrenderBtn = document.getElementById('surrenderBtn');
|
||||
if (surrenderBtn) {
|
||||
surrenderBtn.style.display = 'none';
|
||||
}
|
||||
|
||||
// Show multiplayer panel again
|
||||
setTimeout(() => {
|
||||
document.getElementById('multiplayerPanel').style.display = 'block';
|
||||
this.socket.emit('request_active_players');
|
||||
}, 3000);
|
||||
// Show game-over modal with stats
|
||||
const modal = document.getElementById('gameOverModal');
|
||||
const icon = document.getElementById('gameOverIcon');
|
||||
const title = document.getElementById('gameOverTitle');
|
||||
const subtitle = document.getElementById('gameOverMessage');
|
||||
|
||||
if (modal && icon && title && subtitle) {
|
||||
// Set icon and title based on result
|
||||
if (data.reason === 'win' || data.reason === 'opponent_abandoned') {
|
||||
icon.textContent = '🏆';
|
||||
title.textContent = 'Victory!';
|
||||
subtitle.textContent = data.reason === 'opponent_abandoned' ? 'Opponent disconnected' : 'Great game!';
|
||||
} else if (data.reason === 'loss' || data.reason === 'surrender') {
|
||||
icon.textContent = '😔';
|
||||
title.textContent = 'Defeat';
|
||||
subtitle.textContent = data.reason === 'surrender' ? 'You surrendered' : 'Better luck next time!';
|
||||
} else {
|
||||
icon.textContent = '🤝';
|
||||
title.textContent = "It's a Draw!";
|
||||
subtitle.textContent = 'Well played!';
|
||||
}
|
||||
|
||||
// Update stats in modal
|
||||
if (data.stats) {
|
||||
document.getElementById('gameOverWins').textContent = data.stats.wins;
|
||||
document.getElementById('gameOverLosses').textContent = data.stats.losses;
|
||||
document.getElementById('gameOverDraws').textContent = data.stats.draws;
|
||||
}
|
||||
|
||||
// Show modal
|
||||
modal.classList.add('active');
|
||||
}
|
||||
}
|
||||
|
||||
// Handle disconnect
|
||||
@@ -538,6 +590,57 @@ class MultiplayerClient {
|
||||
messageEl.className = `status-text-small ${type}`;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle rematch request from opponent
|
||||
handleRematchRequest(data) {
|
||||
const notification = document.createElement('div');
|
||||
notification.className = 'challenge-notification';
|
||||
notification.innerHTML = `
|
||||
<div class="challenge-content">
|
||||
<h3>Rematch Request!</h3>
|
||||
<p><strong>${data.from}</strong> wants a rematch</p>
|
||||
<p>Board size: ${data.boardSize}×${data.boardSize}</p>
|
||||
<div class="challenge-actions">
|
||||
<button class="accept-btn" onclick="acceptRematchFromNotification('${data.rematchId}')">
|
||||
Accept
|
||||
</button>
|
||||
<button class="decline-btn" onclick="declineRematchFromNotification('${data.rematchId}')">
|
||||
Decline
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
document.body.appendChild(notification);
|
||||
setTimeout(() => notification.classList.add('active'), 10);
|
||||
}
|
||||
}
|
||||
|
||||
// Global helper functions for rematch from notifications
|
||||
function acceptRematchFromNotification(rematchId) {
|
||||
if (window.multiplayerClient && window.multiplayerClient.socket) {
|
||||
window.multiplayerClient.socket.emit('accept_rematch', { rematchId });
|
||||
|
||||
// Hide game over modal if visible
|
||||
const modal = document.getElementById('gameOverModal');
|
||||
if (modal) {
|
||||
modal.classList.remove('active');
|
||||
}
|
||||
|
||||
// Remove notification
|
||||
const notifications = document.querySelectorAll('.challenge-notification');
|
||||
notifications.forEach(n => n.remove());
|
||||
}
|
||||
}
|
||||
|
||||
function declineRematchFromNotification(rematchId) {
|
||||
if (window.multiplayerClient && window.multiplayerClient.socket) {
|
||||
window.multiplayerClient.socket.emit('decline_rematch', { rematchId });
|
||||
|
||||
// Remove notification
|
||||
const notifications = document.querySelectorAll('.challenge-notification');
|
||||
notifications.forEach(n => n.remove());
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize multiplayer client (will be used by game.js)
|
||||
|
||||
Reference in New Issue
Block a user