Commit 6d23a1f2 authored by Mahmoud Aglan's avatar Mahmoud Aglan

fix: Ludo panels match board corners (RTL fix) + exit button added

Panel positions:
- Forced direction:ltr on panel rows so Green=top-left, Yellow=top-right,
  Red=bottom-left, Blue=bottom-right — matching the board zones visually.
  (The page is RTL Arabic which was reversing element order.)

Exit button:
- Added exit button (✕) next to dice area
- Single player: confirms then ends game as loss
- Multiplayer: notifies server to replace player with bot, then exits
- Confirmation dialog prevents accidental exits
Co-Authored-By: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
parent 4704435b
...@@ -75,16 +75,17 @@ export function mountGame(el, params) { ...@@ -75,16 +75,17 @@ export function mountGame(el, params) {
el.innerHTML = ` el.innerHTML = `
<div style="display:flex;flex-direction:column;height:100%;background:#1a1a2e;"> <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 style="display:flex;justify-content:space-between;padding:6px 12px;background:#0f0f1e;direction:ltr;">
${renderPanel(panels[1])} ${renderPanel(panels[1])}
${renderPanel(panels[2])} ${renderPanel(panels[2])}
</div> </div>
<div id="ludo-wrap" style="flex:1;display:flex;align-items:center;justify-content:center;padding:4px;min-height:0;"></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 style="display:flex;justify-content:space-between;padding:6px 12px;background:#0f0f1e;direction:ltr;">
${renderPanel(panels[0])} ${renderPanel(panels[0])}
${renderPanel(panels[3])} ${renderPanel(panels[3])}
</div> </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-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;">
<button class="btn btn-secondary" id="exit-btn" style="min-height:40px;padding:8px 12px;font-size:12px;color:#EF4444;">✕</button>
<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);"> <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);">
</div> </div>
<button class="btn btn-primary" id="roll-btn" style="font-size:15px;padding:12px 32px;min-height:48px;" disabled>ارمِ النرد</button> <button class="btn btn-primary" id="roll-btn" style="font-size:15px;padding:12px 32px;min-height:48px;" disabled>ارمِ النرد</button>
...@@ -118,6 +119,7 @@ export function mountGame(el, params) { ...@@ -118,6 +119,7 @@ export function mountGame(el, params) {
drawBoard(); drawBoard();
updatePanels(el); updatePanels(el);
el.querySelector('#roll-btn').addEventListener('click', () => handleRoll(el)); el.querySelector('#roll-btn').addEventListener('click', () => handleRoll(el));
el.querySelector('#exit-btn').addEventListener('click', () => handleExit(el));
// Emotes + multiplayer // Emotes + multiplayer
const emoteWrap = el.querySelector('#ludo-wrap'); const emoteWrap = el.querySelector('#ludo-wrap');
...@@ -740,10 +742,29 @@ function renderMiniDice(miniDice, value) { ...@@ -740,10 +742,29 @@ function renderMiniDice(miniDice, value) {
).join(''); ).join('');
} }
function handleExit(el) {
if (game.gameOver) return;
if (!confirm('هل تريد الخروج من المباراة؟')) return;
game.gameOver = true;
audio.play('click');
if (game.mode === 'live' && matchId) {
// Multiplayer: notify server to replace player with bot
net.post('ludo-match.php', { action: 'leave', match_id: matchId, player_index: myPlayerIndex }).catch(() => {});
mp.stopDisconnectWatch();
if (livePoller) { clearInterval(livePoller); livePoller = null; }
}
scene.exitGameMode();
scene.replace('ludo-result', { result: 'loss', resigned: true });
bus.emit('game:ended', { gameKey: 'ludo', result: 'loss', resigned: true });
}
function endGame(el) { function endGame(el) {
game.gameOver = true; game.gameOver = true;
matchLive.session.destroy(); // Clear session so homepage doesn't try to rejoin if (matchId) matchLive.session?.destroy?.();
const result = game.winners[0] === 0 ? 'win' : 'loss'; const result = game.winners[0] === myPlayerIndex ? 'win' : 'loss';
if (result === 'win') { juice.confetti(window.innerWidth/2, window.innerHeight/3, 40); juice.hapticSuccess(); audio.play('win','reward'); } if (result === 'win') { juice.confetti(window.innerWidth/2, window.innerHeight/3, 40); juice.hapticSuccess(); audio.play('win','reward'); }
else { audio.play('lose','game'); juice.hapticError(); } else { audio.play('lose','game'); juice.hapticError(); }
setTimeout(() => { scene.exitGameMode(); scene.replace('ludo-result', { result, winners: game.winners }); bus.emit('game:ended', { gameKey: 'ludo', result }); }, 1500); setTimeout(() => { scene.exitGameMode(); scene.replace('ludo-result', { result, winners: game.winners }); bus.emit('game:ended', { gameKey: 'ludo', result }); }, 1500);
......
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