mirror of
https://github.com/DeNNiiInc/Connect-5.git
synced 2026-04-17 18:26:01 +00:00
587 lines
20 KiB
HTML
587 lines
20 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||
<meta name="description" content="Connect-5 - Premium Gomoku Game" />
|
||
<title>Connect-5 - Premium Gomoku Game</title>
|
||
<link rel="icon" type="image/png" href="Logo.png" />
|
||
<link rel="stylesheet" href="styles.css" />
|
||
<link rel="stylesheet" href="multiplayer-styles.css" />
|
||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||
<link
|
||
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap"
|
||
rel="stylesheet"
|
||
/>
|
||
<!-- Socket.io Client -->
|
||
<script src="https://cdn.socket.io/4.7.2/socket.io.min.js"></script>
|
||
</head>
|
||
<body>
|
||
<div class="container">
|
||
<header class="header">
|
||
<div class="header-content">
|
||
<h1 class="title">
|
||
<img src="Logo.png" alt="BCT Logo" class="title-icon" />
|
||
Connect-5
|
||
</h1>
|
||
<p class="subtitle">Premium Gomoku Experience</p>
|
||
<a
|
||
href="https://www.youtube.com/@beyondcloudtechnology"
|
||
target="_blank"
|
||
rel="noopener noreferrer"
|
||
class="youtube-link"
|
||
>
|
||
<svg class="youtube-icon" viewBox="0 0 24 24" fill="currentColor">
|
||
<path
|
||
d="M23.498 6.186a3.016 3.016 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z"
|
||
/>
|
||
</svg>
|
||
Watch on YouTube @beyondcloudtechnology
|
||
</a>
|
||
</div>
|
||
</header>
|
||
|
||
<main class="game-container">
|
||
<!-- Game Mode Toggle -->
|
||
<div class="mode-selector">
|
||
<button
|
||
class="mode-btn active"
|
||
id="localModeBtn"
|
||
onclick="toggleGameMode('local')"
|
||
>
|
||
🎮 Local Play
|
||
</button>
|
||
<button
|
||
class="mode-btn"
|
||
id="multiplayerModeBtn"
|
||
onclick="toggleGameMode('multiplayer')"
|
||
disabled
|
||
>
|
||
🌐 Multiplayer
|
||
</button>
|
||
</div>
|
||
|
||
<!-- Local Game Controls -->
|
||
<div id="gameControls" class="controls-panel">
|
||
<div class="size-selector">
|
||
<label for="boardSize">Board Size:</label>
|
||
<div class="size-buttons">
|
||
<button class="size-btn active" data-size="15">15×15</button>
|
||
<button class="size-btn" data-size="20">20×20</button>
|
||
<button class="size-btn" data-size="25">25×25</button>
|
||
<button class="size-btn" data-size="50">50×50</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="game-info">
|
||
<div class="game-status-bar">
|
||
<!-- Left Side: Turn Status -->
|
||
<div class="status-left">
|
||
<div class="turn-display">
|
||
<span class="label">Current Turn:</span>
|
||
<span class="player-marker" id="currentPlayer">X</span>
|
||
</div>
|
||
<!-- Status text integrated here -->
|
||
<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"
|
||
>
|
||
<span class="label">You are:</span>
|
||
<div class="player-display">
|
||
<span class="player-marker" id="playerIdentity">?</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="score-display">
|
||
<div class="score-item">
|
||
<span class="score-label">Player X</span>
|
||
<span class="score-value" id="scoreX">0</span>
|
||
</div>
|
||
<div class="score-item">
|
||
<span class="score-label">Player O</span>
|
||
<span class="score-value" id="scoreO">0</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<button class="reset-btn" id="resetBtn">
|
||
<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>
|
||
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">
|
||
<div class="board" id="gameBoard"></div>
|
||
</div>
|
||
|
||
<!-- Grid Brightness Controls -->
|
||
<div class="grid-controls">
|
||
<div class="control-group">
|
||
<label for="gridLineOpacity">Grid Lines</label>
|
||
<input
|
||
type="range"
|
||
id="gridLineOpacity"
|
||
min="0.1"
|
||
max="1"
|
||
step="0.1"
|
||
value="0.3"
|
||
/>
|
||
</div>
|
||
<div class="control-group">
|
||
<label for="dotOpacity">Intersections</label>
|
||
<input
|
||
type="range"
|
||
id="dotOpacity"
|
||
min="0.1"
|
||
max="1"
|
||
step="0.1"
|
||
value="0.4"
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
const gridLineSlider = document.getElementById("gridLineOpacity");
|
||
const dotOpacitySlider = document.getElementById("dotOpacity");
|
||
|
||
gridLineSlider.addEventListener("input", (e) => {
|
||
document.documentElement.style.setProperty(
|
||
"--grid-line-opacity",
|
||
e.target.value
|
||
);
|
||
});
|
||
|
||
dotOpacitySlider.addEventListener("input", (e) => {
|
||
document.documentElement.style.setProperty(
|
||
"--grid-dot-opacity",
|
||
e.target.value
|
||
);
|
||
});
|
||
</script>
|
||
|
||
<!-- Multiplayer Panel -->
|
||
<div
|
||
id="multiplayerPanel"
|
||
class="multiplayer-panel"
|
||
style="display: none"
|
||
>
|
||
<div class="player-stats-card">
|
||
<h3>Your Stats</h3>
|
||
<div class="stats-grid">
|
||
<div class="stat-item">
|
||
<span class="stat-label">Player:</span>
|
||
<span class="stat-value" id="playerUsername">-</span>
|
||
</div>
|
||
<div class="stat-item">
|
||
<span class="stat-label">Wins:</span>
|
||
<span class="stat-value" id="playerWins">0</span>
|
||
</div>
|
||
<div class="stat-item">
|
||
<span class="stat-label">Losses:</span>
|
||
<span class="stat-value" id="playerLosses">0</span>
|
||
</div>
|
||
<div class="stat-item">
|
||
<span class="stat-label">Draws:</span>
|
||
<span class="stat-value" id="playerDraws">0</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="board-size-selector-mp">
|
||
<h3>Select Board Size</h3>
|
||
<div class="size-buttons">
|
||
<button class="size-btn-mp active" data-size="15">15×15</button>
|
||
<button class="size-btn-mp" data-size="20">20×20</button>
|
||
<button class="size-btn-mp" data-size="25">25×25</button>
|
||
<button class="size-btn-mp" data-size="50">50×50</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="active-players-card">
|
||
<h3>Online Players</h3>
|
||
<div id="activePlayersList" class="players-list">
|
||
<div class="loading">Loading players...</div>
|
||
</div>
|
||
</div>
|
||
|
||
<button class="return-btn" onclick="toggleGameMode('local')">
|
||
← Return to Local Play
|
||
</button>
|
||
</div>
|
||
</main>
|
||
|
||
<!-- Database Status Bar -->
|
||
<div class="db-status-bar" id="dbStatusBar">
|
||
<div class="db-status-item">
|
||
<span class="db-status-label">SQL Connection:</span>
|
||
<span class="db-status-indicator" id="dbConnection">
|
||
<span class="status-dot"></span>
|
||
<span class="status-text">Checking...</span>
|
||
</span>
|
||
</div>
|
||
<div class="db-status-item">
|
||
<span class="db-status-label">Latency:</span>
|
||
<span class="db-status-value" id="dbLatency">-- ms</span>
|
||
</div>
|
||
<div class="db-status-item">
|
||
<span class="db-status-label">Write Capability:</span>
|
||
<span class="db-status-indicator" id="dbWrite">
|
||
<span class="status-dot"></span>
|
||
<span class="status-text">Checking...</span>
|
||
</span>
|
||
</div>
|
||
<div class="db-status-item db-status-timestamp">
|
||
<span class="db-status-label">Last Check:</span>
|
||
<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"
|
||
/>
|
||
</svg>
|
||
Retry
|
||
</button>
|
||
</div>
|
||
<div class="db-status-item version-container">
|
||
<div class="version-pill" id="gitVersion" title="Git Commit Version" style="display: none;">
|
||
<span class="commit-hash" id="commitHash"></span>
|
||
<span class="version-separator">•</span>
|
||
<span class="commit-age" id="commitAge"></span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Database Status Details (for testing phase) -->
|
||
<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>
|
||
</div>
|
||
<div class="db-details-content" id="dbDetailsContent">
|
||
<div class="db-detail-item">
|
||
<span class="db-detail-label">Status:</span>
|
||
<span class="db-detail-value" id="detailStatus">--</span>
|
||
</div>
|
||
<div class="db-detail-item">
|
||
<span class="db-detail-label">Host:</span>
|
||
<span class="db-detail-value" id="detailHost">--</span>
|
||
</div>
|
||
<div class="db-detail-item">
|
||
<span class="db-detail-label">Database:</span>
|
||
<span class="db-detail-value" id="detailDatabase">--</span>
|
||
</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
|
||
>
|
||
</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-header">
|
||
<span class="sql-error-icon">⚠️</span>
|
||
<span class="sql-error-title">SQL Connection Error</span>
|
||
</div>
|
||
<div class="sql-error-content">
|
||
<div class="sql-error-message" id="sqlErrorMessage">No errors</div>
|
||
<div class="sql-error-details" id="sqlErrorDetails"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<footer class="footer">
|
||
<p>© 2025 Beyond Cloud Technology. All rights reserved.</p>
|
||
</footer>
|
||
</div>
|
||
|
||
<div class="victory-overlay" id="victoryOverlay">
|
||
<div class="victory-content">
|
||
<h2 class="victory-title" id="victoryTitle">Player X Wins!</h2>
|
||
<p class="victory-subtitle">Five in a row achieved!</p>
|
||
<button class="play-again-btn" id="playAgainBtn">Play Again</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Username Modal -->
|
||
<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>
|
||
<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>
|
||
<script src="game.js"></script>
|
||
<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");
|
||
// 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";
|
||
|
||
// Reset to local mode
|
||
if (window.multiplayerClient) {
|
||
window.multiplayerClient.isMultiplayer = false;
|
||
}
|
||
|
||
// Reset turn label to default
|
||
const turnLabel = document.getElementById("turnLabel");
|
||
if (turnLabel) {
|
||
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";
|
||
|
||
// 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;
|
||
}
|
||
window.multiplayerClient = new MultiplayerClient(window.game);
|
||
window.multiplayerClient.connect();
|
||
}
|
||
}
|
||
}
|
||
|
||
// Submit username
|
||
function submitUsername() {
|
||
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";
|
||
return;
|
||
}
|
||
|
||
if (username.length < 3 || username.length > 20) {
|
||
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";
|
||
return;
|
||
}
|
||
|
||
error.style.display = "none";
|
||
|
||
if (window.multiplayerClient) {
|
||
window.multiplayerClient.registerPlayer(username);
|
||
} else {
|
||
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");
|
||
|
||
// Show username modal
|
||
const modal = document.getElementById("usernameModal");
|
||
if (modal) {
|
||
modal.classList.add("active");
|
||
// Clear the input field
|
||
document.getElementById("usernameInput").value = "";
|
||
}
|
||
}
|
||
|
||
// Allow Enter key to submit username
|
||
document.addEventListener("DOMContentLoaded", () => {
|
||
const input = document.getElementById("usernameInput");
|
||
if (input) {
|
||
input.addEventListener("keypress", (e) => {
|
||
if (e.key === "Enter") {
|
||
submitUsername();
|
||
}
|
||
});
|
||
}
|
||
});
|
||
</script>
|
||
</body>
|
||
</html>
|