mirror of
https://github.com/DeNNiiInc/Connect-5.git
synced 2026-04-17 18:26:01 +00:00
Add surrender and rematch UI - Part 1
- Add surrender button to game controls - Add game-over modal with stats and rematch option - Add surrender confirmation modal - Add all CSS styling for new modals and buttons - Add surrender-rematch.js with global helper functions - Update multiplayer.js constructor to track opponent for rematch
This commit is contained in:
285
index.html
285
index.html
@@ -45,10 +45,19 @@
|
||||
<main class="game-container">
|
||||
<!-- Game Mode Toggle -->
|
||||
<div class="mode-selector">
|
||||
<button class="mode-btn active" id="localModeBtn" onclick="toggleGameMode('local')">
|
||||
<button
|
||||
class="mode-btn active"
|
||||
id="localModeBtn"
|
||||
onclick="toggleGameMode('local')"
|
||||
>
|
||||
🎮 Local Play
|
||||
</button>
|
||||
<button class="mode-btn" id="multiplayerModeBtn" onclick="toggleGameMode('multiplayer')" disabled>
|
||||
<button
|
||||
class="mode-btn"
|
||||
id="multiplayerModeBtn"
|
||||
onclick="toggleGameMode('multiplayer')"
|
||||
disabled
|
||||
>
|
||||
🌐 Multiplayer
|
||||
</button>
|
||||
</div>
|
||||
@@ -74,11 +83,17 @@
|
||||
<span class="player-marker" id="currentPlayer">X</span>
|
||||
</div>
|
||||
<!-- Status text integrated here -->
|
||||
<span class="status-text-small" id="statusMessage">Player X starts</span>
|
||||
<span class="status-text-small" id="statusMessage"
|
||||
>Player X starts</span
|
||||
>
|
||||
</div>
|
||||
|
||||
<!-- Right Side: Player Identity (Multiplayer only) -->
|
||||
<div class="status-right" id="playerIdentitySection" style="display: none;">
|
||||
<div
|
||||
class="status-right"
|
||||
id="playerIdentitySection"
|
||||
style="display: none"
|
||||
>
|
||||
<span class="label">You are:</span>
|
||||
<div class="player-display">
|
||||
<span class="player-marker" id="playerIdentity">?</span>
|
||||
@@ -113,6 +128,27 @@
|
||||
</svg>
|
||||
New Game
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="surrender-btn"
|
||||
id="surrenderBtn"
|
||||
style="display: none"
|
||||
onclick="confirmSurrender()"
|
||||
>
|
||||
<svg
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
>
|
||||
<path
|
||||
d="M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2 0 014 0z"
|
||||
/>
|
||||
</svg>
|
||||
Surrender
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="board-wrapper">
|
||||
@@ -120,7 +156,11 @@
|
||||
</div>
|
||||
|
||||
<!-- Multiplayer Panel -->
|
||||
<div id="multiplayerPanel" class="multiplayer-panel" style="display: none;">
|
||||
<div
|
||||
id="multiplayerPanel"
|
||||
class="multiplayer-panel"
|
||||
style="display: none"
|
||||
>
|
||||
<div class="player-stats-card">
|
||||
<h3>Your Stats</h3>
|
||||
<div class="stats-grid">
|
||||
@@ -191,20 +231,39 @@
|
||||
<span class="db-status-value" id="dbTimestamp">--</span>
|
||||
</div>
|
||||
<div class="db-status-item">
|
||||
<button class="db-retry-btn" id="dbRetryBtn" onclick="window.dbStatusMonitor && window.dbStatusMonitor.retryConnection()" title="Retry SQL Connection">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M21.5 2v6h-6M2.5 22v-6h6M2 11.5a10 10 0 0 1 18.8-4.3M22 12.5a10 10 0 0 1-18.8 4.2"/>
|
||||
<button
|
||||
class="db-retry-btn"
|
||||
id="dbRetryBtn"
|
||||
onclick="window.dbStatusMonitor && window.dbStatusMonitor.retryConnection()"
|
||||
title="Retry SQL Connection"
|
||||
>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
>
|
||||
<path
|
||||
d="M21.5 2v6h-6M2.5 22v-6h6M2 11.5a10 10 0 0 1 18.8-4.3M22 12.5a10 10 0 0 1-18.8 4.2"
|
||||
/>
|
||||
</svg>
|
||||
Retry
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Database Status Details (for testing phase) -->
|
||||
<div class="db-status-details" id="dbStatusDetails" style="display: none;">
|
||||
<div class="db-status-details" id="dbStatusDetails" style="display: none">
|
||||
<div class="db-details-header">
|
||||
<span class="db-details-title">🔍 SQL Connection Details</span>
|
||||
<button class="db-details-close" onclick="document.getElementById('dbStatusDetails').style.display='none'">×</button>
|
||||
<button
|
||||
class="db-details-close"
|
||||
onclick="document.getElementById('dbStatusDetails').style.display='none'"
|
||||
>
|
||||
×
|
||||
</button>
|
||||
</div>
|
||||
<div class="db-details-content" id="dbDetailsContent">
|
||||
<div class="db-detail-item">
|
||||
@@ -221,14 +280,16 @@
|
||||
</div>
|
||||
<div class="db-detail-item">
|
||||
<span class="db-detail-label">Error:</span>
|
||||
<span class="db-detail-value db-detail-error" id="detailError">None</span>
|
||||
<span class="db-detail-value db-detail-error" id="detailError"
|
||||
>None</span
|
||||
>
|
||||
</div>
|
||||
<div class="db-detail-logs" id="detailLogs"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- SQL Error Display (Testing Phase) -->
|
||||
<div class="sql-error-banner" id="sqlErrorBanner" style="display: none;">
|
||||
<div class="sql-error-banner" id="sqlErrorBanner" style="display: none">
|
||||
<div class="sql-error-header">
|
||||
<span class="sql-error-icon">⚠️</span>
|
||||
<span class="sql-error-title">SQL Connection Error</span>
|
||||
@@ -256,15 +317,97 @@
|
||||
<div class="modal-overlay" id="usernameModal">
|
||||
<div class="modal-content username-modal">
|
||||
<h2>Enter Your Username</h2>
|
||||
<p class="modal-subtitle">Choose a family-friendly name (3-20 characters)</p>
|
||||
<input type="text" id="usernameInput" placeholder="Your username" maxlength="20" />
|
||||
<div id="usernameError" class="error-message" style="display: none;"></div>
|
||||
<p class="modal-subtitle">
|
||||
Choose a family-friendly name (3-20 characters)
|
||||
</p>
|
||||
<input
|
||||
type="text"
|
||||
id="usernameInput"
|
||||
placeholder="Your username"
|
||||
maxlength="20"
|
||||
/>
|
||||
<div
|
||||
id="usernameError"
|
||||
class="error-message"
|
||||
style="display: none"
|
||||
></div>
|
||||
<button class="submit-btn" onclick="submitUsername()">
|
||||
Join Multiplayer
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Game Over Modal -->
|
||||
<div class="modal-overlay" id="gameOverModal">
|
||||
<div class="modal-content game-over-modal">
|
||||
<div class="game-over-icon" id="gameOverIcon">🎮</div>
|
||||
<h2 id="gameOverTitle">Game Over</h2>
|
||||
<p class="game-over-subtitle" id="gameOverMessage">
|
||||
Thanks for playing!
|
||||
</p>
|
||||
|
||||
<div class="game-over-stats">
|
||||
<div class="stat-item">
|
||||
<span class="stat-label">Wins:</span>
|
||||
<span class="stat-value" id="gameOverWins">0</span>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<span class="stat-label">Losses:</span>
|
||||
<span class="stat-value" id="gameOverLosses">0</span>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<span class="stat-label">Draws:</span>
|
||||
<span class="stat-value" id="gameOverDraws">0</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="game-over-actions">
|
||||
<button
|
||||
class="rematch-btn"
|
||||
id="rematchBtn"
|
||||
onclick="requestRematch()"
|
||||
>
|
||||
<svg
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
>
|
||||
<path
|
||||
d="M21.5 2v6h-6M2.5 22v-6h6M2 11.5a10 10 0 0 1 18.8-4.3M22 12.5a10 10 0 0 1-18.8 4.2"
|
||||
/>
|
||||
</svg>
|
||||
Challenge Again
|
||||
</button>
|
||||
<button class="lobby-btn" onclick="returnToLobby()">
|
||||
← Return to Lobby
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Surrender Confirmation Modal -->
|
||||
<div class="modal-overlay" id="surrenderModal">
|
||||
<div class="modal-content surrender-modal">
|
||||
<div class="surrender-icon">⚠️</div>
|
||||
<h2>Surrender Game?</h2>
|
||||
<p class="modal-subtitle">
|
||||
Are you sure you want to surrender? This will count as a loss.
|
||||
</p>
|
||||
<div class="surrender-actions">
|
||||
<button class="confirm-surrender-btn" onclick="executeSurrender()">
|
||||
Yes, Surrender
|
||||
</button>
|
||||
<button class="cancel-surrender-btn" onclick="cancelSurrender()">
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="surrender-rematch.js"></script>
|
||||
<script src="storage.js"></script>
|
||||
<script src="db-status.js"></script>
|
||||
<script src="multiplayer.js"></script>
|
||||
@@ -272,51 +415,51 @@
|
||||
<script>
|
||||
// Game mode toggling
|
||||
function toggleGameMode(mode) {
|
||||
const localBtn = document.getElementById('localModeBtn');
|
||||
const multiplayerBtn = document.getElementById('multiplayerModeBtn');
|
||||
const gameControls = document.getElementById('gameControls');
|
||||
const multiplayerPanel = document.getElementById('multiplayerPanel');
|
||||
const boardWrapper = document.querySelector('.board-wrapper');
|
||||
const localBtn = document.getElementById("localModeBtn");
|
||||
const multiplayerBtn = document.getElementById("multiplayerModeBtn");
|
||||
const gameControls = document.getElementById("gameControls");
|
||||
const multiplayerPanel = document.getElementById("multiplayerPanel");
|
||||
const boardWrapper = document.querySelector(".board-wrapper");
|
||||
// statusMessage is now inside gameControls, so we don't need to toggle it separately
|
||||
|
||||
if (mode === 'local') {
|
||||
localBtn.classList.add('active');
|
||||
multiplayerBtn.classList.remove('active');
|
||||
gameControls.style.display = 'grid';
|
||||
multiplayerPanel.style.display = 'none';
|
||||
boardWrapper.style.display = 'flex';
|
||||
if (mode === "local") {
|
||||
localBtn.classList.add("active");
|
||||
multiplayerBtn.classList.remove("active");
|
||||
gameControls.style.display = "grid";
|
||||
multiplayerPanel.style.display = "none";
|
||||
boardWrapper.style.display = "flex";
|
||||
|
||||
// Reset to local mode
|
||||
if (window.multiplayerClient) {
|
||||
window.multiplayerClient.isMultiplayer = false;
|
||||
}
|
||||
|
||||
|
||||
// Reset turn label to default
|
||||
const turnLabel = document.getElementById('turnLabel');
|
||||
const turnLabel = document.getElementById("turnLabel");
|
||||
if (turnLabel) {
|
||||
turnLabel.textContent = 'Current Turn:';
|
||||
turnLabel.textContent = "Current Turn:";
|
||||
}
|
||||
} else {
|
||||
localBtn.classList.remove('active');
|
||||
multiplayerBtn.classList.add('active');
|
||||
gameControls.style.display = 'none';
|
||||
multiplayerPanel.style.display = 'block';
|
||||
boardWrapper.style.display = 'none';
|
||||
localBtn.classList.remove("active");
|
||||
multiplayerBtn.classList.add("active");
|
||||
gameControls.style.display = "none";
|
||||
multiplayerPanel.style.display = "block";
|
||||
boardWrapper.style.display = "none";
|
||||
|
||||
// Initialize multiplayer if not already
|
||||
if (!window.multiplayerClient) {
|
||||
if (!window.game) {
|
||||
console.log('⏳ Waiting for game to initialize...');
|
||||
// Poll for game to be ready
|
||||
const checkGame = setInterval(() => {
|
||||
if (window.game) {
|
||||
clearInterval(checkGame);
|
||||
console.log('✅ Game ready, initializing multiplayer...');
|
||||
window.multiplayerClient = new MultiplayerClient(window.game);
|
||||
window.multiplayerClient.connect();
|
||||
}
|
||||
}, 100); // Check every 100ms
|
||||
return;
|
||||
console.log("⏳ Waiting for game to initialize...");
|
||||
// Poll for game to be ready
|
||||
const checkGame = setInterval(() => {
|
||||
if (window.game) {
|
||||
clearInterval(checkGame);
|
||||
console.log("✅ Game ready, initializing multiplayer...");
|
||||
window.multiplayerClient = new MultiplayerClient(window.game);
|
||||
window.multiplayerClient.connect();
|
||||
}
|
||||
}, 100); // Check every 100ms
|
||||
return;
|
||||
}
|
||||
window.multiplayerClient = new MultiplayerClient(window.game);
|
||||
window.multiplayerClient.connect();
|
||||
@@ -326,59 +469,61 @@
|
||||
|
||||
// Submit username
|
||||
function submitUsername() {
|
||||
const input = document.getElementById('usernameInput');
|
||||
const error = document.getElementById('usernameError');
|
||||
const input = document.getElementById("usernameInput");
|
||||
const error = document.getElementById("usernameError");
|
||||
const username = input.value.trim();
|
||||
|
||||
if (!username) {
|
||||
error.textContent = 'Please enter a username';
|
||||
error.style.display = 'block';
|
||||
error.textContent = "Please enter a username";
|
||||
error.style.display = "block";
|
||||
return;
|
||||
}
|
||||
|
||||
if (username.length < 3 || username.length > 20) {
|
||||
error.textContent = 'Username must be 3-20 characters';
|
||||
error.style.display = 'block';
|
||||
error.textContent = "Username must be 3-20 characters";
|
||||
error.style.display = "block";
|
||||
return;
|
||||
}
|
||||
|
||||
if (!/^[a-zA-Z0-9_-]+$/.test(username)) {
|
||||
error.textContent = 'Only letters, numbers, underscores, and hyphens allowed';
|
||||
error.style.display = 'block';
|
||||
error.textContent =
|
||||
"Only letters, numbers, underscores, and hyphens allowed";
|
||||
error.style.display = "block";
|
||||
return;
|
||||
}
|
||||
|
||||
error.style.display = 'none';
|
||||
|
||||
error.style.display = "none";
|
||||
|
||||
if (window.multiplayerClient) {
|
||||
window.multiplayerClient.registerPlayer(username);
|
||||
window.multiplayerClient.registerPlayer(username);
|
||||
} else {
|
||||
console.error("Multiplayer client not initialized");
|
||||
error.textContent = "Error: Multiplayer not initialized. Refresh page.";
|
||||
error.style.display = 'block';
|
||||
console.error("Multiplayer client not initialized");
|
||||
error.textContent =
|
||||
"Error: Multiplayer not initialized. Refresh page.";
|
||||
error.style.display = "block";
|
||||
}
|
||||
}
|
||||
|
||||
// Change username
|
||||
function changeUsername() {
|
||||
// Clear saved username from localStorage
|
||||
localStorage.removeItem('connect5_username');
|
||||
|
||||
localStorage.removeItem("connect5_username");
|
||||
|
||||
// Show username modal
|
||||
const modal = document.getElementById('usernameModal');
|
||||
const modal = document.getElementById("usernameModal");
|
||||
if (modal) {
|
||||
modal.classList.add('active');
|
||||
modal.classList.add("active");
|
||||
// Clear the input field
|
||||
document.getElementById('usernameInput').value = '';
|
||||
document.getElementById("usernameInput").value = "";
|
||||
}
|
||||
}
|
||||
|
||||
// Allow Enter key to submit username
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const input = document.getElementById('usernameInput');
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
const input = document.getElementById("usernameInput");
|
||||
if (input) {
|
||||
input.addEventListener('keypress', (e) => {
|
||||
if (e.key === 'Enter') {
|
||||
input.addEventListener("keypress", (e) => {
|
||||
if (e.key === "Enter") {
|
||||
submitUsername();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -453,3 +453,200 @@
|
||||
padding: 2rem 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* Surrender Button */
|
||||
.surrender-btn {
|
||||
padding: 0.75rem 1.5rem;
|
||||
background: linear-gradient(135deg, #dc2626 0%, #991b1b 100%);
|
||||
border: none;
|
||||
border-radius: 10px;
|
||||
color: white;
|
||||
font-weight: 600;
|
||||
font-size: 0.95rem;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
font-family: "Inter", sans-serif;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
box-shadow: 0 4px 12px rgba(220, 38, 38, 0.3);
|
||||
}
|
||||
|
||||
.surrender-btn:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 6px 20px rgba(220, 38, 38, 0.5);
|
||||
}
|
||||
|
||||
/* Game Over Modal */
|
||||
.game-over-modal {
|
||||
max-width: 450px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.game-over-icon {
|
||||
font-size: 4rem;
|
||||
margin-bottom: 1rem;
|
||||
animation: bounce 1s ease-in-out;
|
||||
}
|
||||
|
||||
@keyframes bounce {
|
||||
0%, 100% { transform: translateY(0); }
|
||||
50% { transform: translateY(-20px); }
|
||||
}
|
||||
|
||||
.game-over-modal h2 {
|
||||
font-size: 2rem;
|
||||
margin-bottom: 0.5rem;
|
||||
background: var(--accent-gradient);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
}
|
||||
|
||||
.game-over-subtitle {
|
||||
color: var(--text-secondary);
|
||||
font-size: 1.1rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.game-over-stats {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 1rem;
|
||||
padding: 1.5rem;
|
||||
background: var(--bg-tertiary);
|
||||
border-radius: 12px;
|
||||
margin-bottom: 1.5rem;
|
||||
border: 1px solid var(--border-light);
|
||||
}
|
||||
|
||||
.game-over-stats .stat-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.game-over-stats .stat-label {
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.85rem;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.game-over-stats .stat-value {
|
||||
color: var(--accent-primary);
|
||||
font-size: 1.5rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.game-over-actions {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.rematch-btn,
|
||||
.lobby-btn {
|
||||
padding: 1rem 2rem;
|
||||
border: none;
|
||||
border-radius: 12px;
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
font-family: "Inter", sans-serif;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.rematch-btn {
|
||||
background: var(--accent-gradient);
|
||||
color: var(--text-primary);
|
||||
box-shadow: 0 4px 16px rgba(147, 51, 234, 0.3);
|
||||
}
|
||||
|
||||
.rematch-btn:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 6px 24px rgba(147, 51, 234, 0.5);
|
||||
}
|
||||
|
||||
.lobby-btn {
|
||||
background: var(--bg-tertiary);
|
||||
color: var(--text-primary);
|
||||
border: 2px solid var(--border-light);
|
||||
}
|
||||
|
||||
.lobby-btn:hover {
|
||||
border-color: var(--accent-primary);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(147, 51, 234, 0.2);
|
||||
}
|
||||
|
||||
/* Surrender Confirmation Modal */
|
||||
.surrender-modal {
|
||||
max-width: 400px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.surrender-icon {
|
||||
font-size: 4rem;
|
||||
margin-bottom: 1rem;
|
||||
animation: shake 0.5s ease-in-out;
|
||||
}
|
||||
|
||||
@keyframes shake {
|
||||
0%, 100% { transform: translateX(0); }
|
||||
25% { transform: translateX(-10px); }
|
||||
75% { transform: translateX(10px); }
|
||||
}
|
||||
|
||||
.surrender-modal h2 {
|
||||
font-size: 1.75rem;
|
||||
margin-bottom: 0.75rem;
|
||||
color: var(--danger);
|
||||
}
|
||||
|
||||
.surrender-actions {
|
||||
display: flex;
|
||||
gap: 0.75rem;
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
|
||||
.confirm-surrender-btn,
|
||||
.cancel-surrender-btn {
|
||||
flex: 1;
|
||||
padding: 0.875rem 1.5rem;
|
||||
border: none;
|
||||
border-radius: 10px;
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
font-family: "Inter", sans-serif;
|
||||
}
|
||||
|
||||
.confirm-surrender-btn {
|
||||
background: linear-gradient(135deg, #dc2626 0%, #991b1b 100%);
|
||||
color: white;
|
||||
box-shadow: 0 4px 12px rgba(220, 38, 38, 0.3);
|
||||
}
|
||||
|
||||
.confirm-surrender-btn:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 6px 20px rgba(220, 38, 38, 0.5);
|
||||
}
|
||||
|
||||
.cancel-surrender-btn {
|
||||
background: var(--bg-tertiary);
|
||||
color: var(--text-primary);
|
||||
border: 2px solid var(--border-light);
|
||||
}
|
||||
|
||||
.cancel-surrender-btn:hover {
|
||||
border-color: var(--accent-primary);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(147, 51, 234, 0.2);
|
||||
}
|
||||
|
||||
@@ -10,6 +10,9 @@ class MultiplayerClient {
|
||||
this.activePlayers = [];
|
||||
this.pendingChallenges = new Map();
|
||||
this.selectedBoardSize = 15; // Default board size for multiplayer
|
||||
this.opponent = null; // Track opponent for rematch
|
||||
this.opponentId = null;
|
||||
this.lastGameResult = null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
68
surrender-rematch.js
Normal file
68
surrender-rematch.js
Normal file
@@ -0,0 +1,68 @@
|
||||
// Global helper functions for surrender and rematch features
|
||||
|
||||
// Show surrender confirmation
|
||||
function confirmSurrender() {
|
||||
if (!window.multiplayerClient || !window.multiplayerClient.isMultiplayer) {
|
||||
return;
|
||||
}
|
||||
|
||||
const modal = document.getElementById('surrenderModal');
|
||||
if (modal) {
|
||||
modal.classList.add('active');
|
||||
}
|
||||
}
|
||||
|
||||
// Cancel surrender
|
||||
function cancelSurrender() {
|
||||
const modal = document.getElementById('surrenderModal');
|
||||
if (modal) {
|
||||
modal.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
// Execute surrender
|
||||
function executeSurrender() {
|
||||
const modal = document.getElementById('surrenderModal');
|
||||
if (modal) {
|
||||
modal.classList.remove('active');
|
||||
}
|
||||
|
||||
if (window.multiplayerClient) {
|
||||
window.multiplayerClient.socket.emit('surrender', {
|
||||
gameId: window.multiplayerClient.currentGameId
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Request rematch
|
||||
function requestRematch() {
|
||||
if (!window.multiplayerClient || !window.multiplayerClient.opponent) {
|
||||
return;
|
||||
}
|
||||
|
||||
window.multiplayerClient.socket.emit('send_rematch', {
|
||||
opponentId: window.multiplayerClient.opponentId,
|
||||
boardSize: window.multiplayerClient.selectedBoardSize || 15
|
||||
});
|
||||
|
||||
if (window.multiplayerClient.showMessage) {
|
||||
window.multiplayerClient.showMessage(`Rematch request sent to ${window.multiplayerClient.opponent}`, 'info');
|
||||
}
|
||||
|
||||
const modal = document.getElementById('gameOverModal');
|
||||
if (modal) {
|
||||
modal.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
// Return to lobby from game over modal
|
||||
function returnToLobby() {
|
||||
if (window.multiplayerClient) {
|
||||
window.multiplayerClient.returnToLobby();
|
||||
}
|
||||
|
||||
const modal = document.getElementById('gameOverModal');
|
||||
if (modal) {
|
||||
modal.classList.remove('active');
|
||||
}
|
||||
}
|
||||
71
temp-multiplayer-methods.txt
Normal file
71
temp-multiplayer-methods.txt
Normal file
@@ -0,0 +1,71 @@
|
||||
|
||||
// Surrender game
|
||||
surrenderGame() {
|
||||
if (!this.isMultiplayer || !this.currentGameId) {
|
||||
this.showMessage('No active game to surrender', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
this.socket.emit('surrender', { gameId: this.currentGameId });
|
||||
}
|
||||
|
||||
// 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="multiplayerClient.acceptRematch('${data.rematchId}')">
|
||||
Accept
|
||||
</button>
|
||||
<button class="decline-btn" onclick="multiplayerClient.declineRematch('${data.rematchId}')">
|
||||
Decline
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
document.body.appendChild(notification);
|
||||
setTimeout(() => notification.classList.add('active'), 10);
|
||||
}
|
||||
|
||||
// Send rematch request
|
||||
sendRematchRequest() {
|
||||
if (!this.opponentId) {
|
||||
this.showMessage('No opponent to challenge', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
this.socket.emit('send_rematch', {
|
||||
opponentId: this.opponentId,
|
||||
boardSize: this.selectedBoardSize
|
||||
});
|
||||
|
||||
this.showMessage(`Rematch request sent to ${this.opponent}`, 'info');
|
||||
document.getElementById('gameOverModal').classList.remove('active');
|
||||
}
|
||||
|
||||
// Accept rematch
|
||||
acceptRematch(rematchId) {
|
||||
this.socket.emit('accept_rematch', { rematchId });
|
||||
|
||||
// Hide game over modal
|
||||
document.getElementById('gameOverModal').classList.remove('active');
|
||||
|
||||
// Remove notification
|
||||
const notifications = document.querySelectorAll('.challenge-notification');
|
||||
notifications.forEach(n => n.remove());
|
||||
}
|
||||
|
||||
// Decline rematch
|
||||
declineRematch(rematchId) {
|
||||
this.socket.emit('decline_rematch', { rematchId });
|
||||
|
||||
// Remove notification
|
||||
const notifications = document.querySelectorAll('.challenge-notification');
|
||||
notifications.forEach(n => n.remove());
|
||||
}
|
||||
Reference in New Issue
Block a user