Commit 23f9266a authored by Mahmoud Aglan's avatar Mahmoud Aglan

feat: player-centric panels — photos, names, levels in ALL games

Ludo:
- Each panel shows: avatar (photo/bot🤖), name, level
- YOUR panel shows your profile photo + display_name + level
- Bot panels show 🤖 emoji with colored border matching their zone
- Panel positions already match zone colors (Green TL, Yellow TR, Red BL, Blue BR)

Chess:
- Opponent bar: photo (bot portrait for bots, profile pic for humans) + name + level
- YOUR bar: your profile photo + display_name + Lv.X
- Gold border around your avatar, blue border around opponent's
- Live mode: fetches opponent profile → updates avatar, name, level dynamically
- Bot mode: shows Stockfish portrait + bot name + 'بوت' label

Both games now feel player-centric — you always see WHO you're playing against
with their real identity, not generic 'Opponent' or 'Bot 1' text.
Co-Authored-By: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
parent 317f8d20
......@@ -40,12 +40,15 @@ export function mountGame(el, params) {
<!-- Opponent Bar -->
<div class="chess-bar" style="display:flex;align-items:center;justify-content:space-between;padding:6px 12px;background:#0f0f1e;">
<div style="display:flex;align-items:center;gap:8px;">
<div style="width:32px;height:32px;border-radius:50%;background:#2a2a4a;display:flex;align-items:center;justify-content:center;overflow:hidden;">
<img src="https://stockfishapi.caprover.al-arcade.com/portraits/${botId || 'amina'}.png" style="width:100%;height:100%;object-fit:cover;" onerror="this.style.display='none';this.parentNode.innerHTML='${emoji('robot', '🤖', 14).replace(/'/g, "\\'")}'">
<div id="opponent-avatar" style="width:34px;height:34px;border-radius:50%;background:#2a2a4a;display:flex;align-items:center;justify-content:center;overflow:hidden;border:2px solid ${mode === 'bot' ? '#64748b' : '#3B82F6'};">
${mode === 'bot' ? `<img src="https://stockfishapi.caprover.al-arcade.com/portraits/${botId || 'amina'}.png" style="width:100%;height:100%;object-fit:cover;" onerror="this.style.display='none';this.parentNode.textContent='🤖'">` : '<span style="font-size:16px;">👤</span>'}
</div>
<div>
<div style="font-size:13px;font-weight:600;color:#f8fafc;" id="opponent-name">${mode === 'bot' ? (botId || 'Bot') : 'Opponent'}</div>
<div id="opponent-captured" style="font-size:11px;color:#94a3b8;min-height:14px;letter-spacing:1px;"></div>
<div style="font-size:13px;font-weight:600;color:#f8fafc;" id="opponent-name">${mode === 'bot' ? (botId || 'Bot') : 'جاري التحميل...'}</div>
<div style="display:flex;gap:6px;align-items:center;">
<div id="opponent-level" style="font-size:10px;color:#64748b;">${mode === 'bot' ? 'بوت' : ''}</div>
<div id="opponent-captured" style="font-size:11px;color:#94a3b8;letter-spacing:1px;"></div>
</div>
</div>
</div>
<div id="clock-opponent" class="chess-clock" style="font-size:18px;font-weight:700;font-family:Inter,monospace;background:#1e1e3a;padding:4px 12px;border-radius:6px;color:#f8fafc;min-width:60px;text-align:center;">${clock.format(tc.time)}</div>
......@@ -62,10 +65,15 @@ export function mountGame(el, params) {
<!-- Player Bar -->
<div class="chess-bar" style="display:flex;align-items:center;justify-content:space-between;padding:6px 12px;background:#0f0f1e;">
<div style="display:flex;align-items:center;gap:8px;">
<div style="width:32px;height:32px;border-radius:50%;background:#2a2a4a;display:flex;align-items:center;justify-content:center;font-size:14px;color:#f8fafc;">${emoji('person', '👤', 14)}</div>
<div style="width:34px;height:34px;border-radius:50%;background:#2a2a4a;display:flex;align-items:center;justify-content:center;overflow:hidden;border:2px solid var(--gold);">
${store.get('player.avatar_url') ? `<img src="${store.get('player.avatar_url')}" style="width:100%;height:100%;object-fit:cover;">` : `<span style="font-size:16px;">👤</span>`}
</div>
<div>
<div style="font-size:13px;font-weight:600;color:#f8fafc;">${store.get('player.username') || store.get('player.display_name') || 'You'}</div>
<div id="player-captured" style="font-size:11px;color:#94a3b8;min-height:14px;letter-spacing:1px;"></div>
<div style="font-size:13px;font-weight:600;color:#f8fafc;">${store.get('player.display_name') || store.get('player.username') || 'أنت'}</div>
<div style="display:flex;gap:6px;align-items:center;">
<span style="font-size:10px;color:#64748b;">Lv.${store.get('player.level') || 1}</span>
<div id="player-captured" style="font-size:11px;color:#94a3b8;letter-spacing:1px;"></div>
</div>
</div>
</div>
<div id="clock-player" class="chess-clock" style="font-size:18px;font-weight:700;font-family:Inter,monospace;background:#1e1e3a;padding:4px 12px;border-radius:6px;color:#f8fafc;min-width:60px;text-align:center;">${clock.format(tc.time)}</div>
......@@ -205,15 +213,18 @@ export function mountGame(el, params) {
clock.start('w');
}
// Fetch and render opponent profile bar
// Fetch and render opponent profile bar (photo, name, level)
const opponentId = params.opponentId || (playerColor === 'w' ? params.blackPlayerId : params.whitePlayerId);
if (opponentId) {
mp.fetchOpponentProfile(opponentId).then(opp => {
if (opp && !opp.error) {
const oppBar = el.querySelector('.chess-bar');
if (oppBar) {
const nameEl = oppBar.querySelector('#opponent-name');
if (nameEl) nameEl.textContent = opp.display_name || opp.username || 'خصم';
const nameEl = el.querySelector('#opponent-name');
const levelEl = el.querySelector('#opponent-level');
const avatarEl = el.querySelector('#opponent-avatar');
if (nameEl) nameEl.textContent = opp.display_name || opp.username || 'خصم';
if (levelEl) levelEl.textContent = `Lv.${opp.level || 1}`;
if (avatarEl && opp.avatar_url) {
avatarEl.innerHTML = `<img src="${opp.avatar_url}" style="width:100%;height:100%;object-fit:cover;border-radius:50%;">`;
}
}
});
......
......@@ -25,6 +25,20 @@ let myPlayerIndex = 0;
let matchId = null;
let isHost = false;
function renderPanel(p) {
return `
<div class="pp" id="pp-${p.i}" style="--pc:${p.color};">
<div class="pp-avatar" style="width:28px;height:28px;border-radius:50%;background:#2a2a4a;border:2px solid ${p.color};display:flex;align-items:center;justify-content:center;font-size:14px;overflow:hidden;">
${p.avatar}
</div>
<div style="display:flex;flex-direction:column;">
<span style="font-size:11px;font-weight:600;color:#f8fafc;">${p.name}</span>
${p.level ? `<span style="font-size:9px;color:#64748b;">${p.level}</span>` : ''}
</div>
</div>
`;
}
export function mountGame(el, params) {
const { mode = 'bot', numPlayers = 4 } = params;
scene.enterGameMode();
......@@ -49,16 +63,26 @@ export function mountGame(el, params) {
diceAnimating = false;
livePoller = null;
const player = store.get('player') || {};
const panels = [0,1,2,3].map(i => {
const isMe = i === myPlayerIndex;
const isBot = PLAYER_NAMES[i].startsWith('Bot');
const avatar = isMe && player.avatar_url ? `<img src="${player.avatar_url}" style="width:100%;height:100%;object-fit:cover;border-radius:50%;">` : isBot ? '🤖' : '👤';
const name = isMe ? (player.display_name || player.username || 'أنت') : PLAYER_NAMES[i];
const level = isMe ? `Lv.${player.level || 1}` : (isBot ? '' : '');
return { i, avatar, name, level, color: COLORS[i] };
});
el.innerHTML = `
<div style="display:flex;flex-direction:column;height:100%;background:#1a1a2e;">
<div style="display:flex;justify-content:space-between;padding:6px 12px;background:#0f0f1e;">
<div class="pp" id="pp-1" style="--pc:${COLORS[1]};"><div class="pp-dot"></div><span>${PLAYER_NAMES[1]}</span><div class="pp-dice" id="dice-1"></div></div>
<div class="pp" id="pp-2" style="--pc:${COLORS[2]};"><div class="pp-dot"></div><span>${PLAYER_NAMES[2]}</span><div class="pp-dice" id="dice-2"></div></div>
${renderPanel(panels[1])}
${renderPanel(panels[2])}
</div>
<div id="ludo-wrap" style="flex:1;display:flex;align-items:center;justify-content:center;padding:4px;min-height:0;"></div>
<div style="display:flex;justify-content:space-between;padding:6px 12px;background:#0f0f1e;">
<div class="pp" id="pp-0" style="--pc:${COLORS[0]};"><div class="pp-dot"></div><span>${PLAYER_NAMES[0]}</span><div class="pp-dice" id="dice-0"></div></div>
<div class="pp" id="pp-3" style="--pc:${COLORS[3]};"><div class="pp-dot"></div><span>${PLAYER_NAMES[3]}</span><div class="pp-dice" id="dice-3"></div></div>
${renderPanel(panels[0])}
${renderPanel(panels[3])}
</div>
<div id="dice-area" style="display:flex;align-items:center;gap:12px;padding:10px 16px;background:#0f0f1e;border-top:1px solid rgba(255,255,255,0.06);justify-content:center;">
<div id="dice-box" style="width:50px;height:50px;background:#f8fafc;border-radius:10px;display:grid;grid-template:repeat(3,1fr)/repeat(3,1fr);padding:6px;box-shadow:0 3px 10px rgba(0,0,0,0.4),inset 0 1px 0 rgba(255,255,255,0.8);transition:transform 0.15s cubic-bezier(0.34,1.56,0.64,1);">
......
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