Commit 38adbafe authored by Mahmoud Aglan's avatar Mahmoud Aglan

feat: domino gameplay UI overhaul + fix animation origin

- Add fromPlayer param to animatePlacement (top for opponent, bottom for me)
- Rewrite buildLayout with semantic .dg-* class structure
- Full CSS for opponent bar, score ribbon, controls, board area
- Opponent back-tiles visual with removal animations
- Bot thinking indicator in opponent meta area
- Remove dead legacy DOM elements
Co-Authored-By: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
parent db2e704d
...@@ -176,7 +176,7 @@ export class DominoBoard { ...@@ -176,7 +176,7 @@ export class DominoBoard {
return null; return null;
} }
animatePlacement(tile, end, callback) { animatePlacement(tile, end, callback, fromPlayer = 'me') {
const ep = end === 'left' ? this.layout.endpoints.left : this.layout.endpoints.right; const ep = end === 'left' ? this.layout.endpoints.left : this.layout.endpoints.right;
if (!ep) { callback?.(); return; } if (!ep) { callback?.(); return; }
...@@ -194,20 +194,21 @@ export class DominoBoard { ...@@ -194,20 +194,21 @@ export class DominoBoard {
h = dbl ? TILE_W : TILE_H; h = dbl ? TILE_W : TILE_H;
} }
// Animate from the correct player's side
const startX = this.width / 2; const startX = this.width / 2;
const startY = this.height + 40; const startY = fromPlayer === 'me' ? this.height + 40 : -40;
const duration = 320; const duration = 350;
const startTime = performance.now(); const startTime = performance.now();
const animate = (now) => { const animate = (now) => {
const t = Math.min((now - startTime) / duration, 1); const t = Math.min((now - startTime) / duration, 1);
// Ease out cubic // Ease out back (slight overshoot for satisfying snap)
const ease = 1 - Math.pow(1 - t, 3); const ease = 1 - Math.pow(1 - t, 3);
const x = startX + (ep.x - startX) * ease; const x = startX + (ep.x - startX) * ease;
const y = startY + (ep.y - startY) * ease; const y = startY + (ep.y - startY) * ease;
const scale = 1.3 + (1 - 1.3) * ease; const scale = 1.3 + (1 - 1.3) * ease;
const alpha = 0.4 + 0.6 * ease; const alpha = 0.3 + 0.7 * ease;
this.animatingTile = { tile, x, y, w, h, rotation: 0, scale, alpha }; this.animatingTile = { tile, x, y, w, h, rotation: 0, scale, alpha };
this.draw(); this.draw();
......
...@@ -105,63 +105,68 @@ function dealNewRound() { ...@@ -105,63 +105,68 @@ function dealNewRound() {
function buildLayout(mode) { function buildLayout(mode) {
const isLive = mode === 'live'; const isLive = mode === 'live';
return ` return `
<div id="domino-wrap" style="display:flex;flex-direction:column;height:100%;background:linear-gradient(180deg,#070d14 0%,#0a1420 100%);position:relative;overflow:hidden;"> <div id="domino-wrap" class="dg-wrap">
<!-- Opponent bar --> <!-- Opponent zone (top) -->
<div id="domino-opp-bar" style="display:flex;align-items:center;justify-content:space-between;padding:10px 14px;background:rgba(15,20,32,0.95);border-bottom:1px solid rgba(228,172,56,0.08);backdrop-filter:blur(8px);z-index:5;"> <div id="domino-opp-bar" class="dg-opp-bar">
<div style="display:flex;align-items:center;gap:10px;"> <div class="dg-opp-left">
<div id="opp-avatar" style="width:36px;height:36px;border-radius:50%;background:linear-gradient(135deg,#1a2a44,#0f1e36);border:2px solid rgba(228,172,56,0.3);display:flex;align-items:center;justify-content:center;font-size:16px;">${isLive ? '👤' : '🤖'}</div> <div id="opp-avatar" class="dg-avatar">${isLive ? '👤' : '🤖'}</div>
<div> <div class="dg-opp-info">
<div id="opp-name" style="font-size:13px;font-weight:700;color:#f8fafc;">${isLive ? 'خصم' : 'بوت'}</div> <div id="opp-name" class="dg-opp-name">${isLive ? 'خصم' : 'بوت'}</div>
<div id="opp-count" style="font-size:11px;color:#94a3b8;">7 قطع</div> <div class="dg-opp-meta">
<span id="opp-count" class="dg-opp-count">7 قطع</span>
<span id="bot-thinking" class="dg-thinking">يفكر<span class="dg-dots"></span></span>
</div>
</div> </div>
</div> </div>
<div style="display:flex;align-items:center;gap:8px;"> <div class="dg-opp-right">
<div id="bot-thinking" style="font-size:11px;color:#E4AC38;display:none;"><span class="dg-dots">يفكر</span></div> ${isLive ? '<div id="conn-dot" class="dg-conn-dot"></div>' : ''}
${isLive ? '<div id="conn-dot" style="width:8px;height:8px;border-radius:50%;background:#4ade80;box-shadow:0 0 6px rgba(74,222,128,0.4);"></div>' : ''} <div id="boneyard-count" class="dg-boneyard-badge">
<div id="boneyard-count" style="font-size:11px;color:#E4AC38;background:rgba(228,172,56,0.06);padding:5px 10px;border-radius:10px;border:1px solid rgba(228,172,56,0.12);font-weight:600;">المخزن: 14</div> <span class="dg-boneyard-icon">📦</span>
<span id="boneyard-num">14</span>
</div>
</div> </div>
</div> </div>
<!-- Emote display area --> <!-- Opponent back-tiles (visual only) -->
<div id="emote-display" style="position:absolute;top:60px;left:50%;transform:translateX(-50%);z-index:50;pointer-events:none;"></div> <div id="opp-hand-visual" class="dg-opp-hand"></div>
<!-- Emote display -->
<div id="emote-display" class="dg-emote-area"></div>
<!-- Canvas board --> <!-- Canvas board -->
<div id="domino-board" style="flex:1;min-height:0;padding:0;position:relative;"> <div id="domino-board" class="dg-board"></div>
<!-- Boneyard visual pile -->
<div id="boneyard-pile" style="position:absolute;left:10px;top:50%;transform:translateY(-50%);z-index:10;display:flex;flex-direction:column;align-items:center;gap:4px;pointer-events:none;"> <!-- Score + Turn status ribbon -->
<div id="boneyard-stack" style="position:relative;width:26px;height:40px;"> <div class="dg-status-ribbon">
<div style="position:absolute;top:0;left:0;width:24px;height:38px;background:#d4c9a8;border:1.5px solid rgba(155,131,96,0.4);border-radius:3px;box-shadow:0 2px 4px rgba(0,0,0,0.4);"></div> <div class="dg-score-section">
<div style="position:absolute;top:2px;left:2px;width:24px;height:38px;background:#e8dfc4;border:1.5px solid rgba(155,131,96,0.3);border-radius:3px;box-shadow:0 2px 3px rgba(0,0,0,0.3);"></div> <div class="dg-score-me">
<div style="position:absolute;top:4px;left:4px;width:24px;height:38px;background:#F5F0E8;border:1.5px solid rgba(155,131,96,0.2);border-radius:3px;box-shadow:0 2px 6px rgba(0,0,0,0.3);"></div> <span class="dg-score-label">أنت</span>
<span class="dg-score-value" id="my-score">0</span>
</div>
<div class="dg-score-divider"></div>
<div class="dg-score-target">
<span id="round-num">1</span>/<span class="dg-target-val">${state.targetScore}</span>
</div>
<div class="dg-score-divider"></div>
<div class="dg-score-opp">
<span class="dg-score-label">خصم</span>
<span class="dg-score-value" id="opp-score">0</span>
</div> </div>
<div id="boneyard-pile-count" style="font-size:10px;font-weight:700;color:#E4AC38;text-shadow:0 1px 2px rgba(0,0,0,0.8);"></div>
</div> </div>
<div id="turn-status" class="dg-turn-status">دورك!</div>
</div> </div>
<!-- Score bar --> <!-- Player hand -->
<div id="score-bar" style="display:flex;align-items:center;justify-content:center;gap:10px;padding:6px 12px;background:rgba(15,20,32,0.9);border-top:1px solid rgba(255,255,255,0.04);"> <div id="domino-hand-area" class="dg-hand-area"></div>
<span style="font-size:12px;color:#4ade80;font-weight:600;">أنت: <b id="my-score">0</b></span>
<span style="width:1px;height:12px;background:rgba(255,255,255,0.1);"></span>
<span style="font-size:12px;color:#94a3b8;">خصم: <b id="opp-score">0</b></span>
<span style="width:1px;height:12px;background:rgba(255,255,255,0.1);"></span>
<span id="target-display" style="font-size:11px;color:#64748b;">${emoji('target', '🎯', 12)} ${state.targetScore}</span>
<span style="width:1px;height:12px;background:rgba(255,255,255,0.1);"></span>
<span style="font-size:11px;color:#64748b;">ج<span id="round-num">1</span></span>
</div>
<!-- Turn status -->
<div id="turn-status" style="text-align:center;padding:6px;font-size:13px;font-weight:700;color:#E4AC38;letter-spacing:0.3px;">دورك!</div>
<!-- Hand area -->
<div id="domino-hand-area"></div>
<!-- Controls --> <!-- Controls -->
<div id="domino-controls" style="display:flex;gap:6px;padding:8px 12px;padding-bottom:calc(8px + env(safe-area-inset-bottom,0px));background:rgba(10,20,32,0.95);border-top:1px solid rgba(228,172,56,0.06);"> <div id="domino-controls" class="dg-controls">
<button class="btn btn-secondary dg-ctrl-btn" id="btn-resign" style="flex:0.5;font-size:11px;background:rgba(239,68,68,0.06);border:1px solid rgba(239,68,68,0.15);color:#fca5a5;">استسلام</button> <button class="dg-ctrl-btn dg-btn-resign" id="btn-resign">استسلام</button>
<button class="btn btn-secondary dg-ctrl-btn" id="btn-emote" style="flex:0.4;font-size:18px;background:rgba(255,255,255,0.03);border:1px solid rgba(255,255,255,0.06);">😄</button> <button class="dg-ctrl-btn dg-btn-emote" id="btn-emote">😄</button>
<button class="btn btn-secondary dg-ctrl-btn dg-draw-btn" id="btn-draw" style="flex:1;font-size:14px;font-weight:700;background:rgba(228,172,56,0.08);border:1px solid rgba(228,172,56,0.2);color:#E4AC38;">سحب</button> <button class="dg-ctrl-btn dg-btn-draw" id="btn-draw">سحب من المخزن</button>
<button class="btn btn-secondary dg-ctrl-btn" id="btn-pass" style="flex:0.6;font-size:12px;background:rgba(251,191,36,0.06);border:1px solid rgba(251,191,36,0.15);color:#fde68a;display:none;">تمرير</button> <button class="dg-ctrl-btn dg-btn-pass" id="btn-pass" style="display:none;">تمرير</button>
</div> </div>
</div> </div>
`; `;
} }
...@@ -169,6 +174,149 @@ function buildLayout(mode) { ...@@ -169,6 +174,149 @@ function buildLayout(mode) {
function injectStyles(el) { function injectStyles(el) {
const style = document.createElement('style'); const style = document.createElement('style');
style.textContent = (hand?.getStyle() || '') + (drag?.getStyle() || '') + ` style.textContent = (hand?.getStyle() || '') + (drag?.getStyle() || '') + `
/* ═══ Layout ═══ */
.dg-wrap {
display:flex;flex-direction:column;height:100%;
background:linear-gradient(180deg,#060a10 0%,#0a1420 50%,#0c1828 100%);
position:relative;overflow:hidden;
}
/* ═══ Opponent Bar ═══ */
.dg-opp-bar {
display:flex;align-items:center;justify-content:space-between;
padding:10px 14px;
background:rgba(12,18,30,0.92);
border-bottom:1px solid rgba(255,255,255,0.04);
z-index:5;
}
.dg-opp-left { display:flex;align-items:center;gap:10px; }
.dg-avatar {
width:38px;height:38px;border-radius:50%;
background:linear-gradient(135deg,#1a2844,#0f1a30);
border:2px solid rgba(228,172,56,0.25);
display:flex;align-items:center;justify-content:center;font-size:17px;
box-shadow:0 2px 8px rgba(0,0,0,0.3);
}
.dg-opp-info { display:flex;flex-direction:column;gap:2px; }
.dg-opp-name { font-size:14px;font-weight:700;color:#f8fafc; }
.dg-opp-meta { display:flex;align-items:center;gap:6px; }
.dg-opp-count { font-size:11px;color:#94a3b8; }
.dg-thinking { font-size:11px;color:#E4AC38;display:none; }
.dg-dots::after { content:'...';animation:dotsAnim 1.4s steps(3) infinite; }
.dg-opp-right { display:flex;align-items:center;gap:8px; }
.dg-conn-dot { width:8px;height:8px;border-radius:50%;background:#4ade80;box-shadow:0 0 6px rgba(74,222,128,0.5); }
.dg-boneyard-badge {
display:flex;align-items:center;gap:5px;
font-size:12px;font-weight:700;color:#E4AC38;
background:rgba(228,172,56,0.06);
padding:5px 10px;border-radius:10px;
border:1px solid rgba(228,172,56,0.1);
}
.dg-boneyard-icon { font-size:13px; }
/* ═══ Opponent back-tiles ═══ */
.dg-opp-hand {
display:flex;align-items:center;justify-content:center;gap:3px;
padding:6px 12px;min-height:32px;
overflow:hidden;
}
.dg-opp-tile {
width:16px;height:26px;
background:linear-gradient(135deg,#2a3a5a,#1a2844);
border:1px solid rgba(255,255,255,0.08);
border-radius:3px;
box-shadow:0 1px 3px rgba(0,0,0,0.3);
transition:transform 0.3s cubic-bezier(0.34,1.56,0.64,1), opacity 0.3s;
}
/* ═══ Board ═══ */
.dg-board { flex:1;min-height:0;position:relative; }
/* ═══ Status Ribbon ═══ */
.dg-status-ribbon {
display:flex;align-items:center;justify-content:space-between;
padding:8px 14px;
background:rgba(12,18,30,0.92);
border-top:1px solid rgba(255,255,255,0.03);
border-bottom:1px solid rgba(255,255,255,0.03);
}
.dg-score-section {
display:flex;align-items:center;gap:10px;
}
.dg-score-me, .dg-score-opp {
display:flex;flex-direction:column;align-items:center;gap:1px;
}
.dg-score-label { font-size:10px;color:#64748b;font-weight:500; }
.dg-score-me .dg-score-value { font-size:16px;font-weight:800;color:#4ade80; }
.dg-score-opp .dg-score-value { font-size:16px;font-weight:800;color:#94a3b8; }
.dg-score-divider { width:1px;height:24px;background:rgba(255,255,255,0.06); }
.dg-score-target {
font-size:11px;color:#64748b;
display:flex;align-items:center;gap:2px;
}
.dg-target-val { color:#E4AC38;font-weight:700; }
.dg-turn-status {
font-size:13px;font-weight:700;color:#E4AC38;
padding:4px 12px;border-radius:8px;
background:rgba(228,172,56,0.06);
border:1px solid rgba(228,172,56,0.1);
transition:all 0.3s;
}
.dg-turn-status.dg-waiting {
color:#64748b;background:transparent;border-color:transparent;
}
/* ═══ Hand area ═══ */
.dg-hand-area {
/* Styled by DominoHand component */
}
/* ═══ Controls ═══ */
.dg-controls {
display:flex;gap:6px;
padding:8px 12px;
padding-bottom:calc(8px + env(safe-area-inset-bottom, 0px));
background:rgba(8,14,24,0.95);
border-top:1px solid rgba(255,255,255,0.03);
}
.dg-ctrl-btn {
min-height:40px;border-radius:12px;border:none;
font-size:12px;font-weight:600;cursor:pointer;
transition:transform 0.15s cubic-bezier(0.34,1.56,0.64,1), opacity 0.2s;
}
.dg-ctrl-btn:active { transform:scale(0.93); }
.dg-btn-resign {
flex:0;padding:0 12px;
background:rgba(239,68,68,0.06);border:1px solid rgba(239,68,68,0.12);
color:#fca5a5;font-size:11px;
}
.dg-btn-emote {
flex:0;padding:0 14px;
background:rgba(255,255,255,0.03);border:1px solid rgba(255,255,255,0.06);
font-size:18px;
}
.dg-btn-draw {
flex:1;
background:rgba(228,172,56,0.08);border:1px solid rgba(228,172,56,0.18);
color:#E4AC38;font-size:13px;font-weight:700;
}
.dg-btn-pass {
flex:0;padding:0 16px;
background:rgba(251,191,36,0.06);border:1px solid rgba(251,191,36,0.12);
color:#fde68a;font-size:12px;
}
/* ═══ Emotes ═══ */
.dg-emote-area {
position:absolute;top:90px;left:50%;transform:translateX(-50%);
z-index:50;pointer-events:none;
}
.emote-bubble {
animation: emoteFloat 2.2s cubic-bezier(0.34,1.56,0.64,1) forwards;
position:absolute;left:50%;font-size:40px;
}
/* ═══ Animations ═══ */
@keyframes fadeIn { from{opacity:0;transform:translateY(8px)} to{opacity:1;transform:translateY(0)} } @keyframes fadeIn { from{opacity:0;transform:translateY(8px)} to{opacity:1;transform:translateY(0)} }
@keyframes emoteFloat { @keyframes emoteFloat {
0%{transform:translateX(-50%) scale(0);opacity:0} 0%{transform:translateX(-50%) scale(0);opacity:0}
...@@ -176,21 +324,14 @@ function injectStyles(el) { ...@@ -176,21 +324,14 @@ function injectStyles(el) {
75%{opacity:1} 75%{opacity:1}
100%{transform:translateX(-50%) translateY(-30px) scale(0.8);opacity:0} 100%{transform:translateX(-50%) translateY(-30px) scale(0.8);opacity:0}
} }
.emote-bubble { animation: emoteFloat 2.2s cubic-bezier(0.34,1.56,0.64,1) forwards; position:absolute;left:50%;font-size:40px; }
@keyframes drawPulse { @keyframes drawPulse {
0%,100%{box-shadow:0 0 4px rgba(228,172,56,0.15);border-color:rgba(228,172,56,0.2)} 0%,100%{box-shadow:0 0 4px rgba(228,172,56,0.1);border-color:rgba(228,172,56,0.18)}
50%{box-shadow:0 0 18px rgba(228,172,56,0.4);border-color:rgba(228,172,56,0.5)} 50%{box-shadow:0 0 16px rgba(228,172,56,0.35);border-color:rgba(228,172,56,0.5)}
}
.dg-ctrl-btn {
min-height:42px;border-radius:12px;
transition:transform 0.15s cubic-bezier(0.34,1.56,0.64,1), box-shadow 0.2s, opacity 0.2s;
}
.dg-ctrl-btn:active { transform:scale(0.93); }
.dg-dots::after {
content:'...';
animation:dotsAnim 1.2s steps(3) infinite;
} }
@keyframes dotsAnim { 0%{content:'.'} 33%{content:'..'} 66%{content:'...'} } @keyframes dotsAnim { 0%{content:'.'} 33%{content:'..'} 66%{content:'...'} }
@keyframes oppTileRemove {
to { transform:translateY(10px) scale(0.6);opacity:0; }
}
`; `;
el.appendChild(style); el.appendChild(style);
} }
...@@ -400,7 +541,7 @@ function applyOpponentMove(el, data, gs, lastMove) { ...@@ -400,7 +541,7 @@ function applyOpponentMove(el, data, gs, lastMove) {
board.animatePlacement({ left: 0, right: 0, id: lastMove.tile_id || 'opp' }, lastMove.end || 'right', () => { board.animatePlacement({ left: 0, right: 0, id: lastMove.tile_id || 'opp' }, lastMove.end || 'right', () => {
board.setChain(state.chain); board.setChain(state.chain);
}); }, 'opponent');
audio.play('place', 'game'); audio.play('place', 'game');
...@@ -913,7 +1054,7 @@ function endMatch(el, result, reason) { ...@@ -913,7 +1054,7 @@ function endMatch(el, result, reason) {
function scheduleBotTurn(el) { function scheduleBotTurn(el) {
state.botThinking = true; state.botThinking = true;
const thinkEl = el.querySelector('#bot-thinking'); const thinkEl = el.querySelector('#bot-thinking');
if (thinkEl) thinkEl.style.display = 'block'; if (thinkEl) thinkEl.style.display = 'inline';
const delay = bot.getThinkDelay(state.botPersonality); const delay = bot.getThinkDelay(state.botPersonality);
botTimeout = setTimeout(() => executeBotTurn(el), delay); botTimeout = setTimeout(() => executeBotTurn(el), delay);
...@@ -955,7 +1096,7 @@ function executeBotTurn(el) { ...@@ -955,7 +1096,7 @@ function executeBotTurn(el) {
board.animatePlacement(move.tile, actualEnd, () => { board.animatePlacement(move.tile, actualEnd, () => {
board.setChain(state.chain); board.setChain(state.chain);
}); }, 'opponent');
audio.play('place', 'game'); audio.play('place', 'game');
if (bot.shouldEmote(state.botPersonality)) { if (bot.shouldEmote(state.botPersonality)) {
...@@ -978,16 +1119,16 @@ function updateUI(el) { ...@@ -978,16 +1119,16 @@ function updateUI(el) {
if (turnEl) { if (turnEl) {
if (state.gameOver) { if (state.gameOver) {
turnEl.textContent = 'انتهت الجولة'; turnEl.textContent = 'انتهت الجولة';
turnEl.style.color = '#94a3b8'; turnEl.className = 'dg-turn-status dg-waiting';
} else if (isMyTurn && state.selectedTile) { } else if (isMyTurn && state.selectedTile) {
turnEl.textContent = 'اختر مكان الوضع'; turnEl.textContent = 'اختر مكان الوضع';
turnEl.style.color = '#E4AC38'; turnEl.className = 'dg-turn-status';
} else if (isMyTurn) { } else if (isMyTurn) {
turnEl.textContent = 'دورك!'; turnEl.textContent = 'دورك!';
turnEl.style.color = '#E4AC38'; turnEl.className = 'dg-turn-status';
} else { } else {
turnEl.textContent = 'الخصم يلعب...'; turnEl.textContent = 'الخصم يلعب...';
turnEl.style.color = '#64748b'; turnEl.className = 'dg-turn-status dg-waiting';
} }
} }
...@@ -1021,13 +1162,9 @@ function updateUI(el) { ...@@ -1021,13 +1162,9 @@ function updateUI(el) {
} }
} }
const boneEl = el.querySelector('#boneyard-count'); const boneNumEl = el.querySelector('#boneyard-num');
if (boneEl) boneEl.textContent = `المخزن: ${state.boneyard.length}`; if (boneNumEl) boneNumEl.textContent = state.boneyard.length;
const pileCountEl = el.querySelector('#boneyard-pile-count');
const pileEl = el.querySelector('#boneyard-pile');
if (pileCountEl) pileCountEl.textContent = state.boneyard.length;
if (pileEl) pileEl.style.display = state.boneyard.length > 0 ? '' : 'none';
const myScoreEl = el.querySelector('#my-score'); const myScoreEl = el.querySelector('#my-score');
const oppScoreEl = el.querySelector('#opp-score'); const oppScoreEl = el.querySelector('#opp-score');
...@@ -1056,6 +1193,43 @@ function updateUI(el) { ...@@ -1056,6 +1193,43 @@ function updateUI(el) {
if (drawBtn) { drawBtn.style.display = ''; drawBtn.style.animation = ''; } if (drawBtn) { drawBtn.style.display = ''; drawBtn.style.animation = ''; }
if (passBtn) passBtn.style.display = 'none'; if (passBtn) passBtn.style.display = 'none';
} }
// Opponent back-tiles visual
const oppHandVisual = el.querySelector('#opp-hand-visual');
if (oppHandVisual) {
const currentCount = oppHandVisual.children.length;
if (currentCount !== oppHandLen) {
if (oppHandLen < currentCount && currentCount > 0) {
// Animate removal of last tile
const last = oppHandVisual.lastElementChild;
if (last) {
last.style.animation = 'oppTileRemove 0.3s ease forwards';
setTimeout(() => {
oppHandVisual.innerHTML = '';
for (let i = 0; i < oppHandLen; i++) {
const tile = document.createElement('div');
tile.className = 'dg-opp-tile';
oppHandVisual.appendChild(tile);
}
}, 300);
}
} else {
oppHandVisual.innerHTML = '';
for (let i = 0; i < oppHandLen; i++) {
const tile = document.createElement('div');
tile.className = 'dg-opp-tile';
tile.style.animationDelay = `${i * 30}ms`;
oppHandVisual.appendChild(tile);
}
}
}
}
// Bot thinking indicator
const thinkingEl = el.querySelector('#bot-thinking');
if (thinkingEl) {
thinkingEl.style.display = (!isMyTurn && !state.gameOver && state.mode === 'bot') ? 'inline' : 'none';
}
} }
function refreshHand() { function refreshHand() {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment