mirror of
https://github.com/DeNNiiInc/Connect-5.git
synced 2026-04-17 20:36:00 +00:00
- Created storage.js wrapper with IndexedDB support - Automatic fallback to localStorage if IndexedDB unavailable - Updated all username storage calls to use async API - Better performance and more storage capacity - Improved PWA compatibility Benefits: - 50MB+ storage vs 5-10MB localStorage - Async API doesn't block main thread - Better for future features (game history, stats, etc.)
302 lines
11 KiB
HTML
302 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="storage.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');
|
||
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>
|