mirror of
https://github.com/DeNNiiInc/Connect-5.git
synced 2026-04-17 22:46:00 +00:00
- Add polling mechanism to wait for window.game initialization - Disable multiplayer button until game is fully initialized - Enable button in DOMContentLoaded after game creation - Prevents 'Game instance not found' error on production This fixes the critical issue where clicking Multiplayer too quickly would fail because window.game wasn't ready yet.
301 lines
11 KiB
HTML
301 lines
11 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="turn-indicator">
|
||
<span class="label">Current Turn:</span>
|
||
<div class="player-display">
|
||
<span class="player-marker" id="currentPlayer">X</span>
|
||
</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>
|
||
</div>
|
||
|
||
<div class="board-wrapper">
|
||
<div class="board" id="gameBoard"></div>
|
||
</div>
|
||
|
||
<div class="status-message" id="statusMessage">
|
||
Player X starts the game
|
||
</div>
|
||
|
||
<!-- 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>
|
||
|
||
<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>
|
||
|
||
<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');
|
||
const statusMessage = document.getElementById('statusMessage');
|
||
|
||
if (mode === 'local') {
|
||
localBtn.classList.add('active');
|
||
multiplayerBtn.classList.remove('active');
|
||
gameControls.style.display = 'grid';
|
||
multiplayerPanel.style.display = 'none';
|
||
boardWrapper.style.display = 'flex';
|
||
statusMessage.style.display = 'block';
|
||
|
||
// Reset to local mode
|
||
if (window.multiplayerClient) {
|
||
window.multiplayerClient.isMultiplayer = false;
|
||
}
|
||
} else {
|
||
localBtn.classList.remove('active');
|
||
multiplayerBtn.classList.add('active');
|
||
gameControls.style.display = 'none';
|
||
multiplayerPanel.style.display = 'block';
|
||
boardWrapper.style.display = 'none';
|
||
statusMessage.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>
|