Files
Poker/public/assets/lobby.js
T
agomizi1cmandClaude Opus 5.5 66907345ff Add optional card mode: server deals cards and settles showdowns automatically
- New "カードを配る" switch on the create-table screen (default off); the lobby
  shows "カードあり" for tables that use it
- Card mode (server/cards.js restored): crypto-shuffled deck kept server-side,
  hole cards sent only to their owner until showdown/all-in reveal, board dealt
  per street with burns, all-in runout on a timer (frozen while paused)
- Showdown is evaluated and every pot (incl. side pots, split pots) is paid out
  automatically; host winner selection is only used when card mode is off
- Play UI: board, hole cards, own cards in the action bar, hand names, winning
  cards highlighted; layout tuned for 7+ seats and short screens
- Playing-card styles use .pcard so they don't clash with the existing .card panel
- Tests for hand evaluation, card privacy, runout, side pots, ties and random play

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 16:44:22 +09:00

79 lines
2.9 KiB
JavaScript

(function () {
'use strict';
const { Store, esc, chips } = App;
const nameInput = document.getElementById('name');
const nameError = document.getElementById('name-error');
const listEl = document.getElementById('list');
const countEl = document.getElementById('count');
nameInput.value = Store.name;
nameInput.addEventListener('input', () => {
Store.name = nameInput.value.trim();
nameInput.classList.remove('invalid');
nameError.textContent = '';
});
function requireName() {
if (Store.name) return true;
nameInput.classList.add('invalid');
nameError.textContent = '先に名前を入力してください。';
nameInput.focus();
return false;
}
// 管理者だけにハンドレンジ学習へのリンクを出す(ページ自体もサーバ側で管理者以外は 404)
App.api('GET', '/api/me')
.then((me) => { document.getElementById('range-link').hidden = !me.admin; })
.catch(() => {});
document.getElementById('create').addEventListener('click', () => {
if (requireName()) location.href = '/create';
});
listEl.addEventListener('click', (e) => {
const btn = e.target.closest('[data-join]');
if (btn && requireName()) location.href = '/table/' + encodeURIComponent(btn.dataset.join);
});
function phaseBadge(t) {
if (t.paused) return '<span class="badge paused">PAUSED</span>';
if (t.phase === 'WAITING') return '<span class="badge">WAITING</span>';
return '<span class="badge live">PLAYING</span>';
}
function render(tables) {
countEl.textContent = tables.length ? `${tables.length} テーブル` : '';
if (!tables.length) {
listEl.innerHTML = '<div class="empty">開催中のテーブルはありません。<br>テーブルを作成して友達を招待しよう。</div>';
return;
}
listEl.innerHTML = tables
.map((t) => {
const full = t.playerCount >= t.maxSeats;
const timer = t.timerEnabled ? `⏱ ${t.intervalMin}分` : '⏱ なし';
return `
<div class="table-item">
<div class="info">
<div class="name">${esc(t.name)}${phaseBadge(t)}</div>
<div class="meta">
<span>👤 <b>${t.playerCount}/${t.maxSeats}</b></span>
<span>開始 <b>${chips(t.startingStack)}</b></span>
<span class="gold">Lv.${t.level} <b class="gold">${chips(t.smallBlind)}/${chips(t.bigBlind)}</b></span>
<span>${timer}</span>
${t.cardsEnabled ? '<span>🃏 カードあり</span>' : ''}
</div>
</div>
<button class="btn btn-blue btn-sm" data-join="${esc(t.id)}" ${full ? 'title="満席(観戦のみ)"' : ''}>
${full ? '観戦' : '参加'}
</button>
</div>`;
})
.join('');
}
const socket = App.connectSocket();
socket.on('connect', () => socket.emit('lobby:subscribe'));
socket.on('lobby:tables', render);
})();