Commit ef6b1d98 authored by Mahmoud Aglan's avatar Mahmoud Aglan

feat: wire ALL remaining emoji slots — dice faces, puzzle themes, particles, room icons

Every admin emoji slot now has a corresponding emoji() or getAsset() call:
- Dice faces (1-6): custom dice images replace CSS dots when uploaded
- Puzzle themes: fork, pin, castle, crown, sacrifice use emoji()
- Particle star: juice engine uses themed particle
- Bot thinking dots, book/best-move symbols in classifier
- Domino/ludo room icons, chess pawn in leaderboard tabs
Co-Authored-By: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
parent 9be847bd
// EL3AB Juice Engine — particles, shake, counters, haptics, glow, bounce
import { emoji } from './theme.js';
// ============ PARTICLES ============
const particleContainer = document.createElement('div');
......@@ -20,7 +21,7 @@ export function burst(x, y, options = {}) {
const rotation = Math.random() * 360;
if (type === 'star') {
el.textContent = '✦';
el.innerHTML = emoji('particle_star', '✦', s * 2);
el.style.cssText = `position:absolute;left:${x}px;top:${y}px;font-size:${s * 2}px;color:${color};pointer-events:none;z-index:9999;`;
} else {
el.style.cssText = `position:absolute;left:${x}px;top:${y}px;width:${s}px;height:${s}px;background:${color};border-radius:${type === 'square' ? '2px' : '50%'};pointer-events:none;z-index:9999;`;
......
// Move Classification Engine
// Classifies moves as: brilliant, great, best, good, book, inaccuracy, mistake, blunder
import { emoji } from '../../../core/theme.js';
const THRESHOLDS = {
blunder: 2.0, // Lost 2+ pawns worth of eval
......@@ -17,7 +18,7 @@ export function classifyMove(evalBefore, evalAfter, bestEval, isPlayerWhite, mov
const lossFromBest = (bestEval - evalAfter) * sign;
// Book moves (first 2 full moves = 4 half-moves)
if (moveNum <= 2) return { class: 'book', symbol: '📖', color: '#94a3b8' };
if (moveNum <= 2) return { class: 'book', symbol: emoji('book', '📖', 14), color: '#94a3b8' };
// If eval is mate-level, special handling
if (Math.abs(evalAfter) > 900) {
......@@ -40,7 +41,7 @@ export function classifyMove(evalBefore, evalAfter, bestEval, isPlayerWhite, mov
return { class: 'brilliant', symbol: '!!', color: '#06B6D4' };
}
if (lossFromBest <= THRESHOLDS.great) {
return { class: 'best', symbol: '★', color: '#10B981' };
return { class: 'best', symbol: emoji('best_move_star', '★', 14), color: '#10B981' };
}
return { class: 'great', symbol: '!', color: '#3B82F6' };
}
......
......@@ -181,7 +181,7 @@ async function analyzePosition(el, fen) {
</div>`
).join('');
linesContainer.innerHTML = `<div style="border-bottom:1px solid rgba(255,255,255,0.05);padding-bottom:4px;margin-bottom:4px;">
<div style="font-size:9px;color:#64748b;margin-bottom:2px;">📖 كتاب الافتتاحات</div>${explorerHtml}</div>
<div style="font-size:9px;color:#64748b;margin-bottom:2px;">${emoji('book', '📖', 9)} كتاب الافتتاحات</div>${explorerHtml}</div>
<div style="color:#64748b;font-size:11px;text-align:center;">جاري تحليل المحرك...</div>`;
}
......@@ -204,7 +204,7 @@ function renderAnalysisLines(el, lines, fen, explorerData) {
let explorerHtml = '';
if (explorerData) {
explorerHtml = `<div style="border-bottom:1px solid rgba(255,255,255,0.05);padding-bottom:4px;margin-bottom:4px;">
<div style="font-size:9px;color:#64748b;margin-bottom:2px;">📖 كتاب الافتتاحات</div>
<div style="font-size:9px;color:#64748b;margin-bottom:2px;">${emoji('book', '📖', 9)} كتاب الافتتاحات</div>
${explorerData.slice(0, 3).map(e =>
`<div style="display:flex;align-items:center;gap:6px;padding:1px 0;">
<span style="font-size:11px;font-weight:600;color:#f8fafc;min-width:32px;font-family:Inter,monospace;">${e.move}</span>
......
......@@ -53,7 +53,7 @@ export function mountGame(el, params) {
<!-- Board -->
<div id="board-container" style="flex:1;display:flex;align-items:center;justify-content:center;padding:2px 4px;position:relative;min-height:0;">
<div id="bot-thinking" style="display:none;position:absolute;top:8px;left:50%;transform:translateX(-50%);background:rgba(0,0,0,0.8);color:#E4AC38;padding:4px 12px;border-radius:12px;font-size:12px;font-weight:600;z-index:10;">
${t('game.thinking')} <span class="pulse">●●●</span>
${t('game.thinking')} <span class="pulse">${emoji('thinking_dots', '●●●', 12)}</span>
</div>
<div id="promo-dialog" style="display:none;position:absolute;z-index:20;background:#1e1e3a;border-radius:8px;padding:8px;box-shadow:0 8px 32px rgba(0,0,0,0.8);border:1px solid rgba(255,255,255,0.1);"></div>
</div>
......
......@@ -159,7 +159,7 @@ function renderReview(el, results, moves, playerColor) {
</div>
<!-- Opening -->
<div style="text-align:center;font-size:12px;color:#64748b;margin-bottom:12px;">📖 ${opening}</div>
<div style="text-align:center;font-size:12px;color:#64748b;margin-bottom:12px;">${emoji('book', '📖', 12)} ${opening}</div>
</div>
<!-- Move Classification Breakdown -->
......@@ -176,9 +176,9 @@ function renderReview(el, results, moves, playerColor) {
<tbody>
${renderClassRow('brilliant', '!! رائعة', '#06B6D4', whiteSummary, blackSummary, playerColor)}
${renderClassRow('great', '! ممتازة', '#3B82F6', whiteSummary, blackSummary, playerColor)}
${renderClassRow('best', '★ أفضل نقلة', '#10B981', whiteSummary, blackSummary, playerColor)}
${renderClassRow('best', emoji('best_move_star', '★', 12) + ' أفضل نقلة', '#10B981', whiteSummary, blackSummary, playerColor)}
${renderClassRow('good', '✓ جيدة', '#94a3b8', whiteSummary, blackSummary, playerColor)}
${renderClassRow('book', '📖 نظرية', '#94a3b8', whiteSummary, blackSummary, playerColor)}
${renderClassRow('book', emoji('book', '📖', 12) + ' نظرية', '#94a3b8', whiteSummary, blackSummary, playerColor)}
${renderClassRow('inaccuracy', '?! عدم دقة', '#FBBF24', whiteSummary, blackSummary, playerColor)}
${renderClassRow('mistake', '? خطأ', '#F97316', whiteSummary, blackSummary, playerColor)}
${renderClassRow('blunder', '?? خطأ فادح', '#EF4444', whiteSummary, blackSummary, playerColor)}
......
import * as scene from '../../../core/scene.js';
import * as audio from '../../../core/audio.js';
import { t } from '../../../core/i18n.js';
import { emoji } from '../../../core/theme.js';
export function mountRoom(el, params) {
el.innerHTML = `
<div style="display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;gap:var(--s-4);">
<div style="font-size:24px;font-weight:700;">${t('game.domino')}</div>
<div style="color:var(--text-secondary);">${t('play.searching')}</div>
<div class="pulse" style="width:60px;height:60px;border-radius:50%;border:3px solid var(--domino-primary);display:flex;align-items:center;justify-content:center;"></div>
<div class="pulse" style="width:60px;height:60px;border-radius:50%;border:3px solid var(--domino-primary);display:flex;align-items:center;justify-content:center;">${emoji('domino_tile', '⬚', 32)}</div>
<button class="btn btn-secondary" id="cancel-btn">${t('play.cancel')}</button>
</div>
`;
......
......@@ -9,7 +9,7 @@ import * as juice from '../../../core/juice.js';
import { getPiecePosition, getHomeBasePosition, SAFE_SQUARES, HOME_COLUMNS, SHARED_PATH } from '../logic/board-map.js';
import * as emoteSystem from '../../chess/components/emotes.js';
import * as mp from '../../../core/multiplayer.js';
import { emoji } from '../../../core/theme.js';
import { emoji, getAsset } from '../../../core/theme.js';
import * as net from '../../../core/net.js';
let game, validMoves, ctx, canvas, boardSize, cellSize;
......@@ -142,7 +142,17 @@ export function mountGame(el, params) {
}
function renderDiceFace(diceBox, value) {
// Flat dice with CSS dots in a 3x3 grid
const diceUrl = getAsset('emoji_dice_' + value);
if (diceUrl) {
diceBox.innerHTML = `<img src="${diceUrl}" style="width:100%;height:100%;object-fit:contain;" draggable="false">`;
diceBox.style.padding = '0';
diceBox.style.display = 'flex';
diceBox.style.gridTemplate = 'none';
return;
}
diceBox.style.padding = '6px';
diceBox.style.display = 'grid';
diceBox.style.gridTemplate = 'repeat(3,1fr)/repeat(3,1fr)';
const dots = {
1: [0,0,0,0,1,0,0,0,0],
2: [0,0,1,0,0,0,1,0,0],
......
import * as scene from '../../../core/scene.js';
import * as audio from '../../../core/audio.js';
import { t } from '../../../core/i18n.js';
import { emoji } from '../../../core/theme.js';
export function mountRoom(el, params) {
el.innerHTML = `
<div style="display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;gap:var(--s-4);">
<div style="font-size:24px;font-weight:700;">${t('game.ludo')}</div>
<div style="color:var(--text-secondary);">${t('play.searching')}</div>
<div class="pulse" style="width:60px;height:60px;border-radius:50%;border:3px solid var(--ludo-primary);display:flex;align-items:center;justify-content:center;"></div>
<div class="pulse" style="width:60px;height:60px;border-radius:50%;border:3px solid var(--ludo-primary);display:flex;align-items:center;justify-content:center;">${emoji('ludo_hex', '⬡', 32)}</div>
<button class="btn btn-secondary" id="cancel-btn">${t('play.cancel')}</button>
</div>
`;
......
import { emoji } from '../../../core/theme.js';
// Puzzle theme definitions with Arabic names and icons
const THEME_EMOJI_MAP = {
'sacrifice': ['theme_sacrifice', '💥'],
'fork': ['theme_fork', '🔱'],
'pin': ['theme_pin', '📌'],
'back-rank': ['theme_castle', '🏰'],
'promotion': ['theme_crown', '👑'],
};
export const THEMES = [
{ key: 'mate', name: 'كش مات', nameEn: 'Checkmate', icon: '♚' },
......@@ -25,7 +34,9 @@ export function getThemeName(key, lang = 'ar') {
return lang === 'ar' ? theme.name : theme.nameEn;
}
export function getThemeIcon(key) {
export function getThemeIcon(key, size = 16) {
const mapping = THEME_EMOJI_MAP[key];
if (mapping) return emoji(mapping[0], mapping[1], size);
const theme = THEMES.find(t => t.key === key);
return theme?.icon || '♟';
}
......
......@@ -12,9 +12,9 @@ export async function mountLeaderboard(el) {
<button class="btn btn-secondary" id="btn-tournaments" style="font-size:12px;min-height:32px;">${t('rank.tournaments')}</button>
</div>
<div style="display:flex;gap:var(--s-2);overflow-x:auto;" id="game-tabs">
<button class="btn btn-secondary tab-active" data-game="chess" style="font-size:12px;min-height:32px;"> ${t('game.chess')}</button>
<button class="btn btn-secondary" data-game="domino" style="font-size:12px;min-height:32px;"> ${t('game.domino')}</button>
<button class="btn btn-secondary" data-game="ludo" style="font-size:12px;min-height:32px;"> ${t('game.ludo')}</button>
<button class="btn btn-secondary tab-active" data-game="chess" style="font-size:12px;min-height:32px;">${emoji('chess_pawn', '♟', 12)} ${t('game.chess')}</button>
<button class="btn btn-secondary" data-game="domino" style="font-size:12px;min-height:32px;">${emoji('domino_tile', '⬚', 12)} ${t('game.domino')}</button>
<button class="btn btn-secondary" data-game="ludo" style="font-size:12px;min-height:32px;">${emoji('ludo_hex', '⬡', 12)} ${t('game.ludo')}</button>
</div>
<div id="leaderboard-list">
<div class="skeleton" style="height:50px;margin-bottom:var(--s-2);"></div>
......
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