Files
drinking-game/app.js
gongch 6d1dd5d56c Initial commit: 酒桌派对 / 大话骰 multiplayer drinking-game hub
- server.py: zero-dependency stdlib backend (HTTP + WebSocket /ws), 大话骰 rule engine, server-driven bots
- live.html: real MVP frontend wired to the backend over WebSocket
- index.html/app.js/style.css: older static prototype
- deploy/: systemd unit + Nginx reverse proxy + Let's Encrypt setup.sh
- test_*.py: standalone HTTP / in-process test scripts

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 10:05:10 +08:00

23 lines
7.5 KiB
JavaScript

const games = {
poker:{name:'德州扑克',icon:'♠️',people:'2-9人',tag:'策略',accent:'#ff4d6d',desc:'经典翻牌局,支持筹码、公共牌、喝酒惩罚。'},
dice:{name:'大话骰',icon:'🎲',people:'2-12人',tag:'酒局',accent:'#ffd166',desc:'叫点、开骰、劈/斋玩法,最适合酒桌热场。'},
six:{name:'六张牌',icon:'🃏',people:'2-8人',tag:'快节奏',accent:'#38bdf8',desc:'每人六张牌,快速比牌,节奏快、惩罚明确。'},
catch:{name:'抓二游',icon:'🧩',people:'4-8人',tag:'组队',accent:'#43e97b',desc:'抓牌、组队、对抗、结算,适合地方酒桌玩法。'}
};
let state={game:'dice',room:'A102',locked:false,history:['home']};
const $=s=>document.querySelector(s);const $$=s=>document.querySelectorAll(s);
const players=['小鱼','阿杰','Mia','老陈','可乐','Luna'];
function toast(t){const el=$('#toast');el.textContent=t;el.classList.add('show');setTimeout(()=>el.classList.remove('show'),1200)}
function show(id,push=true){$$('.screen').forEach(x=>x.classList.remove('active'));$('#'+id).classList.add('active');$$('.bottom-nav a').forEach(a=>a.classList.toggle('active',a.dataset.go===id));$('[data-back]').hidden=id==='home';$('#subtitle').textContent=id==='home'?'Drinking Game Hub':id==='room'?'房间等待中':id==='game'?'游戏进行中':id==='result'?'本局结算':'个人中心';if(push&&state.history.at(-1)!==id)state.history.push(id);window.scrollTo(0,0)}
function openModal(type){let html='';if(type==='create')html=`<p class="eyebrow">Create Room</p><h2>创建房间</h2><p class="lead">选择一个游戏,创建后邀请朋友加入。</p><div class="modal-actions">${Object.entries(games).map(([k,g])=>`<button data-create="${k}">${g.icon} ${g.name}</button>`).join('')}</div>`;if(type==='join')html=`<p class="eyebrow">Join Room</p><h2>输入房间号</h2><p class="lead">输入朋友分享的 4 位房间号。</p><input id="joinCode" value="A102" /><div class="modal-actions"><button data-join>加入房间</button></div>`;if(type==='rules')html=`<p class="eyebrow">Rules</p><h2>通用喝酒规则</h2><p class="lead">1. 房主创建房间并选择游戏。<br>2. 所有人准备后开始。<br>3. 每局结束按惩罚喝酒。<br>4. 可设置输家 1 杯、2 杯或自定义。<br>5. 理性饮酒,禁止劝酒。</p>`;$('#modalContent').innerHTML=html;$('#modal').classList.add('show')}
function closeModal(){$('#modal').classList.remove('show')}
function enterRoom(game='dice'){state.game=game;state.room=Math.random().toString(36).slice(2,6).toUpperCase();renderRoom();closeModal();show('room')}
function renderHome(){const grid=$('#gameGrid');grid.innerHTML=Object.entries(games).map(([k,g])=>`<article class="game-card" style="--accent:${g.accent}" data-create="${k}"><div class="game-icon">${g.icon}</div><div><h3>${g.name}</h3><p>${g.desc}</p></div><div class="meta"><span>${g.people}</span><span>${g.tag}</span></div></article>`).join('');$('#roomList').innerHTML=[['老友酒局','dice','5/8'],['KTV 第三局','poker','6/9'],['六张牌快局','six','4/6']].map(r=>`<article class="room-card"><div><h3>${r[0]} · ${games[r[1]].name}</h3><p>房主:阿杰 · ${r[2]} 人 · 等待中</p></div><button data-room="${r[1]}">加入</button></article>`).join('')}
function renderRoom(){const g=games[state.game];$('#roomCode').textContent=state.room;$('#roomTitle').textContent=g.name+'房间';$('#roomDesc').textContent=g.desc+' 等待玩家加入,房主可调整规则并开始游戏。';$('#playerCount').textContent=players.length+'/'+g.people.split('-').pop();$('#players').innerHTML=players.map((p,i)=>`<div class="player"><div class="avatar">${i===0?'👑':'🙂'}</div><strong>${p}</strong><span>${i===0?'房主':'已准备'}</span></div>`).join('')}
function startGame(){const g=games[state.game];$('#gameTitle').textContent=g.name;$('#gameMode').textContent=state.room+' · Round 03';if(state.game==='dice'){$('#gameHint').textContent='轮到阿杰叫点,小鱼可以选择跟、加码或开骰。';$('#gameBoard').innerHTML=`<div class="board"><div class="call-box"><p>当前叫点</p><strong>5 个 4</strong><p class="lead">上一手:阿杰</p></div><div class="dice-row"><div class="die">⚀</div><div class="die">⚂</div><div class="die">⚃</div><div class="die">⚅</div><div class="die">⚁</div></div></div>`;$('#gameActions').innerHTML=`<button>加码</button><button data-action="finish">开骰</button>`}
else if(state.game==='poker'){$('#gameHint').textContent='翻牌圈,当前底池 18 杯,轮到小鱼操作。';$('#gameBoard').innerHTML=`<div class="board"><p class="eyebrow">公共牌</p><div class="card-row"><div class="play-card red">A♥</div><div class="play-card">K♠</div><div class="play-card red">9♦</div></div><p class="eyebrow">你的手牌</p><div class="card-row"><div class="play-card">Q♣</div><div class="play-card red">Q♥</div></div></div>`;$('#gameActions').innerHTML=`<button>跟注</button><button>加注</button><button class="ghost-btn">弃牌</button><button data-action="finish">结算</button>`}
else if(state.game==='six'){$('#gameHint').textContent='六张牌亮牌阶段,比较最大组合。';$('#gameBoard').innerHTML=`<div class="board"><p class="eyebrow">你的六张牌</p><div class="card-row"><div class="play-card red">A♥</div><div class="play-card">A♠</div><div class="play-card red">10♦</div><div class="play-card">8♣</div><div class="play-card red">6♥</div><div class="play-card">3♠</div></div></div>`;$('#gameActions').innerHTML=`<button>换一张</button><button data-action="finish">亮牌</button>`}
else {$('#gameHint').textContent='抓二游组队完成,开始出牌对抗。';$('#gameBoard').innerHTML=`<div class="board"><div class="call-box"><p>当前队伍</p><strong>小鱼 + Mia</strong><p class="lead">对阵 阿杰 + 老陈</p></div><div class="card-row"><div class="play-card red">2♥</div><div class="play-card">J♠</div><div class="play-card red">7♦</div><div class="play-card">5♣</div></div></div>`;$('#gameActions').innerHTML=`<button>出牌</button><button>不要</button><button data-action="finish">结算</button>`}show('game')}
function finish(){const loser=['阿杰','老陈','Mia','可乐'][Math.floor(Math.random()*4)];$('#resultTitle').textContent=$('#nickname').value||'小鱼'+'获胜';$('#resultDesc').textContent=`${loser} 本局失败,按规则喝 1 杯。`;$('#punishText').textContent=`${loser} 喝 1 杯`;show('result')}
document.addEventListener('click',e=>{const t=e.target.closest('[data-open],[data-close],[data-create],[data-room],[data-go],[data-action],[data-back],[data-join]');if(!t)return;if(t.dataset.open)openModal(t.dataset.open);if(t.dataset.close!==undefined)closeModal();if(t.dataset.create)enterRoom(t.dataset.create);if(t.dataset.room){state.game=t.dataset.room;renderRoom();show('room')}if(t.dataset.go){if(t.dataset.go==='room')renderRoom();show(t.dataset.go)}if(t.dataset.back!==undefined){state.history.pop();show(state.history.pop()||'home')}if(t.dataset.join!==undefined){state.room=$('#joinCode').value||'A102';renderRoom();closeModal();show('room')}if(t.dataset.action==='enter')toast('欢迎进入大厅');if(t.dataset.action==='randomJoin'){state.game='dice';renderRoom();show('room')}if(t.dataset.action==='startGame')startGame();if(t.dataset.action==='finish')finish();if(t.dataset.action==='again')startGame();if(t.dataset.action==='copyInvite')toast('邀请链接已复制');if(t.dataset.action==='toggleLock'){state.locked=!state.locked;toast(state.locked?'已暂停加入':'已开放加入')}if(t.dataset.action==='toast')toast('房间列表已刷新')});
renderHome();renderRoom();