123
This commit is contained in:
@ -282,11 +282,12 @@
|
||||
yourIndex = -1;
|
||||
const spectatorBadge = $('spectator-badge');
|
||||
if (!spectatorBadge) {
|
||||
const badge = document.createElement('div');
|
||||
badge.id = 'spectator-badge';
|
||||
badge.style.cssText = 'position: fixed; top: 1rem; right: 1rem; background: rgba(212,168,75,0.9); color: #000; padding: 0.5rem 1rem; border-radius: 8px; font-weight: 700; z-index: 200;';
|
||||
badge.textContent = '👁 НАБЛЮДАТЕЛЬ';
|
||||
document.body.appendChild(badge);
|
||||
const badge = document.createElement('div');
|
||||
badge.id = 'spectator-badge';
|
||||
badge.style.cssText = 'position: fixed; top: 1rem; right: 1rem; background: rgba(212,168,75,0.9); color: #000; padding: 0.5rem 1rem; border-radius: 8px; font-weight: 700; z-index: 200; display: flex; align-items: center; gap: 0.5rem;';
|
||||
badge.innerHTML = '<i data-lucide="eye" style="width: 18px; height: 18px;"></i><span>НАБЛЮДАТЕЛЬ</span>';
|
||||
document.body.appendChild(badge);
|
||||
if (typeof lucide !== 'undefined') lucide.createIcons();
|
||||
}
|
||||
} else {
|
||||
const spectatorBadge = $('spectator-badge');
|
||||
@ -367,6 +368,11 @@
|
||||
resetBtn.classList.toggle('hidden', state.yourIndex !== 0);
|
||||
}
|
||||
bindGameEvents(state);
|
||||
|
||||
// Обновляем иконки Lucide после рендеринга
|
||||
if (typeof lucide !== 'undefined') {
|
||||
setTimeout(() => lucide.createIcons(), 100);
|
||||
}
|
||||
}
|
||||
|
||||
function showGameOver(state) {
|
||||
@ -435,18 +441,22 @@
|
||||
const isCurrent = state.currentPlayerIndex === i;
|
||||
const isDead = p.health <= 0 || p.isDead;
|
||||
const name = p.name || 'Игрок ' + (i + 1);
|
||||
const isAI = p.isAI || false;
|
||||
const minions = (p.board || []).map((m, j) => renderBoardMinion(m, i, j, state, true, false));
|
||||
const heroBar = renderHeroTarget(i, state);
|
||||
const heroDrop = renderHeroDropZone(i, state);
|
||||
const canSteal = !state.isSpectator && state.currentPlayerIndex === state.yourIndex && spellMode.active && spellMode.cardId && cardDb[spellMode.cardId]?.spellEffect === 'steal_cards';
|
||||
return `
|
||||
<div class="opponent-block ${isCurrent ? 'current-turn' : ''} ${canSteal ? 'steal-target' : ''} ${isDead ? 'defeated' : ''}" data-opponent-index="${i}" data-player-index="${i}">
|
||||
<div class="opponent-name ${isDead ? 'defeated-name' : ''}">${escapeHtml(name)}${isDead ? ' <span class="defeated-badge">✝</span>' : ''}</div>
|
||||
<div class="opponent-name ${isDead ? 'defeated-name' : ''}">
|
||||
${isAI ? '<i data-lucide="bot" style="width: 16px; height: 16px; vertical-align: middle; margin-right: 0.3rem; color: var(--cyan);"></i>' : ''}
|
||||
${escapeHtml(name)}${isDead ? ' <span class="defeated-badge"><i data-lucide="skull" style="width: 14px; height: 14px; vertical-align: middle;"></i></span>' : ''}
|
||||
</div>
|
||||
<div class="opponent-stats ${isDead ? 'defeated-stats' : ''}">
|
||||
<span class="opponent-health">❤ ${p.health ?? 30}</span>
|
||||
${(p.armor || 0) > 0 ? `<span class="opponent-armor">🛡 ${p.armor}</span>` : ''}
|
||||
<span>🔵 ${p.mana ?? 0}/${p.maxMana ?? 0}</span>
|
||||
${p.deck && p.deck.length > 0 ? `<span>📚 ${p.deck.length}</span>` : ''}
|
||||
<span class="opponent-health"><i data-lucide="heart" style="width: 14px; height: 14px; vertical-align: middle; margin-right: 0.2rem; color: #e63946;"></i>${p.health ?? 30}</span>
|
||||
${(p.armor || 0) > 0 ? `<span class="opponent-armor"><i data-lucide="shield" style="width: 14px; height: 14px; vertical-align: middle; margin-right: 0.2rem; color: #5eb3e8;"></i>${p.armor}</span>` : ''}
|
||||
<span><i data-lucide="zap" style="width: 14px; height: 14px; vertical-align: middle; margin-right: 0.2rem; color: #00d4ff;"></i>${p.mana ?? 0}/${p.maxMana ?? 0}</span>
|
||||
${p.deck && p.deck.length > 0 ? `<span><i data-lucide="book-open" style="width: 14px; height: 14px; vertical-align: middle; margin-right: 0.2rem; color: var(--gold);"></i>${p.deck.length}</span>` : ''}
|
||||
</div>
|
||||
<div class="opponent-board">${heroDrop}${heroBar}${minions.join('')}</div>
|
||||
</div>`;
|
||||
@ -526,7 +536,7 @@
|
||||
const attackerName = attackerCard?.name || 'Миньон';
|
||||
const targetName = targetCard?.name || 'Миньон';
|
||||
const damage = logEntry.damage || attackerMinion?.attack || 0;
|
||||
text = who(attackerIdx) + ' → ' + attackerName + ' ⚔ атакует ' + targetName + ' (' + damage + ' урона) → ' + who(targetIdx);
|
||||
text = who(attackerIdx) + ' → ' + attackerName + ' <i data-lucide="swords" style="width: 14px; height: 14px; vertical-align: middle; display: inline-block;"></i> атакует ' + targetName + ' (' + damage + ' урона) → ' + who(targetIdx);
|
||||
}
|
||||
} else if (logEntry.type === 'attackHero' && logEntry.fromPlayer !== undefined && logEntry.toPlayer !== undefined) {
|
||||
const attackerPlayer = state.players[logEntry.fromPlayer];
|
||||
@ -537,10 +547,10 @@
|
||||
const attackerCard = cardDb[attackerMinion?.cardId];
|
||||
const attackerName = attackerCard?.name || 'Миньон';
|
||||
const damage = logEntry.damage || attackerMinion?.attack || 0;
|
||||
text = who(logEntry.fromPlayer) + ' → ' + attackerName + ' ⚔ атакует героя ' + who(logEntry.toPlayer) + ' (' + damage + ' урона)!';
|
||||
text = who(logEntry.fromPlayer) + ' → ' + attackerName + ' <i data-lucide="swords" style="width: 14px; height: 14px; vertical-align: middle; display: inline-block;"></i> атакует героя ' + who(logEntry.toPlayer) + ' (' + damage + ' урона)!';
|
||||
} else {
|
||||
const damage = logEntry.damage || 0;
|
||||
text = who(logEntry.fromPlayer) + ' ⚔ атакует героя ' + who(logEntry.toPlayer) + ' (' + damage + ' урона)!';
|
||||
text = who(logEntry.fromPlayer) + ' <i data-lucide="swords" style="width: 14px; height: 14px; vertical-align: middle; display: inline-block;"></i> атакует героя ' + who(logEntry.toPlayer) + ' (' + damage + ' урона)!';
|
||||
}
|
||||
}
|
||||
} else if (logEntry.type === 'spell' && logEntry.fromPlayer !== undefined && logEntry.toPlayer !== undefined) {
|
||||
@ -1112,7 +1122,7 @@
|
||||
var heroCls = 'drop-target drop-target-hero' + (spellHero ? ' spell-target' : '') + (heroAb ? ' hero-ability-target' : '');
|
||||
var title = spellHero ? 'Цель заклинания' : (heroAb ? 'Цель геройской способности' : 'Перетащи сюда миньона для атаки по лидеру');
|
||||
return '<div class="' + heroCls + '" data-drop-player="' + playerIndex + '" data-drop-board="-1" title="' + title + '">'
|
||||
+ '<span class="drop-hero-icon">❤</span>'
|
||||
+ '<span class="drop-hero-icon"><i data-lucide="heart" style="width: 18px; height: 18px; color: #e63946;"></i></span>'
|
||||
+ '<span class="drop-hero-name">' + escapeHtml(name) + '</span>'
|
||||
+ '</div>';
|
||||
}
|
||||
@ -1124,7 +1134,7 @@
|
||||
return `
|
||||
<div class="card-wrap targetable hero-target drop-target" data-player-index="${playerIndex}" data-board-index="-1" data-drop-player="${playerIndex}" data-drop-board="-1">
|
||||
<div class="card faction-neutral">
|
||||
<div class="card-art">❤</div>
|
||||
<div class="card-art"><i data-lucide="heart" style="width: 48px; height: 48px; color: #e63946;"></i></div>
|
||||
<div class="card-info">
|
||||
<div class="card-name">${escapeHtml(name)} (лидер)</div>
|
||||
<div class="card-stats"><span class="hp">${p?.health ?? 30}</span></div>
|
||||
@ -2183,7 +2193,8 @@
|
||||
const statusColor = room.gameStarted ? 'var(--amber)' : 'var(--cyan)';
|
||||
const playersText = room.players.length > 0 ? room.players.join(', ') : 'Нет игроков';
|
||||
const canJoin = !room.gameStarted && room.playerCount < room.maxPlayers;
|
||||
const actionText = room.gameStarted ? '👁 Наблюдать' : (canJoin ? '▶ Подключиться' : 'Полная');
|
||||
const actionIcon = room.gameStarted ? '<i data-lucide="eye" style="width: 14px; height: 14px; vertical-align: middle; margin-right: 0.3rem;"></i>' : (canJoin ? '<i data-lucide="play" style="width: 14px; height: 14px; vertical-align: middle; margin-right: 0.3rem;"></i>' : '');
|
||||
const actionText = room.gameStarted ? 'Наблюдать' : (canJoin ? 'Подключиться' : 'Полная');
|
||||
const actionClass = room.gameStarted ? 'btn-ghost' : (canJoin ? 'btn-primary' : 'btn-ghost');
|
||||
const actionDisabled = !room.gameStarted && !canJoin;
|
||||
|
||||
@ -2196,33 +2207,38 @@
|
||||
<div style="text-align: right; font-size: 0.85rem; color: #94a3b8;">
|
||||
<div>Игроки: ${room.playerCount}/${room.maxPlayers}</div>
|
||||
${room.spectators > 0 ? `<div>Наблюдателей: ${room.spectators}</div>` : ''}
|
||||
${room.hasAI ? '<div style="color: var(--amber);">🤖 ИИ</div>' : ''}
|
||||
${room.hasAI ? '<div style="color: var(--amber); display: inline-flex; align-items: center; gap: 0.3rem;"><i data-lucide="bot" style="width: 14px; height: 14px;"></i>ИИ</div>' : ''}
|
||||
</div>
|
||||
</div>
|
||||
<div style="font-size: 0.8rem; color: #94a3b8; margin-bottom: 0.5rem;">${escapeHtml(playersText)}</div>
|
||||
<button type="button" class="btn ${actionClass}" data-room-code="${room.code}" ${actionDisabled ? 'disabled' : ''} style="width: 100%;">${actionText}</button>
|
||||
<button type="button" class="btn ${actionClass}" data-room-code="${room.code}" ${actionDisabled ? 'disabled' : ''} style="width: 100%;">${actionIcon}${actionText}</button>
|
||||
</div>`;
|
||||
}).join('');
|
||||
|
||||
// Обработчики подключения к комнатам
|
||||
$all('.room-item button').forEach(btn => {
|
||||
btn.addEventListener('click', function() {
|
||||
const code = this.dataset.roomCode;
|
||||
const name = ($('browse-name')?.value || '').trim() || 'Игрок';
|
||||
if (!code) return;
|
||||
|
||||
if (!socket || !socket.connected) {
|
||||
const url = window.location.origin;
|
||||
connect(url);
|
||||
socket.once('connect', () => {
|
||||
btn.addEventListener('click', function() {
|
||||
const code = this.dataset.roomCode;
|
||||
const name = ($('browse-name')?.value || '').trim() || 'Игрок';
|
||||
if (!code) return;
|
||||
|
||||
if (!socket || !socket.connected) {
|
||||
const url = window.location.origin;
|
||||
connect(url);
|
||||
socket.once('connect', () => {
|
||||
socket.emit('joinRoom', { code, name });
|
||||
});
|
||||
} else {
|
||||
socket.emit('joinRoom', { code, name });
|
||||
});
|
||||
} else {
|
||||
socket.emit('joinRoom', { code, name });
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// Обновляем иконки Lucide после создания кнопок
|
||||
if (typeof lucide !== 'undefined') {
|
||||
setTimeout(() => lucide.createIcons(), 100);
|
||||
}
|
||||
};
|
||||
$('btn-instructions')?.addEventListener('click', () => {
|
||||
$('instructions-overlay')?.classList.remove('hidden');
|
||||
});
|
||||
@ -2803,9 +2819,19 @@
|
||||
if (window.Music) {
|
||||
window.Music.init();
|
||||
}
|
||||
// Инициализация Lucide Icons после загрузки DOM
|
||||
if (typeof lucide !== 'undefined') {
|
||||
lucide.createIcons();
|
||||
// Обновляем иконки при изменении DOM
|
||||
const observer = new MutationObserver(() => {
|
||||
lucide.createIcons();
|
||||
});
|
||||
observer.observe(document.body, { childList: true, subtree: true });
|
||||
}
|
||||
});
|
||||
} else {
|
||||
init();
|
||||
if (typeof lucide !== 'undefined') lucide.createIcons();
|
||||
if (window.Music) {
|
||||
window.Music.init();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user