/
axe
/
gala
Обзор
Документация
Войти
/
axe
/
gala
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
modules/nextRankCalculator.js
222 строки
8 KB
gusen
refactor 1
26 июн 2026, 17:15
26 июн 2026, 17:15
c828b36
Код
Авторство
О чём код?
import { formatNumber } from './common/utils.js'; import { saveBuilderCounts, loadBuilderCounts } from './common/storage.js'; import { calculateCoresNeeded } from './common/cores.js'; import { RANKS } from './common/ranks.js'; import { ICONS, votesToCounts } from './common/icons.js'; // Состояние let currentCounts = {}; let visualElement = null; let totalElement = null; function getDefaultCounts() { const defaults = {}; ICONS.forEach(icon => { defaults[icon.id] = 0; }); return defaults; } // Прямая конвертация (5 → 1, 3 → 1 для МЗЗ) function normalizeCounts() { let changed = true; while (changed) { changed = false; for (let i = 0; i < ICONS.length; i++) { const icon = ICONS[i]; if (currentCounts[icon.id] >= icon.maxStack) { const overflow = Math.floor(currentCounts[icon.id] / icon.maxStack); if (overflow > 0) { currentCounts[icon.id] -= overflow * icon.maxStack; const nextIcon = ICONS[i + 1]; if (nextIcon) { currentCounts[nextIcon.id] += overflow; changed = true; } else { currentCounts[icon.id] += overflow * icon.maxStack; } } } } } } function calculateTotalVotes() { let total = 0; ICONS.forEach(icon => { total += (currentCounts[icon.id] || 0) * icon.votes; }); return total; } function updateVisualDisplay() { if (!visualElement) return; visualElement.innerHTML = ''; let hasItems = false; for (let i = ICONS.length - 1; i >= 0; i--) { const icon = ICONS[i]; const count = currentCounts[icon.id] || 0; if (count > 0) { hasItems = true; for (let j = 0; j < count; j++) { const img = document.createElement('img'); img.src = icon.img; img.alt = icon.name; img.title = icon.name; img.style.cssText = 'width:36px;height:36px;object-fit:contain;margin:2px;'; visualElement.appendChild(img); } } } visualElement.innerHTML = hasItems ? visualElement.innerHTML : '<span style="color: #7f8c8d;">(ничего не выбрано)</span>'; if (totalElement) { totalElement.textContent = `${formatNumber(calculateTotalVotes())} голосов`; } } function handleBuilderClick(e) { const target = e.target; // Клик по иконке = +1 if (target.classList.contains('builder-img')) { const id = target.dataset.id; if (!id) return; currentCounts[id] = (currentCounts[id] || 0) + 1; normalizeCounts(); updateVisualDisplay(); saveBuilderCounts(currentCounts, 'current'); return; } // Клик по кнопке - = -1 if (target.classList.contains('builder-minus')) { const id = target.dataset.id; if (!id) return; const icon = ICONS.find(i => i.id === id); if (!icon) return; const totalVotes = calculateTotalVotes(); if (totalVotes < icon.votes) return; const newVotes = totalVotes - icon.votes; currentCounts = votesToCounts(newVotes); updateVisualDisplay(); saveBuilderCounts(currentCounts, 'current'); return; } } function renderBuilder(containerId) { const container = document.getElementById(containerId); if (!container) return; container.innerHTML = ''; const fragment = document.createDocumentFragment(); ICONS.forEach(icon => { const div = document.createElement('div'); div.className = 'builder-item'; div.innerHTML = ` <div class="builder-icon-wrapper"> <img class="builder-img" src="${icon.img}" alt="${icon.name}" data-id="${icon.id}" /> <button class="builder-minus" data-id="${icon.id}">−</button> </div> `; fragment.appendChild(div); }); container.appendChild(fragment); container.addEventListener('click', handleBuilderClick); } function getRankByVotes(votes) { let currentRank = null; let nextRank = null; for (let i = 0; i < RANKS.length; i++) { if (votes >= RANKS[i].value) { currentRank = RANKS[i]; } else { nextRank = RANKS[i]; break; } } if (!nextRank && currentRank) { nextRank = { name: 'Максимальный', value: currentRank.value }; } return { currentRank, nextRank }; } export function initNextRankCalculator(containerId, visualId, totalId) { visualElement = document.getElementById(visualId); totalElement = document.getElementById(totalId); const saved = loadBuilderCounts(getDefaultCounts(), 'current'); currentCounts = saved; normalizeCounts(); renderBuilder(containerId); updateVisualDisplay(); const calcBtn = document.getElementById('calcNextRank'); if (calcBtn) { const newCalcBtn = calcBtn.cloneNode(true); calcBtn.parentNode.replaceChild(newCalcBtn, calcBtn); newCalcBtn.onclick = () => { const resultEl = document.getElementById('nextRankResult'); const currentVotes = calculateTotalVotes(); const { currentRank, nextRank } = getRankByVotes(currentVotes); if (!currentRank) { resultEl.innerHTML = 'Не удалось определить текущий ранг'; return; } if (currentRank.name === 'Великий' && !nextRank) { resultEl.innerHTML = '🎉 Поздравляем! Вы достигли максимального ранга «Великий»!'; return; } const votesNeeded = nextRank.value - currentVotes; const cores = calculateCoresNeeded(votesNeeded); // Определяем, сколько голосов в звёздах/рубинах осталось const remainingVotes = votesNeeded; resultEl.innerHTML = ` <div style="margin-bottom:10px;"> <strong>Текущий ранг:</strong> ${currentRank.name} (${formatNumber(currentVotes)} голосов) </div> <div style="margin-bottom:15px;"> <strong>Следующий ранг:</strong> ${nextRank.name} (${formatNumber(nextRank.value)} голосов) </div> <div style="margin-bottom:15px;padding:10px;background:rgba(255,255,255,0.05);border-radius:8px;"> <strong>Осталось до ранга:</strong> ${formatNumber(votesNeeded)} голосов </div> <div style="margin-top:10px;font-weight:bold;color:#e2e8f0;"> Сколько ядер нужно: </div> <div style="display:flex;flex-wrap:wrap;gap:15px;margin-top:8px;"> <div><img src="./assets/images/cb/gball.png" class="core-icon" style="width:24px;vertical-align:middle;"> Золотых: ≈ ${cores.gold}</div> <div><img src="./assets/images/cb/fball.png" class="core-icon" style="width:24px;vertical-align:middle;"> Огненных: ≈ ${cores.fire}</div> <div><img src="./assets/images/cb/xball.png" class="core-icon" style="width:24px;vertical-align:middle;"> Разрывных: ≈ ${cores.explosive}</div> </div> `; }; } } export function resetCurrentBuilder() { currentCounts = getDefaultCounts(); normalizeCounts(); updateVisualDisplay(); saveBuilderCounts(currentCounts, 'current'); }