465 lines
20 KiB
HTML
465 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, maximum-scale=1.0, user-scalable=no">
|
||
<title>Shelem</title>
|
||
<link rel="manifest" href="/manifest.json">
|
||
<meta name="theme-color" content="#1a3a1a">
|
||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
||
<meta name="apple-mobile-web-app-title" content="Shelem">
|
||
<link rel="apple-touch-icon" href="/icons/icon-192.png">
|
||
<link rel="icon" type="image/svg+xml" href="/icons/icon.svg">
|
||
<link rel="stylesheet" href="style.css">
|
||
</head>
|
||
<body>
|
||
|
||
<!-- ══════════════ LOBBY ══════════════ -->
|
||
<div id="screen-lobby" class="screen active">
|
||
<div class="lobby-box">
|
||
<h1 class="logo">🃏 Shelem</h1>
|
||
<p class="tagline">The Persian trick-taking partnership game</p>
|
||
|
||
<div id="auth-bar" class="auth-bar">
|
||
<span id="auth-status" class="auth-status">Playing as guest</span>
|
||
<div class="auth-bar-actions">
|
||
<button id="btn-show-leaderboard" class="btn-text">🏆 Leaderboard</button>
|
||
<button id="btn-show-profile" class="btn-text hidden">👤 Profile</button>
|
||
<button id="btn-logout" class="btn-text hidden">Logout</button>
|
||
<button id="btn-show-auth" class="btn-text">Login / Register</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="field">
|
||
<label>Your Name</label>
|
||
<input id="input-name" type="text" maxlength="16" placeholder="Enter your name…" autocomplete="off">
|
||
</div>
|
||
|
||
<div class="lobby-tabs">
|
||
<button class="tab-btn active" data-tab="create">Create Game</button>
|
||
<button class="tab-btn" data-tab="join">Join Game</button>
|
||
</div>
|
||
|
||
<div id="tab-create" class="tab-panel active">
|
||
<div class="field">
|
||
<label>Game Mode</label>
|
||
<div class="mode-row">
|
||
<button class="mode-btn active" data-joker="true">🃏 With Jokers <small>(200 pts)</small></button>
|
||
<button class="mode-btn" data-joker="false">♠ No Jokers <small>(165 pts)</small></button>
|
||
</div>
|
||
</div>
|
||
<div class="field">
|
||
<label>Win Score <small>(first team to reach this wins)</small></label>
|
||
<div class="score-row">
|
||
<button class="score-btn" data-score="205">205</button>
|
||
<button class="score-btn active" data-score="505">505</button>
|
||
<button class="score-btn" data-score="1005">1005</button>
|
||
</div>
|
||
</div>
|
||
<div class="field">
|
||
<label>Visibility</label>
|
||
<div class="visibility-row">
|
||
<button class="visibility-btn active" data-public="false">🔒 Private</button>
|
||
<button class="visibility-btn" data-public="true">🌐 Public</button>
|
||
</div>
|
||
</div>
|
||
<button id="btn-create" class="btn-primary">Create Game</button>
|
||
</div>
|
||
|
||
<div id="tab-join" class="tab-panel">
|
||
<div class="field">
|
||
<label>Room Code</label>
|
||
<input id="input-code" type="text" maxlength="8" placeholder="e.g. AB12CD" autocomplete="off" style="text-transform:uppercase">
|
||
</div>
|
||
<button id="btn-join" class="btn-primary">Join Game</button>
|
||
<hr class="divider">
|
||
<div class="public-rooms-section">
|
||
<div class="rooms-header">
|
||
<span class="rooms-label">Public Rooms</span>
|
||
<button id="btn-refresh-rooms" class="btn-text">↺</button>
|
||
</div>
|
||
<div id="public-rooms-list"><p class="hint">Loading…</p></div>
|
||
</div>
|
||
<hr class="divider">
|
||
<div class="field">
|
||
<label>Watch as spectator</label>
|
||
<input id="input-spectate-code" type="text" maxlength="8" placeholder="Room code" style="text-transform:uppercase" autocomplete="off">
|
||
</div>
|
||
<button id="btn-spectate" class="btn-secondary">👁 Watch Game</button>
|
||
</div>
|
||
|
||
<p id="lobby-error" class="error-msg"></p>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ══════════════ WAITING ROOM ══════════════ -->
|
||
<div id="screen-waiting" class="screen">
|
||
<div class="waiting-box">
|
||
<div class="waiting-top-row">
|
||
<button id="btn-leave-waiting" class="btn-leave-screen">← Leave</button>
|
||
<div class="game-menu-wrap" style="margin-left:auto">
|
||
<button id="btn-waiting-menu" class="btn-leave-screen" title="Options">☰</button>
|
||
<div id="waiting-menu-dropdown" class="game-menu-dropdown hidden">
|
||
<button id="btn-waiting-reload" class="game-menu-item">↺ Reload</button>
|
||
<button id="btn-waiting-exit" class="game-menu-item game-menu-exit">🚪 Exit</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<h2>Waiting for Players</h2>
|
||
<div class="room-code-box">
|
||
<span class="label">Room Code</span>
|
||
<span id="display-room-code" class="room-code">——</span>
|
||
<button id="btn-copy" class="btn-copy" title="Copy code">⧉</button>
|
||
</div>
|
||
<p class="hint">Share this code — 4 players needed</p>
|
||
|
||
<!-- Partnership preview -->
|
||
<div class="waiting-seats" id="waiting-seats">
|
||
<div class="seat-slot" data-seat="0"><span class="seat-num">1</span><span class="seat-name">—</span></div>
|
||
<div class="seat-slot partner-a" data-seat="2"><span class="seat-num">2</span><span class="seat-name">—</span></div>
|
||
<div class="seat-slot partner-b" data-seat="1"><span class="seat-num">3</span><span class="seat-name">—</span></div>
|
||
<div class="seat-slot partner-b" data-seat="3"><span class="seat-num">4</span><span class="seat-name">—</span></div>
|
||
</div>
|
||
<p class="hint" style="font-size:.78rem;color:rgba(255,255,255,.4)">Seats 1&2 vs Seats 3&4</p>
|
||
<p id="waiting-status" class="waiting-status">Waiting for 3 more players…</p>
|
||
<div id="waiting-options" class="waiting-options hidden">
|
||
<span class="waiting-opt-label" id="waiting-mode-label"></span>
|
||
<span class="waiting-opt-label" id="waiting-score-label"></span>
|
||
</div>
|
||
<div id="waiting-creator-controls" class="waiting-creator-controls hidden">
|
||
<button id="btn-fill-bots" class="btn-fill-bots">🤖 Fill with bots</button>
|
||
<button id="btn-start-game" class="btn-start-game">▶ Start Game</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ══════════════ GAME TABLE ══════════════ -->
|
||
<div id="screen-game" class="screen">
|
||
|
||
<!-- Info bar -->
|
||
<div id="info-bar">
|
||
<button id="btn-leave-game" class="btn-leave-screen">← Menu</button>
|
||
|
||
<!-- Team score block -->
|
||
<div id="score-block">
|
||
<div id="team-score-0" class="team-score"></div>
|
||
<div id="team-score-1" class="team-score"></div>
|
||
</div>
|
||
|
||
<!-- Trump + bid indicator -->
|
||
<div id="game-meta">
|
||
<span id="trump-display" class="trump-display hidden"></span>
|
||
<span id="bid-display" class="bid-display hidden"></span>
|
||
</div>
|
||
|
||
<div class="game-menu-wrap">
|
||
<button id="btn-game-menu" class="btn-leave-screen" title="Options">☰</button>
|
||
<div id="game-menu-dropdown" class="game-menu-dropdown hidden">
|
||
<button id="btn-refresh-game" class="game-menu-item">↺ Reload</button>
|
||
<button id="btn-toggle-bar" class="game-menu-item">⬇ Bar to bottom</button>
|
||
<button id="btn-exit-game" class="game-menu-item game-menu-exit">🚪 Exit</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Spectator banner -->
|
||
<div id="spectator-banner" class="spectator-banner hidden">👁 Spectating — watching only</div>
|
||
|
||
<!-- AFK banner -->
|
||
<div id="afk-banner" class="afk-banner hidden">
|
||
<span id="afk-banner-msg"></span>
|
||
<button id="afk-vote-btn" class="btn-text" style="color:var(--gold);font-weight:700">Let AI Play</button>
|
||
<button id="afk-dismiss-btn" class="btn-text">Dismiss</button>
|
||
</div>
|
||
|
||
<!-- AI control banner -->
|
||
<div id="ai-control-banner" class="ai-control-banner hidden">
|
||
<span id="ai-control-msg"></span>
|
||
</div>
|
||
|
||
<!-- Table grid -->
|
||
<div id="table-grid">
|
||
|
||
<!-- Top player (seat 2, partner of seat 0) -->
|
||
<div id="area-top" class="player-area area-top">
|
||
<div class="player-label">
|
||
<span id="top-name">—</span>
|
||
<span id="top-turn" class="turn-dot hidden">●</span>
|
||
<span id="top-partner" class="partner-badge">partner</span>
|
||
<span id="top-score" class="player-score-badge"></span>
|
||
<span id="top-count" class="card-count-badge"></span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Left player (seat 1) -->
|
||
<div id="area-left" class="player-area area-left">
|
||
<div class="player-label vertical">
|
||
<span id="left-name">—</span>
|
||
<span id="left-turn" class="turn-dot hidden">●</span>
|
||
<span id="left-score" class="player-score-badge"></span>
|
||
<span id="left-count" class="card-count-badge"></span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Center trick area -->
|
||
<div id="trick-area">
|
||
<div id="trick-top" class="trick-slot"></div>
|
||
<div id="trick-middle" class="trick-middle-row">
|
||
<div id="trick-left" class="trick-slot"></div>
|
||
<div id="trick-center" class="trick-center-info">
|
||
<div id="phase-msg" class="phase-msg"></div>
|
||
</div>
|
||
<div id="trick-right" class="trick-slot"></div>
|
||
</div>
|
||
<div id="trick-bottom" class="trick-slot"></div>
|
||
</div>
|
||
|
||
<!-- Right player (seat 3) -->
|
||
<div id="area-right" class="player-area area-right">
|
||
<div class="player-label vertical">
|
||
<span id="right-name">—</span>
|
||
<span id="right-turn" class="turn-dot hidden">●</span>
|
||
<span id="right-score" class="player-score-badge"></span>
|
||
<span id="right-count" class="card-count-badge"></span>
|
||
</div>
|
||
</div>
|
||
|
||
</div><!-- /table-grid -->
|
||
|
||
<!-- Bottom: my hand -->
|
||
<div id="my-area">
|
||
<div id="my-label">
|
||
<span id="my-name">You</span>
|
||
<span id="my-turn" class="turn-dot hidden">●</span>
|
||
<span id="my-partner-label" class="partner-badge">partner</span>
|
||
<span id="my-score" class="player-score-badge"></span>
|
||
<span id="my-tricks" class="tricks-badge hidden"></span>
|
||
<button id="btn-play-mode" class="btn-play-mode hidden" title="Switch play mode">👆 Tap</button>
|
||
<button id="btn-hand-mode" class="btn-hand-mode" title="Switch hand display">📜 Scroll</button>
|
||
</div>
|
||
<div id="my-hand" class="my-hand"></div>
|
||
</div>
|
||
|
||
</div><!-- /screen-game -->
|
||
|
||
<!-- ══════════════ BIDDING OVERLAY ══════════════ -->
|
||
<div id="overlay-bid" class="overlay hidden">
|
||
<div class="overlay-box bid-box">
|
||
<h3>Bidding</h3>
|
||
<div id="bid-history" class="bid-history"></div>
|
||
<div id="bid-controls" class="hidden">
|
||
<p class="bid-hint">Your turn — enter your bid or pass</p>
|
||
<div class="bid-input-row">
|
||
<button id="btn-bid-minus" class="bid-adj">−5</button>
|
||
<span id="bid-amount-display" class="bid-amount-display">85</span>
|
||
<button id="btn-bid-plus" class="bid-adj">+5</button>
|
||
</div>
|
||
<div class="bid-action-row">
|
||
<button id="btn-do-bid" class="btn-primary">Bid</button>
|
||
<button id="btn-do-pass" class="btn-secondary">Pass</button>
|
||
</div>
|
||
</div>
|
||
<p id="bid-waiting-msg" class="hint" style="min-height:20px"></p>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ══════════════ WIDOW / DISCARD OVERLAY ══════════════ -->
|
||
<div id="overlay-widow" class="overlay hidden">
|
||
<div class="overlay-box widow-box">
|
||
<h3 id="widow-title">Pick up widow — select cards to discard</h3>
|
||
<p id="widow-hint" class="pass-hint"></p>
|
||
<div id="widow-hand" class="pass-hand"></div>
|
||
<div class="pass-selected-row">
|
||
<span class="pass-selected-label">Discarding:</span>
|
||
<div id="widow-selected-preview" class="pass-selected-preview"></div>
|
||
</div>
|
||
<button id="btn-confirm-discard" class="btn-primary" disabled>Confirm Discard</button>
|
||
<p id="widow-waiting" class="hint" style="min-height:18px"></p>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ══════════════ TRUMP DECLARE OVERLAY ══════════════ -->
|
||
<div id="overlay-trump" class="overlay hidden">
|
||
<div class="overlay-box trump-box">
|
||
<div id="trump-declare-inner">
|
||
<h3>Choose Trump Suit</h3>
|
||
<p class="hint">You are the declarer — pick your trump</p>
|
||
<div class="trump-suit-row">
|
||
<button class="trump-suit-btn" data-suit="C">♣ Clubs</button>
|
||
<button class="trump-suit-btn" data-suit="D">♦ Diamonds</button>
|
||
<button class="trump-suit-btn" data-suit="H">♥ Hearts</button>
|
||
<button class="trump-suit-btn" data-suit="S">♠ Spades</button>
|
||
</div>
|
||
</div>
|
||
<div id="trump-waiting-inner" class="hidden">
|
||
<div class="result-icon" style="font-size:2rem">⏳</div>
|
||
<p id="trump-waiting-msg" class="hint"></p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ══════════════ HAND RESULT OVERLAY ══════════════ -->
|
||
<div id="overlay-hand" class="overlay hidden">
|
||
<div class="overlay-box">
|
||
<div id="hand-result-icon" class="result-icon"></div>
|
||
<h3 id="hand-result-title"></h3>
|
||
<p id="hand-result-detail"></p>
|
||
<div id="hand-result-scores" class="result-scores"></div>
|
||
<p class="hint">Next hand starting…</p>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ══════════════ GAME OVER OVERLAY ══════════════ -->
|
||
<div id="overlay-gameover" class="overlay hidden">
|
||
<div class="overlay-box">
|
||
<div class="result-icon big">🏆</div>
|
||
<h2 id="gameover-title"></h2>
|
||
<div id="gameover-scores" class="gameover-scores"></div>
|
||
<button id="btn-new-game" class="btn-primary">New Game</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ══════════════ AUTH MODAL ══════════════ -->
|
||
<div id="overlay-auth" class="overlay hidden">
|
||
<div class="overlay-box auth-box">
|
||
<button id="btn-auth-close" class="btn-close">✕</button>
|
||
<div class="auth-tabs">
|
||
<button class="auth-tab active" data-auth-tab="login">Login</button>
|
||
<button class="auth-tab" data-auth-tab="register">Register</button>
|
||
</div>
|
||
|
||
<div id="auth-panel-login" class="auth-panel active">
|
||
<div class="field">
|
||
<label>Username</label>
|
||
<input id="auth-login-user" type="text" maxlength="16" placeholder="Username" autocomplete="username">
|
||
</div>
|
||
<div class="field">
|
||
<label>Password</label>
|
||
<input id="auth-login-pass" type="password" placeholder="Password" autocomplete="current-password">
|
||
</div>
|
||
<p class="hint" style="font-size:.8rem;color:rgba(255,255,255,.45)">Hokm accounts work here too</p>
|
||
<button id="btn-do-login" class="btn-primary">Login</button>
|
||
<p id="auth-login-error" class="error-msg"></p>
|
||
</div>
|
||
|
||
<div id="auth-panel-register" class="auth-panel">
|
||
<div class="field">
|
||
<label>Username <small>(2–16 chars)</small></label>
|
||
<input id="auth-reg-user" type="text" maxlength="16" placeholder="Choose a username" autocomplete="username">
|
||
</div>
|
||
<div class="field">
|
||
<label>Password <small>(min 4 chars)</small></label>
|
||
<input id="auth-reg-pass" type="password" placeholder="Choose a password" autocomplete="new-password">
|
||
</div>
|
||
<button id="btn-do-register" class="btn-primary">Create Account</button>
|
||
<p id="auth-reg-error" class="error-msg"></p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ══════════════ PROFILE ══════════════ -->
|
||
<div id="screen-profile" class="screen">
|
||
<div class="profile-wrap">
|
||
<div class="profile-box">
|
||
<button id="btn-profile-back" class="btn-back">← Back</button>
|
||
<div class="profile-avatar">🃏</div>
|
||
<h2 id="profile-username" class="profile-name"></h2>
|
||
<div class="stat-grid">
|
||
<div class="stat-card">
|
||
<span class="stat-num" id="stat-games-played">—</span>
|
||
<span class="stat-label">Games Played</span>
|
||
</div>
|
||
<div class="stat-card">
|
||
<span class="stat-num" id="stat-games-won">—</span>
|
||
<span class="stat-label">Games Won</span>
|
||
</div>
|
||
<div class="stat-card">
|
||
<span class="stat-num" id="stat-shelem">—</span>
|
||
<span class="stat-label">Shelems 🃏</span>
|
||
</div>
|
||
<div class="stat-card">
|
||
<span class="stat-num" id="stat-total-score">—</span>
|
||
<span class="stat-label">Total Score</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="points-legend">
|
||
<h4>Scoring at a glance</h4>
|
||
<ul>
|
||
<li><strong>Ace / 10</strong> — 10 pts each</li>
|
||
<li><strong>Five</strong> — 5 pts each</li>
|
||
<li><strong>Color Joker</strong> — 20 pts (trump)</li>
|
||
<li><strong>Black Joker</strong> — 15 pts (trump)</li>
|
||
<li><strong>Each trick won</strong> — 5 pts</li>
|
||
<li><strong>Shelem</strong> — win every trick for 250 pts</li>
|
||
<li>Default win score: 505 pts</li>
|
||
</ul>
|
||
</div>
|
||
|
||
<div class="profile-section">
|
||
<button id="btn-show-change-pass" class="btn-secondary" style="width:100%">🔑 Change Password</button>
|
||
<div id="change-pass-form" class="hidden" style="margin-top:10px;display:flex;flex-direction:column;gap:8px">
|
||
<div class="field">
|
||
<label>Current Password</label>
|
||
<input id="change-pass-current" type="password" placeholder="Current password" autocomplete="current-password">
|
||
</div>
|
||
<div class="field">
|
||
<label>New Password <small>(min 4 chars)</small></label>
|
||
<input id="change-pass-new" type="password" placeholder="New password" autocomplete="new-password">
|
||
</div>
|
||
<button id="btn-do-change-pass" class="btn-primary">Update Password</button>
|
||
<p id="change-pass-msg" class="error-msg"></p>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="admin-panel" class="profile-section admin-section hidden">
|
||
<h4 class="admin-title">⚙ Admin</h4>
|
||
<div class="admin-row">
|
||
<span>New registrations</span>
|
||
<button id="btn-toggle-signups" class="btn-secondary btn-admin-toggle">…</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ══════════════ LEADERBOARD ══════════════ -->
|
||
<div id="screen-leaderboard" class="screen">
|
||
<div class="profile-wrap">
|
||
<div class="profile-box" style="max-width:540px">
|
||
<button id="btn-lb-back" class="btn-back">← Back</button>
|
||
<div class="profile-avatar">🏆</div>
|
||
<h2 class="profile-name">Leaderboard</h2>
|
||
<table class="lb-table">
|
||
<thead>
|
||
<tr>
|
||
<th>#</th><th>Player</th><th>Avg Score</th><th>W</th><th>Played</th><th>🃏</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody id="lb-body"></tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ══════════════ EXIT CONFIRM ══════════════ -->
|
||
<div id="overlay-exit-confirm" class="overlay hidden">
|
||
<div class="overlay-box small" style="text-align:center">
|
||
<h3>Leave Game?</h3>
|
||
<p style="font-size:.88rem;color:rgba(255,255,255,.65);margin:8px 0 20px">Your session will be discarded.<br>You will not be able to rejoin.</p>
|
||
<div style="display:flex;gap:10px;justify-content:center">
|
||
<button id="btn-exit-confirm-yes" class="btn-primary" style="background:rgba(200,50,50,.7);border-color:rgba(200,50,50,.9)">Leave & Exit</button>
|
||
<button id="btn-exit-confirm-no" class="btn-secondary">Cancel</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script src="/socket.io/socket.io.js"></script>
|
||
<script src="app.js"></script>
|
||
<script>
|
||
if ('serviceWorker' in navigator) navigator.serviceWorker.register('/sw.js');
|
||
</script>
|
||
</body>
|
||
</html>
|