Mudanças entre as edições de "Widget:Character.Skills"

De Wiki Gla
Ir para navegação Ir para pesquisar
m
m
 
(47 revisões intermediárias pelo mesmo usuário não estão sendo mostradas)
Linha 42: Linha 42:
         } function chooseDescFrom(obj) {
         } function chooseDescFrom(obj) {
             const lang = getLangKey();
             const lang = getLangKey();
             const pack = obj.desc_i18n || {
            // Aceita tanto desc_i18n quanto desc para compatibilidade
             const pack = obj.desc_i18n || obj.desc || {
                 pt: obj.descPt, en: obj.descEn, es: obj.descEs, pl: obj.descPl
                 pt: obj.descPt, en: obj.descEn, es: obj.descEs, pl: obj.descPl
             };
             };
             return (pack && (pack[lang] || pack.pt || pack.en || pack.es || pack.pl)) || (obj.desc || '');
             return (pack && (pack[lang] || pack.pt || pack.en || pack.es || pack.pl)) || '';
         } function renderSubAttributesFromObj(s, L) {
         } function renderSubAttributesFromObj(s, L) {
             const chip = (label, val) => (val ? `<div class="attr-row"><span class="attr-label">${label}</span><span class="attr-value">${val}</span></div>` : '');
             const chip = (label, val) => (val ? `<div class="attr-row"><span class="attr-label">${label}</span><span class="attr-value">${val}</span></div>` : '');
Linha 114: Linha 115:
                 });
                 });
             });
             });
         } function normalizeFileURL(raw, fallback = '') {
         }
 
        // ====== Skill/Subskill inheritance helpers ======
        const mainSkillsMeta = {
            byIndex: new Map(),
            byName: new Map(),
            ready: false
        };
 
        function normalizeFileURL(raw, fallback = '') {
             if (!raw) return fallback;
             if (!raw) return fallback;
             const val = String(raw).trim();
             const val = String(raw).trim();
Linha 121: Linha 131:
                 return val;
                 return val;
             } return filePathURL(val);
             } return filePathURL(val);
         } function collectAssetsFromSubs(subs, iconsSet, videosSet, flagsSet) {
         }
 
        function extractFileNameFromURL(url) {
            if (!url) return '';
            const match = String(url).match(/(?:FilePath\/)([^&?]+)/i);
            return match ? decodeURIComponent(match[1]) : '';
        }
 
        function parseAttrString(raw) {
            const parts = (raw || '').split(',').map(v => v.trim());
            const safe = idx => {
                const val = parts[idx] || '';
                return (val && val !== '-') ? val : '';
            };
            return {
                powerpve: safe(0),
                powerpvp: safe(1),
                energy: safe(2),
                cooldown: safe(3)
            };
        }
 
        function hasText(value) {
            return typeof value === 'string' ? value.trim() !== '' : value !== undefined && value !== null;
        }
 
        function pickFilled(current, fallback) {
            if (current === 0 || current === '0') return current;
            if (!hasText(current)) return fallback;
            return current;
        }
 
        function buildMainSkillsMeta(nodes) {
            if (mainSkillsMeta.ready) {
                return mainSkillsMeta;
            }
            (nodes || []).forEach(icon => {
                const index = (icon.dataset.index || '').trim();
                if (!index) return;
                const name = (icon.dataset.nome || icon.dataset.name || '').trim();
                const attrs = parseAttrString(icon.dataset.atr || '');
                let iconFile = (icon.dataset.iconFile || '').trim();
                if (!iconFile) {
                    const imgSrc = icon.querySelector('img')?.src || '';
                    const iconMatch = imgSrc.match(/(?:FilePath|images)\/([^\/?]+)$/);
                    iconFile = iconMatch ? decodeURIComponent(iconMatch[1]) : '';
                }
                let videoFile = (icon.dataset.videoFile || '').trim();
                if (!videoFile) {
                    videoFile = extractFileNameFromURL(icon.dataset.video || '');
                }
                const meta = {
                    index,
                    name,
                    icon: iconFile || 'Nada.png',
                    level: icon.dataset.level || '',
                    video: videoFile || '',
                    powerpve: attrs.powerpve || '',
                    powerpvp: attrs.powerpvp || '',
                    energy: attrs.energy || '',
                    cooldown: attrs.cooldown || '',
                    desc: icon.dataset.desc || '',
                    descPt: icon.dataset.descPt || '',
                    descEn: icon.dataset.descEn || '',
                    descEs: icon.dataset.descEs || '',
                    descPl: icon.dataset.descPl || ''
                };
                mainSkillsMeta.byIndex.set(index, meta);
                mainSkillsMeta.byIndex.set(parseInt(index, 10), meta);
                if (name) {
                    mainSkillsMeta.byName.set(name, meta);
                }
            });
            mainSkillsMeta.ready = true;
            return mainSkillsMeta;
        }
 
        function inheritSubskillFromMain(sub, meta) {
            if (!sub || !meta) return sub;
            // Suporta refS (novo) e refM (legado)
            const refS = ((sub.refS || sub.S || sub.s || '') + '').trim();
            const refIndex = ((sub.refM || sub.M || sub.m || '') + '').trim();
            let name = (sub.name || sub.n || '').trim();
            let main = null;
 
 
            // Primeiro tenta por refS
            if (refS) {
                main = meta.byIndex.get(refS) || meta.byIndex.get(parseInt(refS, 10));
            }
            // Depois por refM
            if (!main && refIndex) {
                main = meta.byIndex.get(refIndex) || meta.byIndex.get(parseInt(refIndex, 10));
            }
            // Por último pelo nome
            if (!main && name) {
                main = meta.byName.get(name);
            }
            if (!main) {
                return sub;
            }
 
            const hydrated = { ...sub };
            if (!name && main.name) {
                name = main.name;
            }
            hydrated.name = name || hydrated.name || main.name || '';
            hydrated.icon = pickFilled(hydrated.icon, main.icon || 'Nada.png');
            hydrated.level = pickFilled(hydrated.level, main.level || '');
            hydrated.video = pickFilled(hydrated.video, main.video || '');
            hydrated.powerpve = pickFilled(hydrated.powerpve, main.powerpve || '');
            hydrated.powerpvp = pickFilled(hydrated.powerpvp, main.powerpvp || '');
            hydrated.energy = pickFilled(hydrated.energy, main.energy || '');
            hydrated.cooldown = pickFilled(hydrated.cooldown, main.cooldown || '');
 
            if (!hasText(hydrated.descPt) && hasText(main.descPt)) hydrated.descPt = main.descPt;
            if (!hasText(hydrated.descEn) && hasText(main.descEn)) hydrated.descEn = main.descEn;
            if (!hasText(hydrated.descEs) && hasText(main.descEs)) hydrated.descEs = main.descEs;
            if (!hasText(hydrated.descPl) && hasText(main.descPl)) hydrated.descPl = main.descPl;
            if (!hasText(hydrated.desc) && hasText(main.desc)) hydrated.desc = main.desc;
 
            if (!hydrated.desc_i18n && (hydrated.descPt || hydrated.descEn || hydrated.descEs || hydrated.descPl)) {
                hydrated.desc_i18n = {
                    pt: hydrated.descPt || '',
                    en: hydrated.descEn || '',
                    es: hydrated.descEs || '',
                    pl: hydrated.descPl || ''
                };
            }
 
            return hydrated;
        }
 
        function inheritSubskillTree(subs, meta) {
            if (!Array.isArray(subs)) return [];
            return subs.map(sub => {
                const hydrated = inheritSubskillFromMain(sub, meta);
                if (Array.isArray(hydrated.subs)) {
                    hydrated.subs = inheritSubskillTree(hydrated.subs, meta);
                }
                return hydrated;
            });
        }
 
        function collectAssetsFromSubs(subs, iconsSet, videosSet, flagsSet) {
             if (!Array.isArray(subs)) return;
             if (!Array.isArray(subs)) return;
             subs.forEach(sub => {
             subs.forEach(sub => {
                 const iconURL = normalizeFileURL(sub.icon || 'Nada.png', filePathURL('Nada.png'));
                 const iconURL = normalizeFileURL(sub.icon || 'Nada.png', filePathURL('Nada.png'));
                 if (iconURL) iconsSet.add(iconURL);
                 if (iconURL && iconURL !== filePathURL('Nada.png')) iconsSet.add(iconURL);
                 if (sub.video) {
                // Vídeo normal
                 if (sub.video && sub.video.trim() !== '' && sub.video !== 'Nada.png' && !sub.video.toLowerCase().includes('nada.png')) {
                     const videoURL = normalizeFileURL(sub.video);
                     const videoURL = normalizeFileURL(sub.video);
                     if (videoURL) videosSet.add(videoURL);
                     if (videoURL) videosSet.add(videoURL);
                 } if (Array.isArray(sub.flags)) {
                 }
                // Vídeo de weapon
                if (sub.weapon && typeof sub.weapon === 'object' && sub.weapon.video && sub.weapon.video.trim() !== '' && sub.weapon.video !== 'Nada.png' && !sub.weapon.video.toLowerCase().includes('nada.png')) {
                    const weaponVideoURL = normalizeFileURL(sub.weapon.video);
                    if (weaponVideoURL) videosSet.add(weaponVideoURL);
                }
                if (Array.isArray(sub.flags)) {
                     sub.flags.forEach(flagKey => {
                     sub.flags.forEach(flagKey => {
                         const url = getFlagIconURL(flagKey);
                         const url = getFlagIconURL(flagKey);
Linha 148: Linha 309:
                 if (img && img.src) {
                 if (img && img.src) {
                     iconsSet.add(img.src);
                     iconsSet.add(img.src);
                 } else if (el.dataset.icon) {
                 } else if (el.dataset.iconFile) {
                     iconsSet.add(normalizeFileURL(el.dataset.icon));
                     const iconURL = normalizeFileURL(el.dataset.iconFile);
                 } const videoRaw = (el.dataset.video || '').trim();
                    if (iconURL) iconsSet.add(iconURL);
                 if (videoRaw) {
                 }
                     videosSet.add(normalizeFileURL(videoRaw));
                // Vídeo normal da skill
                 } if (el.dataset.flags) {
                const videoRaw = (el.dataset.videoFile || el.dataset.video || '').trim();
                 if (videoRaw && videoRaw !== 'Nada.png' && !videoRaw.toLowerCase().includes('nada.png')) {
                     const videoURL = normalizeFileURL(videoRaw);
                    if (videoURL) videosSet.add(videoURL);
                }
                // Vídeo de weapon da skill
                if (el.dataset.weapon) {
                    try {
                        const weaponData = JSON.parse(el.dataset.weapon);
                        if (weaponData && weaponData.video && weaponData.video.trim() !== '' && weaponData.video !== 'Nada.png' && !weaponData.video.toLowerCase().includes('nada.png')) {
                            const weaponVideoURL = normalizeFileURL(weaponData.video);
                            if (weaponVideoURL) videosSet.add(weaponVideoURL);
                        }
                    } catch (e) {
                    }
                 }
                if (el.dataset.flags) {
                     try {
                     try {
                         const parsedFlags = JSON.parse(el.dataset.flags);
                         const parsedFlags = JSON.parse(el.dataset.flags);
Linha 248: Linha 425:
             }
             }
         } const iconsBar = $('#skills') ? $('.icon-bar', $('#skills')) : null;
         } const iconsBar = $('#skills') ? $('.icon-bar', $('#skills')) : null;
        const skillsTopRail = iconsBar ? iconsBar.closest('.top-rail.skills') : null;
         const iconItems = iconsBar ? Array.from(iconsBar.querySelectorAll('.skill-icon')) : [];
         const iconItems = iconsBar ? Array.from(iconsBar.querySelectorAll('.skill-icon')) : [];
        buildMainSkillsMeta(iconItems);
        // Verifica se há weapon em skills principais OU em subskills
        function checkHasAnyWeapon() {
            // Verifica skills principais
            if (iconItems.some(el => !!el.dataset.weapon)) {
                return true;
            }
            // Verifica subskills
            for (const el of iconItems) {
                const subsRaw = el.getAttribute('data-subs');
                if (!subsRaw) continue;
                try {
                    const subs = JSON.parse(subsRaw);
                    if (Array.isArray(subs) && subs.some(s => s && s.weapon)) {
                        return true;
                    }
                } catch (e) { }
            }
            return false;
        }
        const hasWeaponSkillAvailable = checkHasAnyWeapon();
        let weaponToggleBtn = null;
         if (!assetManifest) {
         if (!assetManifest) {
             assetManifest = buildAssetManifest();
             assetManifest = buildAssetManifest();
Linha 265: Linha 465:
         let userHasInteracted = false;
         let userHasInteracted = false;
         let globalWeaponEnabled = false;
         let globalWeaponEnabled = false;
        try {
            if (localStorage.getItem('glaWeaponEnabled') === '1') {
                globalWeaponEnabled = true;
            }
        } catch (err) {
        }
        const weaponStateListeners = new Set();
        let showWeaponPopupFn = null;
        let popupShouldOpen = false;
        function attachWeaponPopupFn(fn) {
            if (typeof fn !== 'function') return;
            showWeaponPopupFn = fn;
            if (popupShouldOpen) {
                popupShouldOpen = false;
                try {
                    showWeaponPopupFn();
                } catch (err) {
                }
            }
        }
        attachWeaponPopupFn(window.__glaWeaponShowPopup);
        function requestWeaponPopupDisplay() {
            try {
                if (localStorage.getItem('glaWeaponPopupDismissed') === '1') return;
            } catch (err) {
            }
            if (typeof showWeaponPopupFn === 'function') {
                showWeaponPopupFn();
                return;
            }
            popupShouldOpen = true;
        }
        function onWeaponStateChange(fn) {
            if (typeof fn !== 'function') return;
            weaponStateListeners.add(fn);
        }
        function syncWeaponButtonState(enabled) {
            if (!weaponToggleBtn || !weaponToggleBtn.isConnected) return;
            // Usa .weapon-active em vez de .active para não conflitar com skills
            weaponToggleBtn.classList.toggle('weapon-active', !!enabled);
            weaponToggleBtn.classList.remove('active'); // Garante que .active nunca seja aplicado
            weaponToggleBtn.setAttribute('aria-pressed', enabled ? 'true' : 'false');
            weaponToggleBtn.setAttribute('aria-label', enabled ? 'Desativar Arma Especial' : 'Ativar Arma Especial');
        }
        function syncWeaponRailState(enabled) {
            if (skillsTopRail) {
                skillsTopRail.classList.toggle('weapon-mode-on', !!enabled);
            }
        }
        function notifyWeaponStateListeners(enabled) {
            weaponStateListeners.forEach(listener => {
                try {
                    listener(enabled);
                } catch (err) {
                }
            });
        }
        let pendingWeaponState = null;
        window.addEventListener('weapon:ready', (ev) => {
            if (ev && ev.detail && ev.detail.showPopup) {
                attachWeaponPopupFn(ev.detail.showPopup);
            }
            if (pendingWeaponState === null) return;
            if (typeof window.__applyWeaponState === 'function') {
                const target = pendingWeaponState;
                pendingWeaponState = null;
                window.__applyWeaponState(target);
            }
        });
         window.__setGlobalWeaponEnabled = (enabled) => {
         window.__setGlobalWeaponEnabled = (enabled) => {
             globalWeaponEnabled = enabled;
             globalWeaponEnabled = enabled;
            notifyWeaponStateListeners(enabled);
         };
         };
        function requestWeaponState(targetState) {
            if (typeof window.__applyWeaponState === 'function') {
                pendingWeaponState = null;
                window.__applyWeaponState(targetState);
                return;
            }
            pendingWeaponState = targetState;
        }
        onWeaponStateChange(syncWeaponButtonState);
        function reapplyWeaponClassesToBar() {
            if (!globalWeaponEnabled) return;
            // SISTEMA UNIFICADO: Aplica em skills E subskills
            iconsBar.querySelectorAll('.skill-icon[data-weapon], .subicon[data-weapon]').forEach(el => {
                if (!el.classList.contains('has-weapon-available')) {
                    el.classList.add('has-weapon-available');
                }
                if (!el.querySelector('.weapon-indicator')) {
                    const ind = document.createElement('div');
                    ind.className = 'weapon-indicator';
                    el.appendChild(ind);
                }
            });
        }
        function setupWeaponBarToggle(shouldShow) {
            if (!shouldShow || !iconsBar) return;
            if (iconsBar.querySelector('.weapon-bar-toggle')) return;
            const btn = document.createElement('button');
            btn.type = 'button';
            btn.className = 'skill-icon weapon-bar-toggle';
            btn.dataset.weaponToggle = '1';
            btn.dataset.nome = 'Arma Especial';
            btn.setAttribute('aria-pressed', 'false');
            btn.setAttribute('aria-label', 'Arma Especial');
            btn.innerHTML = '<svg width="24" height="24" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path fill="currentColor" d="M19.14 12.94c.04-.31.06-.63.06-.94s-.02-.63-.06-.94l2.03-1.58a.5.5 0 00.12-.62l-1.92-3.32a.5.5 0 00-.61-.22l-2.39.96c-.5-.38-1.03-.7-1.62-.94l-.37-2.48a.55.55 0 00-.55-.5h-3.82a.55.55 0 00-.55.5l-.37 2.48c-.59.24-1.12.56-1.62.94l-2.39-.96a.5.5 0 00-.61.22L3.13 8.5a.5.5 0 00.12.62l2.03 1.58c-.04.31-.06.63-.06.94s.02.63.06.94l-2.03 1.58a.5.5 0 00-.12.62l1.92 3.32a.5.5 0 00.61.22l2.39-.96c.5.38 1.03.7 1.62.94l.37 2.48a.55.55 0 00.55.5h3.82a.55.55 0 00.55-.5l.37-2.48c.59-.24 1.12-.56 1.62-.94l2.39.96a.5.5 0 00.61-.22l1.92-3.32a.5.5 0 00-.12-.62zM12 15.6a3.6 3.6 0 110-7.2 3.6 3.6 0 010 7.2z"></path></svg>';
            iconsBar.appendChild(btn);
            weaponToggleBtn = btn;
            syncWeaponButtonState(globalWeaponEnabled);
            btn.addEventListener('click', () => {
                const nextState = !globalWeaponEnabled;
                if (nextState) {
                    requestWeaponPopupDisplay();
                }
                requestWeaponState(nextState);
            });
            // Wire tooltip for weapon toggle
            const tooltip = window.__globalSkillTooltip;
            if (tooltip) {
                btn.addEventListener('mouseenter', () => {
                    const tip = document.querySelector('.skill-tooltip');
                    if (tip) tip.classList.add('weapon-tooltip');
                    tooltip.show(btn, globalWeaponEnabled ? 'Desativar Arma Especial' : 'Ativar Arma Especial');
                });
                btn.addEventListener('mouseleave', () => {
                    const tip = document.querySelector('.skill-tooltip');
                    if (tip) tip.classList.remove('weapon-tooltip');
                    tooltip.hide();
                });
            }
            reapplyWeaponClassesToBar();
        }
        onWeaponStateChange(syncWeaponRailState);
        syncWeaponRailState(globalWeaponEnabled);
        setupWeaponBarToggle(hasWeaponSkillAvailable);
         (function injectWeaponStyles() {
         (function injectWeaponStyles() {
             if (document.getElementById('weapon-toggle-styles')) return;
             if (document.getElementById('weapon-toggle-styles')) return;
Linha 273: Linha 606:
             style.id = 'weapon-toggle-styles';
             style.id = 'weapon-toggle-styles';
             style.textContent = `
             style.textContent = `
                /* Animação da borda nos ícones */
                @keyframes weapon-icon-border-scan {
                    0% { background-position: 0% 0%; }
                    100% { background-position: 400% 0%; }
                }
                @keyframes weapon-icon-pulse {
                    0%, 100% {
                        opacity: 0.5;
                        box-shadow: 0 0 8px rgba(255, 80, 80, 0.25);
                    }
                    50% {
                        opacity: 1;
                        box-shadow: 0 0 16px rgba(255, 80, 80, 0.45);
                    }
                }
                /* Skills com arma disponível - borda vermelha quando inativa */
                .skill-icon.has-weapon-available:not(.weapon-bar-toggle):not(.active)::after {
                    box-shadow: inset 0 0 0 var(--icon-ring-w) rgba(220, 70, 70, 0.8) !important;
                }
                /* Skill com arma ATIVA - laranja/coral vibrante */
                .skill-icon.has-weapon-available:not(.weapon-bar-toggle).active::after {
                    box-shadow: inset 0 0 0 var(--icon-ring-w) #FF7043 !important;
                }
                .skill-icon.has-weapon-available:not(.weapon-bar-toggle).active::before {
                    box-shadow: 0 0 12px 3px rgba(255, 112, 67, 0.35), 0 0 0 4px rgba(255, 112, 67, 0.25) !important;
                }
                /* Modo arma ON - efeito animado nos ícones */
                .top-rail.skills.weapon-mode-on .skill-icon.has-weapon-available:not(.weapon-bar-toggle) {
                    position: relative;
                }
                .top-rail.skills.weapon-mode-on .skill-icon.has-weapon-available:not(.weapon-bar-toggle)::after {
                    box-shadow: none !important;
                    background: linear-gradient(90deg,
                        rgba(255, 80, 80, 0.9) 0%,
                        rgba(255, 120, 60, 1) 25%,
                        rgba(255, 80, 80, 0.9) 50%,
                        rgba(255, 120, 60, 1) 75%,
                        rgba(255, 80, 80, 0.9) 100%
                    ) !important;
                    background-size: 400% 100% !important;
                    animation: weapon-icon-border-scan 4s linear infinite !important;
                    -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0) !important;
                    mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0) !important;
                    -webkit-mask-composite: xor !important;
                    mask-composite: exclude !important;
                    padding: var(--icon-ring-w) !important;
                }
                .top-rail.skills.weapon-mode-on .skill-icon.has-weapon-available:not(.weapon-bar-toggle)::before {
                    animation: weapon-icon-pulse 3s ease-in-out infinite !important;
                }
                /* Skill ativa com arma - mais intenso */
                .top-rail.skills.weapon-mode-on .skill-icon.has-weapon-available:not(.weapon-bar-toggle).active::after {
                    background: linear-gradient(90deg,
                        rgba(255, 87, 34, 1) 0%,
                        rgba(255, 140, 60, 1) 25%,
                        rgba(255, 87, 34, 1) 50%,
                        rgba(255, 140, 60, 1) 75%,
                        rgba(255, 87, 34, 1) 100%
                    ) !important;
                }
                .top-rail.skills.weapon-mode-on .skill-icon.has-weapon-available:not(.weapon-bar-toggle).active::before {
                    box-shadow: 0 0 14px 4px rgba(255, 87, 34, 0.4), 0 0 0 4px rgba(255, 87, 34, 0.3) !important;
                    animation: weapon-icon-pulse 2.5s ease-in-out infinite !important;
                }
                .skill-icon .weapon-indicator {
                    display: none;
                }
                /* Variáveis de cor vermelha para skill ativa com arma equipada */
                .skill-icon.weapon-equipped {
                    --icon-active: #FF6B6B;
                    --icon-active-ring: rgba(255, 100, 100, 0.6);
                    --icon-active-glow: rgba(255, 90, 90, 0.45);
                }
                 /* Badge de arma no canto inferior direito */
                 /* Badge de arma no canto inferior direito */
                 .skill-icon.weapon-equipped::after {
                 .skill-icon .weapon-badge {
                    content: '';
                     position: absolute;
                     position: absolute;
                     bottom: 4px;
                     bottom: 3px;
                     right: 4px;
                     right: 3px;
                     width: 14px;
                     width: 16px;
                     height: 14px;
                     height: 16px;
                     background: var(--weapon-badge-url) center/contain no-repeat;
                     background: var(--weapon-badge-url) center/contain no-repeat;
                     filter: drop-shadow(0 0 4px rgba(74, 158, 255, 0.7));
                     filter: drop-shadow(0 1px 2px rgba(0,0,0,0.6));
                     pointer-events: none;
                     pointer-events: none;
                     z-index: 5;
                     z-index: 10;
                     border-radius: 2px;
                     border-radius: 3px;
                }
                     display: none;
                /* Desativar círculo amarelo de ativo quando tem arma */
                .skill-icon.weapon-equipped::before {
                     display: none !important;
                }
                /* Estilo limpo com outline azul ao invés de shadow amarelo serrilhado */
                .skill-icon.weapon-equipped {
                    position: relative;
                    outline: 2px solid rgba(74, 158, 255, 0.6);
                    outline-offset: -2px;
                 }
                 }
                /* Quando ativo E com arma, usar efeito azul */
                 .skill-icon.weapon-equipped .weapon-badge {
                 .skill-icon.weapon-equipped.active {
                     display: block;
                     outline-color: rgba(74, 158, 255, 0.9);
                    box-shadow: 0 0 10px rgba(74, 158, 255, 0.4);
                 }
                 }
             `.replace(/\s+/g, ' ').trim();
             `.replace(/\s+/g, ' ').trim();
Linha 306: Linha 700:
         })();
         })();
         function applyWeaponBadge(el, weaponData, equipped) {
         function applyWeaponBadge(el, weaponData, equipped) {
            // Encontrar ou criar o badge
            let badge = el.querySelector('.weapon-badge');
            if (!badge) {
                badge = document.createElement('div');
                badge.className = 'weapon-badge';
                el.appendChild(badge);
            }
             if (equipped && weaponData) {
             if (equipped && weaponData) {
                 el.classList.add('weapon-equipped');
                 el.classList.add('weapon-equipped');
Linha 315: Linha 717:
         } function getWeaponKey(el) {
         } function getWeaponKey(el) {
             return (el.dataset.index || '') + ':' + (el.dataset.nome || el.dataset.name || '');
             return (el.dataset.index || '') + ':' + (el.dataset.nome || el.dataset.name || '');
        } function isWeaponModeOn() {
            try {
                return localStorage.getItem('glaWeaponEnabled') === '1';
            } catch (e) {
                return false;
            }
        } function getWeaponDataForIcon(iconEl) {
            if (!iconEl || !iconEl.dataset.weapon) return null;
            try {
                return JSON.parse(iconEl.dataset.weapon);
            } catch (e) {
                return null;
            }
        } function getEffectiveSkillVideoFromIcon(iconEl) {
            const weaponOn = globalWeaponEnabled;
            const weaponData = getWeaponDataForIcon(iconEl);
            const baseVideoFile = (iconEl.dataset.videoFile || '').trim();
            const baseVideoURL = (iconEl.dataset.video || '').trim();
            console.log('[Skills DEBUG]', {
                skillName: iconEl.dataset.nome || iconEl.dataset.name,
                weaponOn,
                hasWeaponData: !!weaponData,
                weaponData: weaponData,
                baseVideoFile,
                baseVideoURL
            });
            if (weaponOn && weaponData) {
                console.log('[Skills] checking weapon video', {
                    skillName: iconEl.dataset.nome || iconEl.dataset.name,
                    hasVideo: !!(weaponData.video),
                    videoValue: weaponData.video,
                    videoTrimmed: weaponData.video ? weaponData.video.trim() : ''
                });
                if (weaponData.video && weaponData.video.trim() !== '') {
                    console.log('[Skills] video escolhido (weapon)', iconEl.dataset.nome || iconEl.dataset.name, weaponData.video);
                    return weaponData.video.trim();
                }
            }
            const result = baseVideoFile || baseVideoURL || '';
            console.log('[Skills] video escolhido (base)', iconEl.dataset.nome || iconEl.dataset.name, result);
            return result;
         } function createVideoElement(videoURL, extraAttrs = {
         } function createVideoElement(videoURL, extraAttrs = {
         }) {
         }) {
Linha 320: Linha 765:
             v.className = 'skill-video';
             v.className = 'skill-video';
             v.setAttribute('controls', '');
             v.setAttribute('controls', '');
             v.setAttribute('preload', 'metadata');
             v.setAttribute('preload', 'auto');
             v.setAttribute('playsinline', '');
             v.setAttribute('playsinline', '');
             v.style.display = 'none';
             v.style.display = 'none';
Linha 330: Linha 775:
                 v.dataset[k] = extraAttrs[k];
                 v.dataset[k] = extraAttrs[k];
             });
             });
            // Detectar formato do vídeo pela extensão
            const ext = (videoURL.split('.').pop() || '').toLowerCase().split('?')[0];
            const mimeTypes = {
                'mp4': 'video/mp4',
                'm4v': 'video/mp4',
                'webm': 'video/webm',
                'ogv': 'video/ogg',
                'ogg': 'video/ogg',
                'mov': 'video/quicktime'
            };
            const mimeType = mimeTypes[ext] || 'video/mp4';
             const src = document.createElement('source');
             const src = document.createElement('source');
             src.src = videoURL;
             src.src = videoURL;
             src.type = 'video/webm';
             src.type = mimeType;
             v.appendChild(src);
             v.appendChild(src);
            // Fallback para Safari/iOS mais antigos
            v.setAttribute('webkit-playsinline', '');
            v.setAttribute('x-webkit-airplay', 'allow');
             return v;
             return v;
         } function precreateSubskillVideos() {
         } function precreateSubskillVideos() {
Linha 345: Linha 807:
                     const parentIdx = parentIcon.dataset.index || '';
                     const parentIdx = parentIcon.dataset.index || '';
                     subs.forEach(s => {
                     subs.forEach(s => {
                        if (!s.video) return;
                         const subName = (s.name || s.n || '').trim();
                         const subName = (s.name || s.n || '').trim();
                         const key = `sub:${parentIdx}:${subName}`;
                         // Vídeo normal da subskill
                        if (subskillVideosCache.has(key)) return;
                        if (s.video && s.video.trim() !== '' && s.video !== 'Nada.png' && !s.video.toLowerCase().includes('nada.png')) {
                        const videoURL = normalizeFileURL(s.video);
                            const key = `sub:${parentIdx}:${subName}`;
                         if (!videoURL) return;
                            if (subskillVideosCache.has(key)) return;
                        const v = createVideoElement(videoURL, {
                            const videoURL = normalizeFileURL(s.video);
                            sub: '1', parentIndex: parentIdx, subName: subName
                            if (videoURL && videoURL.trim() !== '') {
                        });
                                const v = createVideoElement(videoURL, {
                        videoBox.appendChild(v);
                                    sub: '1', parentIndex: parentIdx, subName: subName
                        subskillVideosCache.set(key, v);
                                });
                                v.setAttribute('preload', 'auto');
                                videoBox.appendChild(v);
                                subskillVideosCache.set(key, v);
                                // Força carregamento apenas se ainda não estiver carregando
                                if (v.readyState === 0 || v.readyState === 1) {
                                    v.load();
                                }
                            }
                        }
                        // Vídeo de weapon da subskill
                         if (s.weapon && typeof s.weapon === 'object' && s.weapon.video && s.weapon.video.trim() !== '' && s.weapon.video !== 'Nada.png' && !s.weapon.video.toLowerCase().includes('nada.png')) {
                            const weaponKey = `sub:${parentIdx}:${subName}:weapon`;
                            if (subskillVideosCache.has(weaponKey)) return;
                            const weaponVideoURL = normalizeFileURL(s.weapon.video);
                            if (weaponVideoURL && weaponVideoURL.trim() !== '') {
                                const v = createVideoElement(weaponVideoURL, {
                                    sub: '1', parentIndex: parentIdx, subName: subName, weapon: '1'
                                });
                                v.setAttribute('preload', 'auto');
                                videoBox.appendChild(v);
                                subskillVideosCache.set(weaponKey, v);
                                // Força carregamento apenas se ainda não estiver carregando
                                if (v.readyState === 0 || v.readyState === 1) {
                                    v.load();
                                }
                            }
                        }
                     });
                     });
                 } catch (e) {
                 } catch (e) {
                 }
                 }
             });
             });
         } setTimeout(precreateSubskillVideos, 500);
         } setTimeout(precreateSubskillVideos, 100);
         if (iconItems.length && videoBox) {
         if (iconItems.length && videoBox) {
             iconItems.forEach(el => {
             iconItems.forEach(el => {
                const src = (el.dataset.video || '').trim();
                 const idx = el.dataset.index || '';
                 const idx = el.dataset.index || '';
                 if (!src || videosCache.has(idx)) return;
                // Vídeo normal
                totalVideos++;
                const src = (el.dataset.videoFile || el.dataset.video || '').trim();
                const v = createVideoElement(src, {
                 if (src && src !== 'Nada.png' && !src.toLowerCase().includes('nada.png') && !videosCache.has(idx)) {
                    index: idx
                    totalVideos++;
                });
                    const videoURL = normalizeFileURL(src);
                v.style.maxWidth = '100%';
                    if (videoURL) {
                v.addEventListener('canplaythrough', () => {
                        const v = createVideoElement(videoURL, {
                    loadedVideos++;
                            index: idx
                    if (!userHasInteracted && loadedVideos === 1) {
                        });
                        try {
                        v.setAttribute('preload', 'auto');
                            v.pause();
                        v.style.maxWidth = '100%';
                            v.currentTime = 0;
                        v.addEventListener('canplaythrough', () => {
                        } catch (e) {
                            loadedVideos++;
                            if (!userHasInteracted && loadedVideos === 1) {
                                try {
                                    v.pause();
                                    v.currentTime = 0;
                                } catch (e) {
                                }
                            } if (loadedVideos === totalVideos) autoplay = true;
                        }, {
                            once: true
                        });
                        v.addEventListener('error', () => {
                            loadedVideos++;
                            if (loadedVideos === totalVideos) autoplay = true;
                        }, {
                            once: true
                        });
                        videoBox.appendChild(v);
                        videosCache.set(idx, v);
                        // Força carregamento
                        v.load();
                    }
                }
                // Vídeo de weapon
                if (el.dataset.weapon) {
                    try {
                        const weaponData = JSON.parse(el.dataset.weapon);
                        if (weaponData && weaponData.video && weaponData.video.trim() !== '' && weaponData.video !== 'Nada.png' && !weaponData.video.toLowerCase().includes('nada.png')) {
                            const weaponKey = `weapon:${idx}:${(el.dataset.nome || el.dataset.name || '').trim()}`;
                            if (!videosCache.has(weaponKey)) {
                                totalVideos++;
                                const weaponVideoURL = normalizeFileURL(weaponData.video);
                                if (weaponVideoURL) {
                                    const v = createVideoElement(weaponVideoURL, {
                                        index: idx, weapon: '1'
                                    });
                                    v.setAttribute('preload', 'auto');
                                    v.style.maxWidth = '100%';
                                    v.addEventListener('canplaythrough', () => {
                                        loadedVideos++;
                                        if (loadedVideos === totalVideos) autoplay = true;
                                    }, {
                                        once: true
                                    });
                                    v.addEventListener('error', () => {
                                        loadedVideos++;
                                        if (loadedVideos === totalVideos) autoplay = true;
                                    }, {
                                        once: true
                                    });
                                    videoBox.appendChild(v);
                                    videosCache.set(weaponKey, v);
                                    // Força carregamento
                                    v.load();
                                }
                            }
                         }
                         }
                     } if (loadedVideos === totalVideos) autoplay = true;
                     } catch (e) {
                }, {
                     }
                     once: true
                 }
                });
                v.addEventListener('error', () => {
                    loadedVideos++;
                    if (loadedVideos === totalVideos) autoplay = true;
                }, {
                    once: true
                 });
                videoBox.appendChild(v);
                videosCache.set(idx, v);
             });
             });
         } function wireTooltipsForNewIcons() {
         } function wireTooltipsForNewIcons() {
Linha 397: Linha 930:
             let lockUntil2 = 0;
             let lockUntil2 = 0;
             Array.from(document.querySelectorAll('.icon-bar .skill-icon')).forEach(icon => {
             Array.from(document.querySelectorAll('.icon-bar .skill-icon')).forEach(icon => {
                if (icon.dataset.weaponToggle === '1' || icon.classList.contains('weapon-bar-toggle')) return;
                 if (icon.dataset.tipwired) return;
                 if (icon.dataset.tipwired) return;
                 icon.dataset.tipwired = '1';
                 icon.dataset.tipwired = '1';
Linha 447: Linha 981:
         } function showVideoForIcon(el) {
         } function showVideoForIcon(el) {
             userHasInteracted = true;
             userHasInteracted = true;
             if (videoBox) {
             if (!videoBox) return;
                 Array.from(videoBox.querySelectorAll('video.skill-video')).forEach(v => {
            const effectiveVideo = getEffectiveSkillVideoFromIcon(el);
                    try {
            if (!effectiveVideo || effectiveVideo.trim() === '') {
                        v.pause();
                videoBox.style.display = 'none';
                    } catch (e) {
                return;
                    } v.style.display = 'none';
            }
                });
            const videoURL = normalizeFileURL(effectiveVideo);
             } if (window.__subskills) window.__subskills.hideAll?.(videoBox);
            if (!videoURL || videoURL.trim() === '') {
                 videoBox.style.display = 'none';
                return;
            }
            Array.from(videoBox.querySelectorAll('video.skill-video')).forEach(v => {
                try {
                    v.pause();
                } catch (e) {
                }
                v.style.display = 'none';
            });
             if (window.__subskills) window.__subskills.hideAll?.(videoBox);
             const hasIdx = !!el.dataset.index;
             const hasIdx = !!el.dataset.index;
             const hasVideo = !!(el.dataset.video && el.dataset.video.trim() !== '');
             const weaponOn = globalWeaponEnabled;
             if (!videoBox || !hasVideo) {
            const weaponData = getWeaponDataForIcon(el);
                 if (videoBox) {
            const isWeaponVideo = weaponOn && weaponData && weaponData.video && weaponData.video.trim() !== '';
                    videoBox.style.display = 'none';
 
                } return;
             console.log('[Skills] showVideoForIcon chamado', {
             } if (hasIdx && videosCache.has(el.dataset.index)) {
                skillName: el.dataset.nome || el.dataset.name,
                weaponOn,
                isWeaponVideo,
                 effectiveVideo: getEffectiveSkillVideoFromIcon(el)
            });
            const videoKey = isWeaponVideo ? `weapon:${getWeaponKey(el)}` : (el.dataset.index || '');
             if (hasIdx && !isWeaponVideo && videosCache.has(el.dataset.index)) {
                 const v = videosCache.get(el.dataset.index);
                 const v = videosCache.get(el.dataset.index);
                 videoBox.style.display = 'block';
                 videoBox.style.display = 'block';
Linha 468: Linha 1 019:
                     v.currentTime = 0;
                     v.currentTime = 0;
                 } catch (e) {
                 } catch (e) {
                 } const suppress = document.body.dataset.suppressSkillPlay === '1';
                 }
                const suppress = document.body.dataset.suppressSkillPlay === '1';
                 if (!suppress) {
                 if (!suppress) {
                     v.play().catch(() => {
                     v.play().catch(() => {
Linha 477: Linha 1 029:
                     } catch (e) {
                     } catch (e) {
                     }
                     }
                } return;
            } const subName = el.dataset.subName || el.dataset.nome || el.dataset.name || '';
            const parentIdx = el.dataset.parentIndex || '';
            if (subName && parentIdx) {
                const subKey = `sub:${parentIdx}:${subName}`;
                if (subskillVideosCache.has(subKey)) {
                    const v = subskillVideosCache.get(subKey);
                    videoBox.style.display = 'block';
                    v.style.display = 'block';
                    try {
                        v.currentTime = 0;
                    } catch (e) {
                    } const suppress = document.body.dataset.suppressSkillPlay === '1';
                    if (!suppress) {
                        v.play().catch(() => {
                        });
                    } else {
                        try {
                            v.pause();
                        } catch (e) {
                        }
                    } return;
                 }
                 }
             } let v = nestedVideoElByIcon.get(el);
                return;
             }
            let v = null;
            if (isWeaponVideo) {
                v = videoBox.querySelector(`video[data-weapon-key="${videoKey}"]`);
            } else {
                v = nestedVideoElByIcon.get(el);
            }
             if (!v) {
             if (!v) {
                 v = createVideoElement(el.dataset.video, {
                 v = createVideoElement(videoURL, isWeaponVideo ? {
                 });
                    weaponKey: videoKey
                 videoBox.appendChild(v);
                 } : {});
                 nestedVideoElByIcon.set(el, v);
                 if (isWeaponVideo) {
             } videoBox.style.display = 'block';
                    videoBox.appendChild(v);
                 } else {
                    videoBox.appendChild(v);
                    nestedVideoElByIcon.set(el, v);
                }
             } else {
                const src = v.querySelector('source');
                if (src && src.src !== videoURL) {
                    src.src = videoURL;
                    v.load();
                }
            }
            videoBox.style.display = 'block';
             v.style.display = 'block';
             v.style.display = 'block';
             try {
             try {
                 v.currentTime = 0;
                 v.currentTime = 0;
             } catch (e) {
             } catch (e) {
             } const suppress = document.body.dataset.suppressSkillPlay === '1';
             }
            const suppress = document.body.dataset.suppressSkillPlay === '1';
             if (!suppress) {
             if (!suppress) {
                 v.play().catch(() => {
                 v.play().catch(() => {
Linha 554: Linha 1 104:
             };
             };
             const baseDesc = baseDescPack[lang] || baseDescPack.pt || baseDescPack.en || baseDescPack.es || baseDescPack.pl || el.dataset.desc || '';
             const baseDesc = baseDescPack[lang] || baseDescPack.pt || baseDescPack.en || baseDescPack.es || baseDescPack.pl || el.dataset.desc || '';
             let weaponDescPack = {
            // Aceita tanto desc_i18n quanto desc para compatibilidade
            };
             let weaponDescPack = {};
             if (weaponData && weaponData.desc_i18n) {
             if (weaponData) {
                weaponDescPack = weaponData.desc_i18n;
                if (weaponData.desc_i18n) {
            } else if (weaponData) {
                    weaponDescPack = weaponData.desc_i18n;
                 weaponDescPack = {
                } else if (weaponData.desc) {
                    pt: weaponData.descPt || '', en: weaponData.descEn || '', es: weaponData.descEs || '', pl: weaponData.descPl || ''
                    weaponDescPack = weaponData.desc;
                 };
                 } else {
             } const weaponDesc = weaponDescPack[lang] || weaponDescPack.pt || weaponDescPack.en || weaponDescPack.es || weaponDescPack.pl || '';
                    weaponDescPack = {
                        pt: weaponData.descPt || '', en: weaponData.descEn || '', es: weaponData.descEs || '', pl: weaponData.descPl || ''
                    };
                 }
             }
            const weaponDesc = weaponDescPack[lang] || weaponDescPack.pt || weaponDescPack.en || weaponDescPack.es || weaponDescPack.pl || '';
             const chosenDesc = (weaponEquipped && weaponDesc) ? weaponDesc : baseDesc;
             const chosenDesc = (weaponEquipped && weaponDesc) ? weaponDesc : baseDesc;
             const descHtml = chosenDesc.replace(/'''(.*?)'''/g, '<b>$1</b>');
             const descHtml = chosenDesc.replace(/'''(.*?)'''/g, '<b>$1</b>');
             let attrsHTML = '';
             let attrsHTML = '';
             if (weaponEquipped && weaponData) {
             if (weaponEquipped && weaponData) {
                 const wPve = (weaponData.powerpve || '').toString().trim();
                // Usa valores do weapon, mas só se existirem (não herda da skill base)
                 const wPvp = (weaponData.powerpvp || '').toString().trim();
                 const wPve = (weaponData.powerpve !== undefined && weaponData.powerpve !== null && weaponData.powerpve !== '') ? weaponData.powerpve.toString().trim() : '';
                 const wEnergy = (weaponData.energy || '').toString().trim();
                 const wPvp = (weaponData.powerpvp !== undefined && weaponData.powerpvp !== null && weaponData.powerpvp !== '') ? weaponData.powerpvp.toString().trim() : '';
                 const wCd = (weaponData.cooldown || '').toString().trim();
                 const wEnergy = (weaponData.energy !== undefined && weaponData.energy !== null && weaponData.energy !== '') ? weaponData.energy.toString().trim() : '';
                 const wCd = (weaponData.cooldown !== undefined && weaponData.cooldown !== null && weaponData.cooldown !== '') ? weaponData.cooldown.toString().trim() : '';
                 const weaponAttrs = [wPve, wPvp, wEnergy, wCd].join(',');
                 const weaponAttrs = [wPve, wPvp, wEnergy, wCd].join(',');
                console.log('[Skills] weaponAttrs string:', weaponAttrs, { wPve, wPvp, wEnergy, wCd, weaponData });
                 attrsHTML = renderAttributes(weaponAttrs);
                 attrsHTML = renderAttributes(weaponAttrs);
             } else {
             } else {
Linha 583: Linha 1 140:
                 }
                 }
             } if (descBox) {
             } if (descBox) {
                 descBox.innerHTML = `<div class="skill-title"><h3>${name}</h3></div>${level ? `<div class="skill-level-line"><span class="attr-label">$ {
                 descBox.innerHTML = `<div class="skill-title"><h3>${name}</h3></div>${level ? `<div class="skill-level-line"><span class="attr-label">${L.level} ${level}</span></div>` : ''}${attrsHTML}<div class="desc">${descHtml}</div>`;
                L.level
            } $ {
                level
            }</span></div>` : ''}${attrsHTML}<div class="desc">${descHtml}</div>`;
             } if (hasWeapon) {
             } if (hasWeapon) {
                 applyWeaponBadge(el, weaponData, weaponEquipped);
                 applyWeaponBadge(el, weaponData, weaponEquipped);
Linha 602: Linha 1 155:
             if (!autoplay && loadedVideos > 0) autoplay = true;
             if (!autoplay && loadedVideos > 0) autoplay = true;
             window.__lastActiveSkillIcon = el;
             window.__lastActiveSkillIcon = el;
             if (weaponEquipped && weaponData && weaponData.video) {
             // Lógica de vídeo: usa função centralizada que já considera weapon
                const weaponVideoURL = normalizeFileURL(weaponData.video);
             showVideoForIcon(el);
                if (weaponVideoURL && videoBox) {
             const subsRaw = el.dataset.subs || el.getAttribute('data-subs');
                    Array.from(videoBox.querySelectorAll('video.skill-video')).forEach(v => {
                        try {
                            v.pause();
                        } catch (e) {
                        } v.style.display = 'none';
                    });
                    const weaponKey = `weapon:${getWeaponKey(el)}`;
                    let weaponVideo = videoBox.querySelector(`video[data-weapon-key="${weaponKey}"]`);
                    if (!weaponVideo) {
                        weaponVideo = createVideoElement(weaponVideoURL, {
                            weaponKey
                        });
                        videoBox.appendChild(weaponVideo);
                    } videoBox.style.display = 'block';
                    weaponVideo.style.display = 'block';
                    try {
                        weaponVideo.currentTime = 0;
                    } catch (e) {
                    } const suppress = document.body.dataset.suppressSkillPlay === '1';
                    if (!suppress) {
                        weaponVideo.play().catch(() => {
                        });
                    }
                }
             } else {
                showVideoForIcon(el);
             } const subsRaw = el.dataset.subs || el.getAttribute('data-subs');
             const isBack = el.dataset.back === 'true' || el.getAttribute('data-back') === 'true' || el.dataset.back === 'yes' || el.getAttribute('data-back') === 'yes' || el.dataset.back === '1' || el.getAttribute('data-back') === '1';
             const isBack = el.dataset.back === 'true' || el.getAttribute('data-back') === 'true' || el.dataset.back === 'yes' || el.getAttribute('data-back') === 'yes' || el.dataset.back === '1' || el.getAttribute('data-back') === '1';
             if (isBack && barStack.length) {
             if (isBack && barStack.length) {
Linha 650: Linha 1 176:
             const currIcons = Array.from(iconsBar.querySelectorAll('.skill-icon'));
             const currIcons = Array.from(iconsBar.querySelectorAll('.skill-icon'));
             currIcons.forEach(el => {
             currIcons.forEach(el => {
                if (el.dataset.weaponToggle === '1' || el.classList.contains('weapon-bar-toggle')) return;
                 if (el.dataset.wired) return;
                 if (el.dataset.wired) return;
                 el.dataset.wired = '1';
                 el.dataset.wired = '1';
Linha 679: Linha 1 206:
             });
             });
         } function snapshotCurrentBarItemsFromDOM() {
         } function snapshotCurrentBarItemsFromDOM() {
             return Array.from(iconsBar.querySelectorAll('.skill-icon')).map(el => {
             return Array.from(iconsBar.querySelectorAll('.skill-icon')).filter(el => el.dataset.weaponToggle !== '1').map(el => {
                 const img = el.querySelector('img');
                 const img = el.querySelector('img');
                 const iconURL = img ? img.src : '';
                 const iconURL = img ? img.src : '';
Linha 773: Linha 1 300:
             animateIconsBarEntrance();
             animateIconsBarEntrance();
             wireClicksForCurrentBar();
             wireClicksForCurrentBar();
            setupWeaponBarToggle(hasWeaponSkillAvailable);
             const b = ensureBackButton();
             const b = ensureBackButton();
             if (b) b.classList.add('peek');
             if (b) b.classList.add('peek');
Linha 808: Linha 1 336:
                     animateIconsBarEntrance();
                     animateIconsBarEntrance();
                     wireClicksForCurrentBar();
                     wireClicksForCurrentBar();
                    setupWeaponBarToggle(hasWeaponSkillAvailable);
                     const cachedBtn = ensureBackButton();
                     const cachedBtn = ensureBackButton();
                     if (cachedBtn) cachedBtn.classList.add('peek');
                     if (cachedBtn) cachedBtn.classList.add('peek');
Linha 818: Linha 1 347:
                 cooldown: 'Recarga', energy_gain: 'Ganho de energia', energy_cost: 'Custo de energia', power: 'Poder', power_pvp: 'Poder PvP', level: 'Nível'
                 cooldown: 'Recarga', energy_gain: 'Ganho de energia', energy_cost: 'Custo de energia', power: 'Poder', power_pvp: 'Poder PvP', level: 'Nível'
             };
             };
             const items = (subs || []).map(s => {
            const hydratedSubs = inheritSubskillTree(subs, mainSkillsMeta);
             const items = (hydratedSubs || []).filter(s => {
                // Filtra só se não tem nada útil
                const hasName = (s.name || s.n || '').trim() !== '';
                const hasIcon = (s.icon || '').trim() !== '' && s.icon !== 'Nada.png';
                const hasRef = (s.refS || s.refM || '').toString().trim() !== '';
                return hasName || hasIcon || hasRef;
            }).map(s => {
                 const name = (s.name || s.n || '').trim();
                 const name = (s.name || s.n || '').trim();
                 const desc = chooseDescFrom(s).replace(/'''(.*?)'''/g, '<b>$1</b>');
                 const desc = chooseDescFrom(s).replace(/'''(.*?)'''/g, '<b>$1</b>');
                 const attrsHTML = renderSubAttributesFromObj(s, L);
                 const attrsHTML = renderSubAttributesFromObj(s, L);
                 return {
                 return {
                     name, level: (s.level || '').toString().trim(), desc, descPt: (s.descPt || (s.desc_i18n && s.desc_i18n.pt) || ''), descEn: (s.descEn || (s.desc_i18n && s.desc_i18n.en) || ''), descEs: (s.descEs || (s.desc_i18n && s.desc_i18n.es) || ''), descPl: (s.descPl || (s.desc_i18n && s.desc_i18n.pl) || ''), attrs: '', icon: (s.icon || 'Nada.png'), iconURL: filePathURL(s.icon || 'Nada.png'), video: s.video ? filePathURL(s.video) : '', subs: Array.isArray(s.subs) ? s.subs : null, subattrs: s, flags: Array.isArray(s.flags) ? s.flags : null, back: (s.back === true || s.back === 'true' || s.back === 'yes' || s.back === '1') ? 'true' : null
                     name, level: (s.level || '').toString().trim(), desc, descPt: (s.descPt || (s.desc_i18n && s.desc_i18n.pt) || ''), descEn: (s.descEn || (s.desc_i18n && s.desc_i18n.en) || ''), descEs: (s.descEs || (s.desc_i18n && s.desc_i18n.es) || ''), descPl: (s.descPl || (s.desc_i18n && s.desc_i18n.pl) || ''), attrs: '', icon: (s.icon || 'Nada.png'), iconURL: filePathURL(s.icon || 'Nada.png'), video: s.video ? filePathURL(s.video) : '', subs: Array.isArray(s.subs) ? s.subs : null, subattrs: s, flags: Array.isArray(s.flags) ? s.flags : null, back: (s.back === true || s.back === 'true' || s.back === 'yes' || s.back === '1') ? 'true' : null, weapon: s.weapon || null
                 };
                 };
             });
             });
Linha 847: Linha 1 383:
                 if (it.flags) node.dataset.flags = JSON.stringify(it.flags);
                 if (it.flags) node.dataset.flags = JSON.stringify(it.flags);
                 if (it.back) node.dataset.back = it.back;
                 if (it.back) node.dataset.back = it.back;
                if (it.weapon) {
                    try {
                        node.dataset.weapon = JSON.stringify(it.weapon);
                    } catch (e) {
                        console.error('[Skills] Erro ao serializar weapon de subskill', it.name, e);
                    }
                }
                 const img = document.createElement('img');
                 const img = document.createElement('img');
                 img.alt = '';
                 img.alt = '';
Linha 858: Linha 1 401:
             animateIconsBarEntrance();
             animateIconsBarEntrance();
             wireClicksForCurrentBar();
             wireClicksForCurrentBar();
            setupWeaponBarToggle(hasWeaponSkillAvailable);
             const b2 = ensureBackButton();
             const b2 = ensureBackButton();
             if (b2) b2.classList.add('peek');
             if (b2) b2.classList.add('peek');
Linha 938: Linha 1 482:
             };
             };
             Array.from(document.querySelectorAll('.icon-bar .skill-icon')).forEach(icon => {
             Array.from(document.querySelectorAll('.icon-bar .skill-icon')).forEach(icon => {
                if (icon.dataset.weaponToggle === '1' || icon.classList.contains('weapon-bar-toggle')) return;
                 if (icon.dataset.tipwired) return;
                 if (icon.dataset.tipwired) return;
                 icon.dataset.tipwired = '1';
                 icon.dataset.tipwired = '1';
Linha 1 191: Linha 1 736:
             };
             };
             const vals = (str || '').split(',').map(v => v.trim());
             const vals = (str || '').split(',').map(v => v.trim());
             const pve = parseInt(vals[0], 10);
            // Processa valores, tratando strings vazias como NaN
             const pvp = parseInt(vals[1], 10);
            // IMPORTANTE: ordem fixa é [powerpve, powerpvp, energy, cooldown]
             const ene = parseInt(vals[2], 10);
             const pve = (vals[0] && vals[0] !== '') ? parseFloat(vals[0]) : NaN;
             const cd = parseInt(vals[3], 10);
             const pvp = (vals[1] && vals[1] !== '') ? parseFloat(vals[1]) : NaN;
             const ene = (vals[2] && vals[2] !== '') ? parseFloat(vals[2]) : NaN;
             const cd = (vals[3] && vals[3] !== '') ? parseFloat(vals[3]) : NaN;
            // Debug: log se houver problema na ordem
            if (str && str.includes(',') && !isNaN(cd) && !isNaN(pvp) && cd === pvp) {
                console.warn('[Skills] renderAttributes: possível problema na ordem dos atributos', { str, vals, pve, pvp, ene, cd });
            }
             const rows = [];
             const rows = [];
            // Ordem de exibição: cooldown, energy, power, power_pvp
             if (!isNaN(cd)) rows.push([L.cooldown, cd]);
             if (!isNaN(cd)) rows.push([L.cooldown, cd]);
             if (!isNaN(ene) && ene !== 0) {
             if (!isNaN(ene) && ene !== 0) {
                 const label = ene > 0 ? L.energy_gain : L.energy_cost;
                 const label = ene > 0 ? L.energy_gain : L.energy_cost;
                 rows.push([label, Math.abs(ene)]);
                 rows.push([label, Math.abs(ene)]);
             } if (!isNaN(pve)) rows.push([L.power, pve]);
             }
            if (!isNaN(pve)) rows.push([L.power, pve]);
             if (!isNaN(pvp)) rows.push([L.power_pvp, pvp]);
             if (!isNaN(pvp)) rows.push([L.power_pvp, pvp]);
            // Debug: log se houver valores suspeitos
            if (str && str.includes(',')) {
                console.log('[Skills] renderAttributes processed', {
                    str,
                    vals: vals.slice(0, 4),
                    parsed: { pve, pvp, ene, cd },
                    rows: rows.map(r => r[0])
                });
            }
             if (!rows.length) return '';
             if (!rows.length) return '';
             const html = rows.map(([label, value]) => `<div class="attr-row"><span class="attr-label">${label}</span><span class="attr-value">${value}</span></div>`).join('');
             const html = rows.map(([label, value]) => `<div class="attr-row"><span class="attr-label">${label}</span><span class="attr-value">${value}</span></div>`).join('');
Linha 1 232: Linha 1 794:
         } setTimeout(() => {
         } setTimeout(() => {
             Array.from(document.querySelectorAll('.skill-icon')).forEach(el => {
             Array.from(document.querySelectorAll('.skill-icon')).forEach(el => {
                console.log('icon', el.dataset.index, 'data-video=', el.dataset.video);
             });
             });
             videosCache.forEach((v, idx) => {
             videosCache.forEach((v, idx) => {
                 const src = v.querySelector('source') ? v.querySelector('source').src : v.src;
                 const src = v.querySelector('source') ? v.querySelector('source').src : v.src;
                console.log('video element', idx, 'src=', src, 'readyState=', v.readyState);
                 v.addEventListener('error', (ev) => {
                 v.addEventListener('error', (ev) => {
                    console.error('VIDEO ERROR idx=', idx, 'src=', src, 'error=', v.error);
                 });
                 });
                 v.addEventListener('loadedmetadata', () => {
                 v.addEventListener('loadedmetadata', () => {
                    console.log('loadedmetadata idx=', idx, 'dimensions=', v.videoWidth, 'x', v.videoHeight);
                 });
                 });
             });
             });

Edição atual tal como às 17h47min de 5 de dezembro de 2025

<script>

   (function () {
       const $ = (s, root = document) => root.querySelector(s);
       const $$ = (s, root = document) => Array.from(root.querySelectorAll(s));
       const ensureRemoved = sel => {
           Array.from(document.querySelectorAll(sel)).forEach(n => n.remove());
       };
       const onceFlag = (el, key) => {
           if (!el) return false;
           if (el.dataset[key]) return false;
           el.dataset[key] = '1';
           return true;
       };
       const addOnce = (el, ev, fn) => {
           if (!el) return;
           const attr = `data-wired-${ev}`;
           if (el.hasAttribute(attr)) return;
           el.addEventListener(ev, fn);
           el.setAttribute(attr, '1');
       };
       const FLAG_ICON_FILES = {
           aggro: 'Enemyaggro-icon.png', bridge: 'Bridgemaker-icon.png', wall: 'Destroywall-icon.png', quickcast: 'Quickcast-icon.png'
       };
       const subBarTemplateCache = window.__skillSubBarTemplateCache || (window.__skillSubBarTemplateCache = new Map());
       const imagePreloadCache = window.__skillImagePreloadCache || (window.__skillImagePreloadCache = new Map());
       const videoPreloadCache = window.__skillVideoPreloadCache || (window.__skillVideoPreloadCache = new Set());
       const flagRowCache = window.__skillFlagRowCache || (window.__skillFlagRowCache = new Map());
       const flagIconURLCache = window.__skillFlagIconURLCache || (window.__skillFlagIconURLCache = new Map());
       function filePathURL(fileName) {
           const f = encodeURIComponent((fileName || 'Nada.png').replace(/^Arquivo:|^File:/, ));
           const base = (window.mw && mw.util && typeof mw.util.wikiScript === 'function') ? mw.util.wikiScript() : (window.mw && window.mw.config ? (mw.config.get('wgScript') || '/index.php') : '/index.php');
           return `${base}?title=Especial:FilePath/${f}`;
       } function slugify(s) {
           if (!s) return ;
           return String(s).toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g, ).replace(/[^\w\s-]/g, ).replace(/[\s:/\-]+/g, '-').replace(/^-+|-+$/g, ).replace(/-+/g, '-');
       } window.__skillSlugify = slugify;
       function getLangKey() {
           const skillsRoot = document.getElementById('skills');
           const raw = (document.documentElement.lang || skillsRoot?.dataset.i18nDefault || 'pt').toLowerCase();
           return raw === 'pt-br' ? 'pt' : (raw.split('-')[0] || 'pt');
       } function chooseDescFrom(obj) {
           const lang = getLangKey();
           // Aceita tanto desc_i18n quanto desc para compatibilidade
           const pack = obj.desc_i18n || obj.desc || {
               pt: obj.descPt, en: obj.descEn, es: obj.descEs, pl: obj.descPl
           };
           return (pack && (pack[lang] || pack.pt || pack.en || pack.es || pack.pl)) || ;
       } function renderSubAttributesFromObj(s, L) {

const chip = (label, val) => (val ? `

${label}${val}

` : );

           const pve = (s.powerpve || ).toString().trim();
           const pvp = (s.powerpvp || ).toString().trim();
           const en = (s.energy || ).toString().trim();
           const cd = (s.cooldown || ).toString().trim();
           const rows = [cd ? chip(L.cooldown, cd) : , en ? chip((en.startsWith('-') ? L.energy_cost : L.energy_gain), en.startsWith('-') ? en.replace(/^-/, ) : en.replace(/^\+?/, )) : , pve ? chip(L.power, pve) : , pvp ? chip(L.power_pvp, pvp) : ,].filter(Boolean);

return rows.length ? `

${rows.join()}

` : ;

       } function getFlagIconURL(key) {
           if (!FLAG_ICON_FILES[key]) return ;
           if (!flagIconURLCache.has(key)) {
               flagIconURLCache.set(key, filePathURL(FLAG_ICON_FILES[key]));
           } return flagIconURLCache.get(key);
       } function renderFlagsRow(flags) {
           const arr = (flags || []).filter(Boolean);
           if (!arr.length) return ;
           const cacheKey = arr.join('|');
           if (flagRowCache.has(cacheKey)) {
               return flagRowCache.get(cacheKey);
           } const items = arr.map(k => {
               const url = getFlagIconURL(k);
               return url ? `<img class="skill-flag" data-flag="${k}" alt="" src="${url}">` : ;
           }).join();

const html = items ? `

${items}

` : ;

           if (html) flagRowCache.set(cacheKey, html);
           return html;
       } function applyFlagTooltips(container) {
           const skillsRoot = document.getElementById('skills');
           if (!skillsRoot) return;
           let pack = {
           };
           try {
               pack = JSON.parse(skillsRoot.dataset.i18nFlags || '{}');
           } catch (e) {
           } const lang = getLangKey();
           const dict = pack[lang] || pack.pt || {
           };
           const flags = container.querySelectorAll('.skill-flags .skill-flag[data-flag]');
           const tooltip = window.__globalSkillTooltip;
           if (!tooltip) return;
           flags.forEach(el => {
               const key = el.getAttribute('data-flag');
               const tip = (dict && dict[key]) || ;
               if (!tip) return;
               if (el.dataset.flagTipWired) return;
               el.dataset.flagTipWired = '1';
               el.setAttribute('aria-label', tip);
               if (el.hasAttribute('title')) el.removeAttribute('title');
               el.addEventListener('mouseenter', () => {
                   const tipEl = document.querySelector('.skill-tooltip');
                   if (tipEl) tipEl.classList.add('flag-tooltip');
                   tooltip.show(el, tip);
               });
               el.addEventListener('mousemove', () => {
                   if (performance.now() >= tooltip.lockUntil.value) {
                       tooltip.measureAndPos(el);
                   }
               });
               el.addEventListener('click', () => {
                   tooltip.lockUntil.value = performance.now() + 240;
                   tooltip.measureAndPos(el);
               });
               el.addEventListener('mouseleave', () => {
                   const tipEl = document.querySelector('.skill-tooltip');
                   if (tipEl) tipEl.classList.remove('flag-tooltip');
                   tooltip.hide();
               });
           });
       }
       // ====== Skill/Subskill inheritance helpers ======
       const mainSkillsMeta = {
           byIndex: new Map(),
           byName: new Map(),
           ready: false
       };
       function normalizeFileURL(raw, fallback = ) {
           if (!raw) return fallback;
           const val = String(raw).trim();
           if (!val) return fallback;
           if (/^(https?:)?\/\//i.test(val) || val.startsWith('data:') || val.includes('Especial:FilePath/')) {
               return val;
           } return filePathURL(val);
       }
       function extractFileNameFromURL(url) {
           if (!url) return ;
           const match = String(url).match(/(?:FilePath\/)([^&?]+)/i);
           return match ? decodeURIComponent(match[1]) : ;
       }
       function parseAttrString(raw) {
           const parts = (raw || ).split(',').map(v => v.trim());
           const safe = idx => {
               const val = parts[idx] || ;
               return (val && val !== '-') ? val : ;
           };
           return {
               powerpve: safe(0),
               powerpvp: safe(1),
               energy: safe(2),
               cooldown: safe(3)
           };
       }
       function hasText(value) {
           return typeof value === 'string' ? value.trim() !==  : value !== undefined && value !== null;
       }
       function pickFilled(current, fallback) {
           if (current === 0 || current === '0') return current;
           if (!hasText(current)) return fallback;
           return current;
       }
       function buildMainSkillsMeta(nodes) {
           if (mainSkillsMeta.ready) {
               return mainSkillsMeta;
           }
           (nodes || []).forEach(icon => {
               const index = (icon.dataset.index || ).trim();
               if (!index) return;
               const name = (icon.dataset.nome || icon.dataset.name || ).trim();
               const attrs = parseAttrString(icon.dataset.atr || );
               let iconFile = (icon.dataset.iconFile || ).trim();
               if (!iconFile) {
                   const imgSrc = icon.querySelector('img')?.src || ;
                   const iconMatch = imgSrc.match(/(?:FilePath|images)\/([^\/?]+)$/);
                   iconFile = iconMatch ? decodeURIComponent(iconMatch[1]) : ;
               }
               let videoFile = (icon.dataset.videoFile || ).trim();
               if (!videoFile) {
                   videoFile = extractFileNameFromURL(icon.dataset.video || );
               }
               const meta = {
                   index,
                   name,
                   icon: iconFile || 'Nada.png',
                   level: icon.dataset.level || ,
                   video: videoFile || ,
                   powerpve: attrs.powerpve || ,
                   powerpvp: attrs.powerpvp || ,
                   energy: attrs.energy || ,
                   cooldown: attrs.cooldown || ,
                   desc: icon.dataset.desc || ,
                   descPt: icon.dataset.descPt || ,
                   descEn: icon.dataset.descEn || ,
                   descEs: icon.dataset.descEs || ,
                   descPl: icon.dataset.descPl || 
               };
               mainSkillsMeta.byIndex.set(index, meta);
               mainSkillsMeta.byIndex.set(parseInt(index, 10), meta);
               if (name) {
                   mainSkillsMeta.byName.set(name, meta);
               }
           });
           mainSkillsMeta.ready = true;
           return mainSkillsMeta;
       }
       function inheritSubskillFromMain(sub, meta) {
           if (!sub || !meta) return sub;
           // Suporta refS (novo) e refM (legado)
           const refS = ((sub.refS || sub.S || sub.s || ) + ).trim();
           const refIndex = ((sub.refM || sub.M || sub.m || ) + ).trim();
           let name = (sub.name || sub.n || ).trim();
           let main = null;


           // Primeiro tenta por refS
           if (refS) {
               main = meta.byIndex.get(refS) || meta.byIndex.get(parseInt(refS, 10));
           }
           // Depois por refM
           if (!main && refIndex) {
               main = meta.byIndex.get(refIndex) || meta.byIndex.get(parseInt(refIndex, 10));
           }
           // Por último pelo nome
           if (!main && name) {
               main = meta.byName.get(name);
           }
           if (!main) {
               return sub;
           }
           const hydrated = { ...sub };
           if (!name && main.name) {
               name = main.name;
           }
           hydrated.name = name || hydrated.name || main.name || ;
           hydrated.icon = pickFilled(hydrated.icon, main.icon || 'Nada.png');
           hydrated.level = pickFilled(hydrated.level, main.level || );
           hydrated.video = pickFilled(hydrated.video, main.video || );
           hydrated.powerpve = pickFilled(hydrated.powerpve, main.powerpve || );
           hydrated.powerpvp = pickFilled(hydrated.powerpvp, main.powerpvp || );
           hydrated.energy = pickFilled(hydrated.energy, main.energy || );
           hydrated.cooldown = pickFilled(hydrated.cooldown, main.cooldown || );
           if (!hasText(hydrated.descPt) && hasText(main.descPt)) hydrated.descPt = main.descPt;
           if (!hasText(hydrated.descEn) && hasText(main.descEn)) hydrated.descEn = main.descEn;
           if (!hasText(hydrated.descEs) && hasText(main.descEs)) hydrated.descEs = main.descEs;
           if (!hasText(hydrated.descPl) && hasText(main.descPl)) hydrated.descPl = main.descPl;
           if (!hasText(hydrated.desc) && hasText(main.desc)) hydrated.desc = main.desc;
           if (!hydrated.desc_i18n && (hydrated.descPt || hydrated.descEn || hydrated.descEs || hydrated.descPl)) {
               hydrated.desc_i18n = {
                   pt: hydrated.descPt || ,
                   en: hydrated.descEn || ,
                   es: hydrated.descEs || ,
                   pl: hydrated.descPl || 
               };
           }
           return hydrated;
       }
       function inheritSubskillTree(subs, meta) {
           if (!Array.isArray(subs)) return [];
           return subs.map(sub => {
               const hydrated = inheritSubskillFromMain(sub, meta);
               if (Array.isArray(hydrated.subs)) {
                   hydrated.subs = inheritSubskillTree(hydrated.subs, meta);
               }
               return hydrated;
           });
       }
       function collectAssetsFromSubs(subs, iconsSet, videosSet, flagsSet) {
           if (!Array.isArray(subs)) return;
           subs.forEach(sub => {
               const iconURL = normalizeFileURL(sub.icon || 'Nada.png', filePathURL('Nada.png'));
               if (iconURL && iconURL !== filePathURL('Nada.png')) iconsSet.add(iconURL);
               // Vídeo normal
               if (sub.video && sub.video.trim() !==  && sub.video !== 'Nada.png' && !sub.video.toLowerCase().includes('nada.png')) {
                   const videoURL = normalizeFileURL(sub.video);
                   if (videoURL) videosSet.add(videoURL);
               }
               // Vídeo de weapon
               if (sub.weapon && typeof sub.weapon === 'object' && sub.weapon.video && sub.weapon.video.trim() !==  && sub.weapon.video !== 'Nada.png' && !sub.weapon.video.toLowerCase().includes('nada.png')) {
                   const weaponVideoURL = normalizeFileURL(sub.weapon.video);
                   if (weaponVideoURL) videosSet.add(weaponVideoURL);
               }
               if (Array.isArray(sub.flags)) {
                   sub.flags.forEach(flagKey => {
                       const url = getFlagIconURL(flagKey);
                       if (url) flagsSet.add(url);
                   });
               } if (Array.isArray(sub.subs)) {
                   collectAssetsFromSubs(sub.subs, iconsSet, videosSet, flagsSet);
               }
           });
       } function buildAssetManifest() {
           if (window.__skillAssetManifest && window.__skillAssetManifest.ready) {
               return window.__skillAssetManifest;
           } const iconsSet = new Set();
           const videosSet = new Set();
           const flagsSet = new Set();
           iconItems.forEach(el => {
               const img = el.querySelector('img');
               if (img && img.src) {
                   iconsSet.add(img.src);
               } else if (el.dataset.iconFile) {
                   const iconURL = normalizeFileURL(el.dataset.iconFile);
                   if (iconURL) iconsSet.add(iconURL);
               }
               // Vídeo normal da skill
               const videoRaw = (el.dataset.videoFile || el.dataset.video || ).trim();
               if (videoRaw && videoRaw !== 'Nada.png' && !videoRaw.toLowerCase().includes('nada.png')) {
                   const videoURL = normalizeFileURL(videoRaw);
                   if (videoURL) videosSet.add(videoURL);
               }
               // Vídeo de weapon da skill
               if (el.dataset.weapon) {
                   try {
                       const weaponData = JSON.parse(el.dataset.weapon);
                       if (weaponData && weaponData.video && weaponData.video.trim() !==  && weaponData.video !== 'Nada.png' && !weaponData.video.toLowerCase().includes('nada.png')) {
                           const weaponVideoURL = normalizeFileURL(weaponData.video);
                           if (weaponVideoURL) videosSet.add(weaponVideoURL);
                       }
                   } catch (e) {
                   }
               }
               if (el.dataset.flags) {
                   try {
                       const parsedFlags = JSON.parse(el.dataset.flags);
                       (parsedFlags || []).forEach(flagKey => {
                           const url = getFlagIconURL(flagKey);
                           if (url) flagsSet.add(url);
                       });
                   } catch (e) {
                   }
               } if (el.dataset.subs) {
                   try {
                       const subs = JSON.parse(el.dataset.subs);
                       collectAssetsFromSubs(subs, iconsSet, videosSet, flagsSet);
                   } catch (e) {
                   }
               }
           });
           Object.keys(FLAG_ICON_FILES).forEach(flagKey => {
               const url = getFlagIconURL(flagKey);
               if (url) flagsSet.add(url);
           });
           const manifest = {
               icons: iconsSet, videos: videosSet, flags: flagsSet, ready: true
           };
           window.__skillAssetManifest = manifest;
           return manifest;
       } function preloadImagesFromSet(set) {
           if (!set || !set.size) return;
           set.forEach(url => {
               if (!url || imagePreloadCache.has(url)) return;
               const img = new Image();
               img.decoding = 'async';
               img.loading = 'eager';
               img.referrerPolicy = 'same-origin';
               img.src = url;
               imagePreloadCache.set(url, img);
           });
       } function preloadVideosFromSet(set) {
           if (!set || !set.size) return;
           const head = document.head || document.getElementsByTagName('head')[0];
           set.forEach(url => {
               if (!url || videoPreloadCache.has(url)) return;
               const link = document.createElement('link');
               link.rel = 'preload';
               link.as = 'video';
               link.href = url;
               link.crossOrigin = 'anonymous';
               head.appendChild(link);
               videoPreloadCache.add(url);
           });
       } const subskillVideosCache = new Map();
       window.__subskillVideosCache = subskillVideosCache;
       let assetManifest = null;
       const skillsTab = $('#skills');
       const skinsTab = $('#skins');
       ensureRemoved('.top-rail');
       ensureRemoved('.content-card');
       ensureRemoved('.video-placeholder');
       Array.from(document.querySelectorAll('.card-skins-title, .card-skins .card-skins-title, .cardskins-title, .rail-title')).forEach(t => {
           if ((t.textContent || ).trim().toLowerCase().includes('skins')) {
               t.remove();
           }
       });
       if (skillsTab) {
           const iconBar = skillsTab.querySelector('.icon-bar');
           if (iconBar) {
               const rail = document.createElement('div');
               rail.className = 'top-rail skills';
               rail.appendChild(iconBar);
               skillsTab.prepend(rail);
           } const details = skillsTab.querySelector('.skills-details');
           const videoContainer = skillsTab.querySelector('.video-container');
           const card = document.createElement('div');
           card.className = 'content-card skills-grid';
           if (details) card.appendChild(details);
           if (videoContainer) card.appendChild(videoContainer);
           skillsTab.appendChild(card);
       } if (skinsTab) {
           const wrapper = skinsTab.querySelector('.skins-carousel-wrapper');
           const rail = document.createElement('div');
           rail.className = 'top-rail skins';
           const title = document.createElement('div');
           title.className = 'rail-title';
           title.textContent = 'Skins & Spotlights';
           rail.appendChild(title);
           if (wrapper) {
               const card = document.createElement('div');
               card.className = 'content-card';
               card.appendChild(wrapper);
               skinsTab.prepend(rail);
               skinsTab.appendChild(card);
           } else {
               skinsTab.prepend(rail);
           }
       } const iconsBar = $('#skills') ? $('.icon-bar', $('#skills')) : null;
       const skillsTopRail = iconsBar ? iconsBar.closest('.top-rail.skills') : null;
       const iconItems = iconsBar ? Array.from(iconsBar.querySelectorAll('.skill-icon')) : [];
       buildMainSkillsMeta(iconItems);
       // Verifica se há weapon em skills principais OU em subskills
       function checkHasAnyWeapon() {
           // Verifica skills principais
           if (iconItems.some(el => !!el.dataset.weapon)) {
               return true;
           }
           // Verifica subskills
           for (const el of iconItems) {
               const subsRaw = el.getAttribute('data-subs');
               if (!subsRaw) continue;
               try {
                   const subs = JSON.parse(subsRaw);
                   if (Array.isArray(subs) && subs.some(s => s && s.weapon)) {
                       return true;
                   }
               } catch (e) { }
           }
           return false;
       }
       const hasWeaponSkillAvailable = checkHasAnyWeapon();
       let weaponToggleBtn = null;
       if (!assetManifest) {
           assetManifest = buildAssetManifest();
           preloadImagesFromSet(assetManifest.icons);
           preloadImagesFromSet(assetManifest.flags);
           preloadVideosFromSet(assetManifest.videos);
       } const descBox = $('#skills') ? $('.desc-box', $('#skills')) : null;
       const videoBox = $('#skills') ? $('.video-container', $('#skills')) : null;
       const videosCache = new Map();
       const nestedVideoElByIcon = new WeakMap();
       const barStack = [];
       window.__barStack = barStack;
       let initialBarSnapshot = null;
       let totalVideos = 0, loadedVideos = 0, autoplay = false;
       window.__lastActiveSkillIcon = null;
       let userHasInteracted = false;
       let globalWeaponEnabled = false;
       try {
           if (localStorage.getItem('glaWeaponEnabled') === '1') {
               globalWeaponEnabled = true;
           }
       } catch (err) {
       }
       const weaponStateListeners = new Set();
       let showWeaponPopupFn = null;
       let popupShouldOpen = false;
       function attachWeaponPopupFn(fn) {
           if (typeof fn !== 'function') return;
           showWeaponPopupFn = fn;
           if (popupShouldOpen) {
               popupShouldOpen = false;
               try {
                   showWeaponPopupFn();
               } catch (err) {
               }
           }
       }
       attachWeaponPopupFn(window.__glaWeaponShowPopup);
       function requestWeaponPopupDisplay() {
           try {
               if (localStorage.getItem('glaWeaponPopupDismissed') === '1') return;
           } catch (err) {
           }
           if (typeof showWeaponPopupFn === 'function') {
               showWeaponPopupFn();
               return;
           }
           popupShouldOpen = true;
       }
       function onWeaponStateChange(fn) {
           if (typeof fn !== 'function') return;
           weaponStateListeners.add(fn);
       }
       function syncWeaponButtonState(enabled) {
           if (!weaponToggleBtn || !weaponToggleBtn.isConnected) return;
           // Usa .weapon-active em vez de .active para não conflitar com skills
           weaponToggleBtn.classList.toggle('weapon-active', !!enabled);
           weaponToggleBtn.classList.remove('active'); // Garante que .active nunca seja aplicado
           weaponToggleBtn.setAttribute('aria-pressed', enabled ? 'true' : 'false');
           weaponToggleBtn.setAttribute('aria-label', enabled ? 'Desativar Arma Especial' : 'Ativar Arma Especial');
       }
       function syncWeaponRailState(enabled) {
           if (skillsTopRail) {
               skillsTopRail.classList.toggle('weapon-mode-on', !!enabled);
           }
       }
       function notifyWeaponStateListeners(enabled) {
           weaponStateListeners.forEach(listener => {
               try {
                   listener(enabled);
               } catch (err) {
               }
           });
       }
       let pendingWeaponState = null;
       window.addEventListener('weapon:ready', (ev) => {
           if (ev && ev.detail && ev.detail.showPopup) {
               attachWeaponPopupFn(ev.detail.showPopup);
           }
           if (pendingWeaponState === null) return;
           if (typeof window.__applyWeaponState === 'function') {
               const target = pendingWeaponState;
               pendingWeaponState = null;
               window.__applyWeaponState(target);
           }
       });
       window.__setGlobalWeaponEnabled = (enabled) => {
           globalWeaponEnabled = enabled;
           notifyWeaponStateListeners(enabled);
       };
       function requestWeaponState(targetState) {
           if (typeof window.__applyWeaponState === 'function') {
               pendingWeaponState = null;
               window.__applyWeaponState(targetState);
               return;
           }
           pendingWeaponState = targetState;
       }
       onWeaponStateChange(syncWeaponButtonState);
       function reapplyWeaponClassesToBar() {
           if (!globalWeaponEnabled) return;
           // SISTEMA UNIFICADO: Aplica em skills E subskills
           iconsBar.querySelectorAll('.skill-icon[data-weapon], .subicon[data-weapon]').forEach(el => {
               if (!el.classList.contains('has-weapon-available')) {
                   el.classList.add('has-weapon-available');
               }
               if (!el.querySelector('.weapon-indicator')) {
                   const ind = document.createElement('div');
                   ind.className = 'weapon-indicator';
                   el.appendChild(ind);
               }
           });
       }
       function setupWeaponBarToggle(shouldShow) {
           if (!shouldShow || !iconsBar) return;
           if (iconsBar.querySelector('.weapon-bar-toggle')) return;
           const btn = document.createElement('button');
           btn.type = 'button';
           btn.className = 'skill-icon weapon-bar-toggle';
           btn.dataset.weaponToggle = '1';
           btn.dataset.nome = 'Arma Especial';
           btn.setAttribute('aria-pressed', 'false');
           btn.setAttribute('aria-label', 'Arma Especial');
           btn.innerHTML = '<svg width="24" height="24" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path fill="currentColor" d="M19.14 12.94c.04-.31.06-.63.06-.94s-.02-.63-.06-.94l2.03-1.58a.5.5 0 00.12-.62l-1.92-3.32a.5.5 0 00-.61-.22l-2.39.96c-.5-.38-1.03-.7-1.62-.94l-.37-2.48a.55.55 0 00-.55-.5h-3.82a.55.55 0 00-.55.5l-.37 2.48c-.59.24-1.12.56-1.62.94l-2.39-.96a.5.5 0 00-.61.22L3.13 8.5a.5.5 0 00.12.62l2.03 1.58c-.04.31-.06.63-.06.94s.02.63.06.94l-2.03 1.58a.5.5 0 00-.12.62l1.92 3.32a.5.5 0 00.61.22l2.39-.96c.5.38 1.03.7 1.62.94l.37 2.48a.55.55 0 00.55.5h3.82a.55.55 0 00.55-.5l.37-2.48c.59-.24 1.12-.56 1.62-.94l2.39.96a.5.5 0 00.61-.22l1.92-3.32a.5.5 0 00-.12-.62zM12 15.6a3.6 3.6 0 110-7.2 3.6 3.6 0 010 7.2z"></path></svg>';
           iconsBar.appendChild(btn);
           weaponToggleBtn = btn;
           syncWeaponButtonState(globalWeaponEnabled);
           btn.addEventListener('click', () => {
               const nextState = !globalWeaponEnabled;
               if (nextState) {
                   requestWeaponPopupDisplay();
               }
               requestWeaponState(nextState);
           });
           // Wire tooltip for weapon toggle
           const tooltip = window.__globalSkillTooltip;
           if (tooltip) {
               btn.addEventListener('mouseenter', () => {
                   const tip = document.querySelector('.skill-tooltip');
                   if (tip) tip.classList.add('weapon-tooltip');
                   tooltip.show(btn, globalWeaponEnabled ? 'Desativar Arma Especial' : 'Ativar Arma Especial');
               });
               btn.addEventListener('mouseleave', () => {
                   const tip = document.querySelector('.skill-tooltip');
                   if (tip) tip.classList.remove('weapon-tooltip');
                   tooltip.hide();
               });
           }
           reapplyWeaponClassesToBar();
       }
       onWeaponStateChange(syncWeaponRailState);
       syncWeaponRailState(globalWeaponEnabled);
       setupWeaponBarToggle(hasWeaponSkillAvailable);
       (function injectWeaponStyles() {
           if (document.getElementById('weapon-toggle-styles')) return;
           const style = document.createElement('style');
           style.id = 'weapon-toggle-styles';
           style.textContent = `
               /* Animação da borda nos ícones */
               @keyframes weapon-icon-border-scan {
                   0% { background-position: 0% 0%; }
                   100% { background-position: 400% 0%; }
               }
               @keyframes weapon-icon-pulse {
                   0%, 100% { 
                       opacity: 0.5;
                       box-shadow: 0 0 8px rgba(255, 80, 80, 0.25);
                   }
                   50% { 
                       opacity: 1;
                       box-shadow: 0 0 16px rgba(255, 80, 80, 0.45);
                   }
               }
               /* Skills com arma disponível - borda vermelha quando inativa */
               .skill-icon.has-weapon-available:not(.weapon-bar-toggle):not(.active)::after {
                   box-shadow: inset 0 0 0 var(--icon-ring-w) rgba(220, 70, 70, 0.8) !important;
               }
               /* Skill com arma ATIVA - laranja/coral vibrante */
               .skill-icon.has-weapon-available:not(.weapon-bar-toggle).active::after {
                   box-shadow: inset 0 0 0 var(--icon-ring-w) #FF7043 !important;
               }
               .skill-icon.has-weapon-available:not(.weapon-bar-toggle).active::before {
                   box-shadow: 0 0 12px 3px rgba(255, 112, 67, 0.35), 0 0 0 4px rgba(255, 112, 67, 0.25) !important;
               }
               /* Modo arma ON - efeito animado nos ícones */
               .top-rail.skills.weapon-mode-on .skill-icon.has-weapon-available:not(.weapon-bar-toggle) {
                   position: relative;
               }
               .top-rail.skills.weapon-mode-on .skill-icon.has-weapon-available:not(.weapon-bar-toggle)::after {
                   box-shadow: none !important;
                   background: linear-gradient(90deg, 
                       rgba(255, 80, 80, 0.9) 0%,
                       rgba(255, 120, 60, 1) 25%,
                       rgba(255, 80, 80, 0.9) 50%,
                       rgba(255, 120, 60, 1) 75%,
                       rgba(255, 80, 80, 0.9) 100%
                   ) !important;
                   background-size: 400% 100% !important;
                   animation: weapon-icon-border-scan 4s linear infinite !important;
                   -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0) !important;
                   mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0) !important;
                   -webkit-mask-composite: xor !important;
                   mask-composite: exclude !important;
                   padding: var(--icon-ring-w) !important;
               }
               .top-rail.skills.weapon-mode-on .skill-icon.has-weapon-available:not(.weapon-bar-toggle)::before {
                   animation: weapon-icon-pulse 3s ease-in-out infinite !important;
               }
               /* Skill ativa com arma - mais intenso */
               .top-rail.skills.weapon-mode-on .skill-icon.has-weapon-available:not(.weapon-bar-toggle).active::after {
                   background: linear-gradient(90deg, 
                       rgba(255, 87, 34, 1) 0%,
                       rgba(255, 140, 60, 1) 25%,
                       rgba(255, 87, 34, 1) 50%,
                       rgba(255, 140, 60, 1) 75%,
                       rgba(255, 87, 34, 1) 100%
                   ) !important;
               }
               .top-rail.skills.weapon-mode-on .skill-icon.has-weapon-available:not(.weapon-bar-toggle).active::before {
                   box-shadow: 0 0 14px 4px rgba(255, 87, 34, 0.4), 0 0 0 4px rgba(255, 87, 34, 0.3) !important;
                   animation: weapon-icon-pulse 2.5s ease-in-out infinite !important;
               }
               .skill-icon .weapon-indicator {
                   display: none;
               }
               /* Variáveis de cor vermelha para skill ativa com arma equipada */
               .skill-icon.weapon-equipped {
                   --icon-active: #FF6B6B;
                   --icon-active-ring: rgba(255, 100, 100, 0.6);
                   --icon-active-glow: rgba(255, 90, 90, 0.45);
               }
               /* Badge de arma no canto inferior direito */
               .skill-icon .weapon-badge {
                   position: absolute;
                   bottom: 3px;
                   right: 3px;
                   width: 16px;
                   height: 16px;
                   background: var(--weapon-badge-url) center/contain no-repeat;
                   filter: drop-shadow(0 1px 2px rgba(0,0,0,0.6));
                   pointer-events: none;
                   z-index: 10;
                   border-radius: 3px;
                   display: none;
               }
               .skill-icon.weapon-equipped .weapon-badge {
                   display: block;
               }
           `.replace(/\s+/g, ' ').trim();
           document.head.appendChild(style);
       })();
       function applyWeaponBadge(el, weaponData, equipped) {
           // Encontrar ou criar o badge
           let badge = el.querySelector('.weapon-badge');
           if (!badge) {
               badge = document.createElement('div');
               badge.className = 'weapon-badge';
               el.appendChild(badge);
           }
           if (equipped && weaponData) {
               el.classList.add('weapon-equipped');
               el.style.setProperty('--weapon-badge-url', `url('${filePathURL(weaponData.icon || 'Nada.png')}')`);
           } else {
               el.classList.remove('weapon-equipped');
               el.style.removeProperty('--weapon-badge-url');
           }
       } function getWeaponKey(el) {
           return (el.dataset.index || ) + ':' + (el.dataset.nome || el.dataset.name || );
       } function isWeaponModeOn() {
           try {
               return localStorage.getItem('glaWeaponEnabled') === '1';
           } catch (e) {
               return false;
           }
       } function getWeaponDataForIcon(iconEl) {
           if (!iconEl || !iconEl.dataset.weapon) return null;
           try {
               return JSON.parse(iconEl.dataset.weapon);
           } catch (e) {
               return null;
           }
       } function getEffectiveSkillVideoFromIcon(iconEl) {
           const weaponOn = globalWeaponEnabled;
           const weaponData = getWeaponDataForIcon(iconEl);
           const baseVideoFile = (iconEl.dataset.videoFile || ).trim();
           const baseVideoURL = (iconEl.dataset.video || ).trim();
           console.log('[Skills DEBUG]', {
               skillName: iconEl.dataset.nome || iconEl.dataset.name,
               weaponOn,
               hasWeaponData: !!weaponData,
               weaponData: weaponData,
               baseVideoFile,
               baseVideoURL
           });
           if (weaponOn && weaponData) {
               console.log('[Skills] checking weapon video', {
                   skillName: iconEl.dataset.nome || iconEl.dataset.name,
                   hasVideo: !!(weaponData.video),
                   videoValue: weaponData.video,
                   videoTrimmed: weaponData.video ? weaponData.video.trim() : 
               });
               if (weaponData.video && weaponData.video.trim() !== ) {
                   console.log('[Skills] video escolhido (weapon)', iconEl.dataset.nome || iconEl.dataset.name, weaponData.video);
                   return weaponData.video.trim();
               }
           }
           const result = baseVideoFile || baseVideoURL || ;
           console.log('[Skills] video escolhido (base)', iconEl.dataset.nome || iconEl.dataset.name, result);
           return result;
       } function createVideoElement(videoURL, extraAttrs = {
       }) {
           const v = document.createElement('video');
           v.className = 'skill-video';
           v.setAttribute('controls', );
           v.setAttribute('preload', 'auto');
           v.setAttribute('playsinline', );
           v.style.display = 'none';
           v.style.width = '100%';
           v.style.height = 'auto';
           v.style.aspectRatio = '16/9';
           v.style.objectFit = 'cover';
           Object.keys(extraAttrs).forEach(k => {
               v.dataset[k] = extraAttrs[k];
           });
           // Detectar formato do vídeo pela extensão
           const ext = (videoURL.split('.').pop() || ).toLowerCase().split('?')[0];
           const mimeTypes = {
               'mp4': 'video/mp4',
               'm4v': 'video/mp4',
               'webm': 'video/webm',
               'ogv': 'video/ogg',
               'ogg': 'video/ogg',
               'mov': 'video/quicktime'
           };
           const mimeType = mimeTypes[ext] || 'video/mp4';
           const src = document.createElement('source');
           src.src = videoURL;
           src.type = mimeType;
           v.appendChild(src);
           // Fallback para Safari/iOS mais antigos
           v.setAttribute('webkit-playsinline', );
           v.setAttribute('x-webkit-airplay', 'allow');
           return v;
       } function precreateSubskillVideos() {
           if (!videoBox) return;
           iconItems.forEach(parentIcon => {
               const subsRaw = parentIcon.dataset.subs || parentIcon.getAttribute('data-subs');
               if (!subsRaw) return;
               try {
                   const subs = JSON.parse(subsRaw);
                   if (!Array.isArray(subs)) return;
                   const parentIdx = parentIcon.dataset.index || ;
                   subs.forEach(s => {
                       const subName = (s.name || s.n || ).trim();
                       // Vídeo normal da subskill
                       if (s.video && s.video.trim() !==  && s.video !== 'Nada.png' && !s.video.toLowerCase().includes('nada.png')) {
                           const key = `sub:${parentIdx}:${subName}`;
                           if (subskillVideosCache.has(key)) return;
                           const videoURL = normalizeFileURL(s.video);
                           if (videoURL && videoURL.trim() !== ) {
                               const v = createVideoElement(videoURL, {
                                   sub: '1', parentIndex: parentIdx, subName: subName
                               });
                               v.setAttribute('preload', 'auto');
                               videoBox.appendChild(v);
                               subskillVideosCache.set(key, v);
                               // Força carregamento apenas se ainda não estiver carregando
                               if (v.readyState === 0 || v.readyState === 1) {
                                   v.load();
                               }
                           }
                       }
                       // Vídeo de weapon da subskill
                       if (s.weapon && typeof s.weapon === 'object' && s.weapon.video && s.weapon.video.trim() !==  && s.weapon.video !== 'Nada.png' && !s.weapon.video.toLowerCase().includes('nada.png')) {
                           const weaponKey = `sub:${parentIdx}:${subName}:weapon`;
                           if (subskillVideosCache.has(weaponKey)) return;
                           const weaponVideoURL = normalizeFileURL(s.weapon.video);
                           if (weaponVideoURL && weaponVideoURL.trim() !== ) {
                               const v = createVideoElement(weaponVideoURL, {
                                   sub: '1', parentIndex: parentIdx, subName: subName, weapon: '1'
                               });
                               v.setAttribute('preload', 'auto');
                               videoBox.appendChild(v);
                               subskillVideosCache.set(weaponKey, v);
                               // Força carregamento apenas se ainda não estiver carregando
                               if (v.readyState === 0 || v.readyState === 1) {
                                   v.load();
                               }
                           }
                       }
                   });
               } catch (e) {
               }
           });
       } setTimeout(precreateSubskillVideos, 100);
       if (iconItems.length && videoBox) {
           iconItems.forEach(el => {
               const idx = el.dataset.index || ;
               // Vídeo normal
               const src = (el.dataset.videoFile || el.dataset.video || ).trim();
               if (src && src !== 'Nada.png' && !src.toLowerCase().includes('nada.png') && !videosCache.has(idx)) {
                   totalVideos++;
                   const videoURL = normalizeFileURL(src);
                   if (videoURL) {
                       const v = createVideoElement(videoURL, {
                           index: idx
                       });
                       v.setAttribute('preload', 'auto');
                       v.style.maxWidth = '100%';
                       v.addEventListener('canplaythrough', () => {
                           loadedVideos++;
                           if (!userHasInteracted && loadedVideos === 1) {
                               try {
                                   v.pause();
                                   v.currentTime = 0;
                               } catch (e) {
                               }
                           } if (loadedVideos === totalVideos) autoplay = true;
                       }, {
                           once: true
                       });
                       v.addEventListener('error', () => {
                           loadedVideos++;
                           if (loadedVideos === totalVideos) autoplay = true;
                       }, {
                           once: true
                       });
                       videoBox.appendChild(v);
                       videosCache.set(idx, v);
                       // Força carregamento
                       v.load();
                   }
               }
               // Vídeo de weapon
               if (el.dataset.weapon) {
                   try {
                       const weaponData = JSON.parse(el.dataset.weapon);
                       if (weaponData && weaponData.video && weaponData.video.trim() !==  && weaponData.video !== 'Nada.png' && !weaponData.video.toLowerCase().includes('nada.png')) {
                           const weaponKey = `weapon:${idx}:${(el.dataset.nome || el.dataset.name || ).trim()}`;
                           if (!videosCache.has(weaponKey)) {
                               totalVideos++;
                               const weaponVideoURL = normalizeFileURL(weaponData.video);
                               if (weaponVideoURL) {
                                   const v = createVideoElement(weaponVideoURL, {
                                       index: idx, weapon: '1'
                                   });
                                   v.setAttribute('preload', 'auto');
                                   v.style.maxWidth = '100%';
                                   v.addEventListener('canplaythrough', () => {
                                       loadedVideos++;
                                       if (loadedVideos === totalVideos) autoplay = true;
                                   }, {
                                       once: true
                                   });
                                   v.addEventListener('error', () => {
                                       loadedVideos++;
                                       if (loadedVideos === totalVideos) autoplay = true;
                                   }, {
                                       once: true
                                   });
                                   videoBox.appendChild(v);
                                   videosCache.set(weaponKey, v);
                                   // Força carregamento
                                   v.load();
                               }
                           }
                       }
                   } catch (e) {
                   }
               }
           });
       } function wireTooltipsForNewIcons() {
           const tip = document.querySelector('.skill-tooltip');
           if (!tip) return;
           let lockUntil2 = 0;
           Array.from(document.querySelectorAll('.icon-bar .skill-icon')).forEach(icon => {
               if (icon.dataset.weaponToggle === '1' || icon.classList.contains('weapon-bar-toggle')) return;
               if (icon.dataset.tipwired) return;
               icon.dataset.tipwired = '1';
               const label = icon.dataset.nome || icon.dataset.name || icon.title || ;
               if (label && !icon.hasAttribute('aria-label')) icon.setAttribute('aria-label', label);
               if (icon.hasAttribute('title')) icon.removeAttribute('title');
               const img = icon.querySelector('img');
               if (img) {
                   const imgAlt = img.getAttribute('alt') || ;
                   const imgTitle = img.getAttribute('title') || ;
                   if (!label && (imgAlt || imgTitle)) icon.setAttribute('aria-label', imgAlt || imgTitle);
                   img.setAttribute('alt', );
                   if (img.hasAttribute('title')) img.removeAttribute('title');
               } const measureAndPos = (el) => {
                   if (!el || tip.getAttribute('aria-hidden') === 'true') return;
                   tip.style.left = '0px';
                   tip.style.top = '0px';
                   const rect = el.getBoundingClientRect();
                   const tr = tip.getBoundingClientRect();
                   let left = Math.round(rect.left + (rect.width - tr.width) / 2);
                   left = Math.max(8, Math.min(left, window.innerWidth - tr.width - 8));
                   const coarse = (window.matchMedia && matchMedia('(pointer: coarse)').matches) || (window.innerWidth <= 600);
                   let top = coarse ? Math.round(rect.bottom + 10) : Math.round(rect.top - tr.height - 8);
                   if (top < 8) top = Math.round(rect.bottom + 10);
                   tip.style.left = left + 'px';
                   tip.style.top = top + 'px';
               };
               const show = (el, text) => {
                   tip.textContent = text || ;
                   tip.setAttribute('aria-hidden', 'false');
                   measureAndPos(el);
                   tip.style.opacity = '1';
               };
               const hide = () => {
                   tip.setAttribute('aria-hidden', 'true');
                   tip.style.opacity = '0';
                   tip.style.left = '-9999px';
                   tip.style.top = '-9999px';
               };
               icon.addEventListener('mouseenter', () => show(icon, (icon.dataset.nome || icon.dataset.name || )));
               icon.addEventListener('mousemove', () => {
                   if (performance.now() >= lockUntil2) measureAndPos(icon);
               });
               icon.addEventListener('click', () => {
                   lockUntil2 = performance.now() + 240;
                   measureAndPos(icon);
               });
               icon.addEventListener('mouseleave', hide);
           });
       } function showVideoForIcon(el) {
           userHasInteracted = true;
           if (!videoBox) return;
           const effectiveVideo = getEffectiveSkillVideoFromIcon(el);
           if (!effectiveVideo || effectiveVideo.trim() === ) {
               videoBox.style.display = 'none';
               return;
           }
           const videoURL = normalizeFileURL(effectiveVideo);
           if (!videoURL || videoURL.trim() === ) {
               videoBox.style.display = 'none';
               return;
           }
           Array.from(videoBox.querySelectorAll('video.skill-video')).forEach(v => {
               try {
                   v.pause();
               } catch (e) {
               }
               v.style.display = 'none';
           });
           if (window.__subskills) window.__subskills.hideAll?.(videoBox);
           const hasIdx = !!el.dataset.index;
           const weaponOn = globalWeaponEnabled;
           const weaponData = getWeaponDataForIcon(el);
           const isWeaponVideo = weaponOn && weaponData && weaponData.video && weaponData.video.trim() !== ;
           console.log('[Skills] showVideoForIcon chamado', {
               skillName: el.dataset.nome || el.dataset.name,
               weaponOn,
               isWeaponVideo,
               effectiveVideo: getEffectiveSkillVideoFromIcon(el)
           });
           const videoKey = isWeaponVideo ? `weapon:${getWeaponKey(el)}` : (el.dataset.index || );
           if (hasIdx && !isWeaponVideo && videosCache.has(el.dataset.index)) {
               const v = videosCache.get(el.dataset.index);
               videoBox.style.display = 'block';
               v.style.display = 'block';
               try {
                   v.currentTime = 0;
               } catch (e) {
               }
               const suppress = document.body.dataset.suppressSkillPlay === '1';
               if (!suppress) {
                   v.play().catch(() => {
                   });
               } else {
                   try {
                       v.pause();
                   } catch (e) {
                   }
               }
               return;
           }
           let v = null;
           if (isWeaponVideo) {
               v = videoBox.querySelector(`video[data-weapon-key="${videoKey}"]`);
           } else {
               v = nestedVideoElByIcon.get(el);
           }
           if (!v) {
               v = createVideoElement(videoURL, isWeaponVideo ? {
                   weaponKey: videoKey
               } : {});
               if (isWeaponVideo) {
                   videoBox.appendChild(v);
               } else {
                   videoBox.appendChild(v);
                   nestedVideoElByIcon.set(el, v);
               }
           } else {
               const src = v.querySelector('source');
               if (src && src.src !== videoURL) {
                   src.src = videoURL;
                   v.load();
               }
           }
           videoBox.style.display = 'block';
           v.style.display = 'block';
           try {
               v.currentTime = 0;
           } catch (e) {
           }
           const suppress = document.body.dataset.suppressSkillPlay === '1';
           if (!suppress) {
               v.play().catch(() => {
               });
           } else {
               try {
                   v.pause();
               } catch (e) {
               }
           }
       } function activateSkill(el, options = {
       }) {
           const {
               openSubs = true
           } = options;
           const tip = document.querySelector('.skill-tooltip');
           if (tip) {
               tip.setAttribute('aria-hidden', 'true');
               tip.style.opacity = '0';
               tip.style.left = '-9999px';
               tip.style.top = '-9999px';
           } const skillsRoot = document.getElementById('skills');
           const i18nMap = skillsRoot ? JSON.parse(skillsRoot.dataset.i18nAttrs || '{}') : {
           };
           const L = i18nMap[getLangKey()] || i18nMap.pt || {
               cooldown: 'Recarga', energy_gain: 'Ganho de energia', energy_cost: 'Custo de energia', power: 'Poder', power_pvp: 'Poder PvP', level: 'Nível'
           };
           const name = el.dataset.nome || el.dataset.name || ;
           const level = (el.dataset.level || ).trim();
           let weaponData = null;
           if (el.dataset.weapon) {
               try {
                   weaponData = JSON.parse(el.dataset.weapon);
               } catch (e) {
                   weaponData = null;
               }
           } const hasWeapon = !!weaponData;
           const weaponEquipped = hasWeapon && globalWeaponEnabled;
           const lang = getLangKey();
           const baseDescPack = {
               pt: el.dataset.descPt || , en: el.dataset.descEn || , es: el.dataset.descEs || , pl: el.dataset.descPl || 
           };
           const baseDesc = baseDescPack[lang] || baseDescPack.pt || baseDescPack.en || baseDescPack.es || baseDescPack.pl || el.dataset.desc || ;
           // Aceita tanto desc_i18n quanto desc para compatibilidade
           let weaponDescPack = {};
           if (weaponData) {
               if (weaponData.desc_i18n) {
                   weaponDescPack = weaponData.desc_i18n;
               } else if (weaponData.desc) {
                   weaponDescPack = weaponData.desc;
               } else {
                   weaponDescPack = {
                       pt: weaponData.descPt || , en: weaponData.descEn || , es: weaponData.descEs || , pl: weaponData.descPl || 
                   };
               }
           }
           const weaponDesc = weaponDescPack[lang] || weaponDescPack.pt || weaponDescPack.en || weaponDescPack.es || weaponDescPack.pl || ;
           const chosenDesc = (weaponEquipped && weaponDesc) ? weaponDesc : baseDesc;
           const descHtml = chosenDesc.replace(/(.*?)/g, '$1');
           let attrsHTML = ;
           if (weaponEquipped && weaponData) {
               // Usa valores do weapon, mas só se existirem (não herda da skill base)
               const wPve = (weaponData.powerpve !== undefined && weaponData.powerpve !== null && weaponData.powerpve !== ) ? weaponData.powerpve.toString().trim() : ;
               const wPvp = (weaponData.powerpvp !== undefined && weaponData.powerpvp !== null && weaponData.powerpvp !== ) ? weaponData.powerpvp.toString().trim() : ;
               const wEnergy = (weaponData.energy !== undefined && weaponData.energy !== null && weaponData.energy !== ) ? weaponData.energy.toString().trim() : ;
               const wCd = (weaponData.cooldown !== undefined && weaponData.cooldown !== null && weaponData.cooldown !== ) ? weaponData.cooldown.toString().trim() : ;
               const weaponAttrs = [wPve, wPvp, wEnergy, wCd].join(',');
               console.log('[Skills] weaponAttrs string:', weaponAttrs, { wPve, wPvp, wEnergy, wCd, weaponData });
               attrsHTML = renderAttributes(weaponAttrs);
           } else {
               attrsHTML = el.dataset.atr ? renderAttributes(el.dataset.atr) : (el.dataset.subattrs ? renderSubAttributesFromObj(JSON.parse(el.dataset.subattrs), L) : );
           } let flagsHTML = ;
           if (el.dataset.flags) {
               try {
                   const flags = JSON.parse(el.dataset.flags);
                   flagsHTML = renderFlagsRow(flags);
               } catch (e) {
               }
           } if (descBox) {

descBox.innerHTML = `

${name}

${level ? `

${L.level} ${level}

` : }${attrsHTML}

${descHtml}

`;

           } if (hasWeapon) {
               applyWeaponBadge(el, weaponData, weaponEquipped);
           } if (videoBox) {
               const oldFlags = videoBox.querySelector('.skill-flags');
               if (oldFlags) oldFlags.remove();
               if (flagsHTML) {
                   videoBox.insertAdjacentHTML('beforeend', flagsHTML);
                   applyFlagTooltips(videoBox);
               }
           } const currIcons = Array.from(iconsBar.querySelectorAll('.skill-icon'));
           currIcons.forEach(i => i.classList.remove('active'));
           el.classList.add('active');
           if (!autoplay && loadedVideos > 0) autoplay = true;
           window.__lastActiveSkillIcon = el;
           // Lógica de vídeo: usa função centralizada que já considera weapon
           showVideoForIcon(el);
           const subsRaw = el.dataset.subs || el.getAttribute('data-subs');
           const isBack = el.dataset.back === 'true' || el.getAttribute('data-back') === 'true' || el.dataset.back === 'yes' || el.getAttribute('data-back') === 'yes' || el.dataset.back === '1' || el.getAttribute('data-back') === '1';
           if (isBack && barStack.length) {
               const prev = barStack.pop();
               renderBarFromItems(prev.items);
               const btn = document.querySelector('.skills-back-wrapper');
               if (btn) btn.style.display = barStack.length ? 'block' : 'none';
               return;
           } if (openSubs && subsRaw && subsRaw.trim() !== ) {
               if (barStack.length && barStack[barStack.length - 1].parentIcon === el) return;
               try {
                   const subs = JSON.parse(subsRaw);
                   pushSubBarFrom(subs, el);
               } catch {
               }
           }
       } function wireClicksForCurrentBar() {
           const currIcons = Array.from(iconsBar.querySelectorAll('.skill-icon'));
           currIcons.forEach(el => {
               if (el.dataset.weaponToggle === '1' || el.classList.contains('weapon-bar-toggle')) return;
               if (el.dataset.wired) return;
               el.dataset.wired = '1';
               const label = el.dataset.nome || el.dataset.name || ;
               el.setAttribute('aria-label', label);
               if (el.hasAttribute('title')) el.removeAttribute('title');
               const img = el.querySelector('img');
               if (img) {
                   img.setAttribute('alt', );
                   if (img.hasAttribute('title')) img.removeAttribute('title');
               } el.addEventListener('click', () => {
                   activateSkill(el, {
                       openSubs: true
                   });
               });
           });
           wireTooltipsForNewIcons();
       } function animateIconsBarEntrance() {
           Array.from(iconsBar.children).forEach((c, i) => {
               c.style.opacity = '0';
               c.style.transform = 'translateY(6px)';
               requestAnimationFrame(() => {
                   setTimeout(() => {
                       c.style.transition = 'opacity .18s ease, transform .18s ease';
                       c.style.opacity = '1';
                       c.style.transform = 'translateY(0)';
                   }, i * 24);
               });
           });
       } function snapshotCurrentBarItemsFromDOM() {
           return Array.from(iconsBar.querySelectorAll('.skill-icon')).filter(el => el.dataset.weaponToggle !== '1').map(el => {
               const img = el.querySelector('img');
               const iconURL = img ? img.src : ;
               const subsRaw = el.dataset.subs || el.getAttribute('data-subs') || ;
               let subs = null;
               try {
                   subs = subsRaw ? JSON.parse(subsRaw) : null;
               } catch {
                   subs = null;
               } const subattrsRaw = el.dataset.subattrs || ;
               let flags = null;
               if (el.dataset.flags) {
                   try {
                       flags = JSON.parse(el.dataset.flags);
                   } catch (e) {
                   }
               } let weapon = null;
               if (el.dataset.weapon) {
                   try {
                       weapon = JSON.parse(el.dataset.weapon);
                   } catch (e) {
                   }
               } return {
                   name: el.dataset.nome || el.dataset.name || , index: el.dataset.index || , level: el.dataset.level || , desc: el.dataset.desc || , descPt: el.dataset.descPt || , descEn: el.dataset.descEn || , descEs: el.dataset.descEs || , descPl: el.dataset.descPl || , attrs: el.dataset.atr || el.dataset.attrs || , video: el.dataset.video || , iconURL, subs, subattrsStr: subattrsRaw, flags: flags, weapon: weapon
               };
           });
       } function ensureBackButton() {
           const rail = iconsBar.closest('.top-rail.skills');
           if (!rail) return null;
           let wrap = rail.parentElement;
           if (!wrap || !wrap.classList || !wrap.classList.contains('skills-rail-wrap')) {
               const parentNode = rail.parentNode;
               const newWrap = document.createElement('div');
               newWrap.className = 'skills-rail-wrap';
               parentNode.insertBefore(newWrap, rail);
               newWrap.appendChild(rail);
               wrap = newWrap;
           } let backWrap = wrap.querySelector('.skills-back-wrapper');
           if (!backWrap) {
               backWrap = document.createElement('div');
               backWrap.className = 'skills-back-wrapper';
               const btnInner = document.createElement('button');
               btnInner.className = 'skills-back';
               btnInner.type = 'button';
               btnInner.setAttribute('aria-label', 'Voltar');
               btnInner.innerHTML = '<svg class="back-chevron" width="100%" height="100%" viewBox="0 0 36 32" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" preserveAspectRatio="xMidYMid meet"><path d="M10 2L4 16L10 30" stroke="currentColor" stroke-width="2.8" stroke-linecap="round" stroke-linejoin="round"/><path d="M20 2L14 16L20 30" stroke="currentColor" stroke-width="2.8" stroke-linecap="round" stroke-linejoin="round"/><path d="M30 2L24 16L30 30" stroke="currentColor" stroke-width="2.8" stroke-linecap="round" stroke-linejoin="round"/></svg>';
               backWrap.appendChild(btnInner);
               wrap.insertBefore(backWrap, rail);
               btnInner.addEventListener('click', () => {
                   if (!barStack.length) return;
                   const prev = barStack.pop();
                   renderBarFromItems(prev.items);
                   backWrap.style.display = barStack.length ? 'block' : 'none';
                   wrap.classList.toggle('has-sub-bar', barStack.length > 0);
                   if (!barStack.length) btnInner.classList.remove('peek');
               });
           } backWrap.style.display = barStack.length ? 'block' : 'none';
           wrap.classList.toggle('has-sub-bar', barStack.length > 0);
           const btnInner = backWrap.querySelector('.skills-back');
           return btnInner;
       } function renderBarFromItems(items) {
           const tip = document.querySelector('.skill-tooltip');
           if (tip) {
               tip.setAttribute('aria-hidden', 'true');
               tip.style.opacity = '0';
               tip.style.left = '-9999px';
               tip.style.top = '-9999px';
           } iconsBar.innerHTML = ;
           items.forEach((it, idx) => {
               const node = document.createElement('div');
               node.className = 'skill-icon';
               node.dataset.nome = it.name || ;
               if (it.index) node.dataset.index = it.index;
               if (it.level) node.dataset.level = it.level;
               if (it.desc) node.dataset.desc = it.desc;
               if (it.descPt) node.dataset.descPt = it.descPt;
               if (it.descEn) node.dataset.descEn = it.descEn;
               if (it.descEs) node.dataset.descEs = it.descEs;
               if (it.descPl) node.dataset.descPl = it.descPl;
               if (it.attrs) node.dataset.atr = it.attrs;
               if (it.video) node.dataset.video = it.video;
               if (it.subs) node.dataset.subs = JSON.stringify(it.subs);
               if (it.subattrsStr) node.dataset.subattrs = it.subattrsStr;
               if (it.flags) node.dataset.flags = JSON.stringify(it.flags);
               if (it.weapon) node.dataset.weapon = JSON.stringify(it.weapon);
               if (!it.index) node.dataset.nested = '1';
               const img = document.createElement('img');
               img.alt = ;
               img.src = it.iconURL || (it.icon ? filePathURL(it.icon) : );
               node.appendChild(img);
               iconsBar.appendChild(node);
           });
           animateIconsBarEntrance();
           wireClicksForCurrentBar();
           setupWeaponBarToggle(hasWeaponSkillAvailable);
           const b = ensureBackButton();
           if (b) b.classList.add('peek');
       } function pushSubBarFrom(subs, parentIconEl) {
           const tip = document.querySelector('.skill-tooltip');
           if (tip) {
               tip.setAttribute('aria-hidden', 'true');
               tip.style.opacity = '0';
               tip.style.left = '-9999px';
               tip.style.top = '-9999px';
           } const parentNameSnapshot = parentIconEl ? (parentIconEl.dataset.nome || parentIconEl.dataset.name || ) : ;
           const parentIndexSnapshot = parentIconEl ? (parentIconEl.dataset.index || ) : ;
           barStack.push({
               items: snapshotCurrentBarItemsFromDOM(), parentIcon: parentIconEl, parentName: parentNameSnapshot, parentIndex: parentIndexSnapshot
           });
           ensureBackButton();
           const langKey = getLangKey();
           let cacheKey = null;
           if (parentIconEl) {
               cacheKey = parentIconEl.dataset.subCacheKey || null;
               if (!cacheKey) {
                   if (parentIconEl.dataset.index) {
                       cacheKey = `idx:${parentIconEl.dataset.index}`;
                   } else {
                       const slug = slugify(parentIconEl.dataset.nome || parentIconEl.dataset.name || );
                       if (slug) cacheKey = `slug:${slug}`;
                   } if (cacheKey) parentIconEl.dataset.subCacheKey = cacheKey;
               }
           } if (cacheKey) {
               const cached = subBarTemplateCache.get(cacheKey);
               if (cached && cached.lang === langKey) {
                   iconsBar.innerHTML = ;
                   const clone = cached.template.cloneNode(true);
                   iconsBar.appendChild(clone);
                   animateIconsBarEntrance();
                   wireClicksForCurrentBar();
                   setupWeaponBarToggle(hasWeaponSkillAvailable);
                   const cachedBtn = ensureBackButton();
                   if (cachedBtn) cachedBtn.classList.add('peek');
                   return;
               }
           } const skillsRoot = document.getElementById('skills');
           const i18nMap = skillsRoot ? JSON.parse(skillsRoot.dataset.i18nAttrs || '{}') : {
           };
           const L = i18nMap[getLangKey()] || i18nMap.pt || {
               cooldown: 'Recarga', energy_gain: 'Ganho de energia', energy_cost: 'Custo de energia', power: 'Poder', power_pvp: 'Poder PvP', level: 'Nível'
           };
           const hydratedSubs = inheritSubskillTree(subs, mainSkillsMeta);
           const items = (hydratedSubs || []).filter(s => {
               // Filtra só se não tem nada útil
               const hasName = (s.name || s.n || ).trim() !== ;
               const hasIcon = (s.icon || ).trim() !==  && s.icon !== 'Nada.png';
               const hasRef = (s.refS || s.refM || ).toString().trim() !== ;
               return hasName || hasIcon || hasRef;
           }).map(s => {
               const name = (s.name || s.n || ).trim();
               const desc = chooseDescFrom(s).replace(/(.*?)/g, '$1');
               const attrsHTML = renderSubAttributesFromObj(s, L);
               return {
                   name, level: (s.level || ).toString().trim(), desc, descPt: (s.descPt || (s.desc_i18n && s.desc_i18n.pt) || ), descEn: (s.descEn || (s.desc_i18n && s.desc_i18n.en) || ), descEs: (s.descEs || (s.desc_i18n && s.desc_i18n.es) || ), descPl: (s.descPl || (s.desc_i18n && s.desc_i18n.pl) || ), attrs: , icon: (s.icon || 'Nada.png'), iconURL: filePathURL(s.icon || 'Nada.png'), video: s.video ? filePathURL(s.video) : , subs: Array.isArray(s.subs) ? s.subs : null, subattrs: s, flags: Array.isArray(s.flags) ? s.flags : null, back: (s.back === true || s.back === 'true' || s.back === 'yes' || s.back === '1') ? 'true' : null, weapon: s.weapon || null
               };
           });
           const fragment = document.createDocumentFragment();
           items.forEach((it, iIdx) => {
               const node = document.createElement('div');
               node.className = 'skill-icon';
               node.dataset.nested = '1';
               node.dataset.nome = it.name || ;
               node.dataset.parentIndex = parentIndexSnapshot;
               node.dataset.subName = it.name || ;
               const subSlug = slugify(it.name || );
               if (subSlug) node.dataset.slug = subSlug;
               if (it.level) node.dataset.level = it.level;
               if (it.desc) node.dataset.desc = it.desc;
               if (it.descPt) node.dataset.descPt = it.descPt;
               if (it.descEn) node.dataset.descEn = it.descEn;
               if (it.descEs) node.dataset.descEs = it.descEs;
               if (it.descPl) node.dataset.descPl = it.descPl;
               if (it.video) node.dataset.video = it.video;
               if (it.subs) node.dataset.subs = JSON.stringify(it.subs);
               if (it.subattrs) node.dataset.subattrs = JSON.stringify(it.subattrs);
               if (it.flags) node.dataset.flags = JSON.stringify(it.flags);
               if (it.back) node.dataset.back = it.back;
               if (it.weapon) {
                   try {
                       node.dataset.weapon = JSON.stringify(it.weapon);
                   } catch (e) {
                       console.error('[Skills] Erro ao serializar weapon de subskill', it.name, e);
                   }
               }
               const img = document.createElement('img');
               img.alt = ;
               img.src = it.iconURL;
               node.appendChild(img);
               fragment.appendChild(node);
           });
           const templateClone = fragment.cloneNode(true);
           iconsBar.innerHTML = ;
           iconsBar.appendChild(fragment);
           animateIconsBarEntrance();
           wireClicksForCurrentBar();
           setupWeaponBarToggle(hasWeaponSkillAvailable);
           const b2 = ensureBackButton();
           if (b2) b2.classList.add('peek');
           if (cacheKey) {
               subBarTemplateCache.set(cacheKey, {
                   template: templateClone, lang: langKey
               });
           }
       } window.addEventListener('gla:langChanged', () => {
           subBarTemplateCache.clear();
           const skillsRoot = document.getElementById('skills');
           const i18nMap = skillsRoot ? JSON.parse(skillsRoot.dataset.i18nAttrs || '{}') : {
           };
           const lang = getLangKey();
           Array.from(iconsBar.querySelectorAll('.skill-icon')).forEach(icon => {
               const pack = {
                   pt: icon.dataset.descPt || , en: icon.dataset.descEn || , es: icon.dataset.descEs || , pl: icon.dataset.descPl || 
               };
               const chosen = (pack[lang] || pack.pt || pack.en || pack.es || pack.pl || icon.dataset.desc || ).trim();
               if (chosen) icon.dataset.desc = chosen;
           });
           barStack.forEach(frame => {
               (frame.items || []).forEach(it => {
                   const pack = {
                       pt: it.descPt, en: it.descEn, es: it.descEs, pl: it.descPl
                   };
                   const chosen = (pack[lang] || pack.pt || pack.en || pack.es || pack.pl || it.desc || );
                   it.desc = chosen;
               });
           });
           if (descBox) {
               applyFlagTooltips(descBox);
           } const activeIcon = window.__lastActiveSkillIcon;
           if (activeIcon && activeIcon.dataset.weapon) {
               activateSkill(activeIcon, {
                   openSubs: false
               });
           }
       });
       wireClicksForCurrentBar();
       const b0 = ensureBackButton();
       if (b0) {
           b0.classList.add('peek');
           b0.style.alignSelf = 'stretch';
       } (function initSkillTooltip() {
           if (document.querySelector('.skill-tooltip')) return;
           const tip = document.createElement('div');
           tip.className = 'skill-tooltip';
           tip.setAttribute('role', 'tooltip');
           tip.setAttribute('aria-hidden', 'true');
           document.body.appendChild(tip);
           const lockUntilRef = {
               value: 0
           };
           function measureAndPos(el) {
               if (!el || tip.getAttribute('aria-hidden') === 'true') return;
               tip.style.left = '0px';
               tip.style.top = '0px';
               const rect = el.getBoundingClientRect();
               const tr = tip.getBoundingClientRect();
               let left = Math.round(rect.left + (rect.width - tr.width) / 2);
               left = Math.max(8, Math.min(left, window.innerWidth - tr.width - 8));
               const coarse = (window.matchMedia && matchMedia('(pointer: coarse)').matches) || (window.innerWidth <= 600);
               let top = coarse ? Math.round(rect.bottom + 10) : Math.round(rect.top - tr.height - 8);
               if (top < 8) top = Math.round(rect.bottom + 10);
               tip.style.left = left + 'px';
               tip.style.top = top + 'px';
           } function show(el, text) {
               tip.textContent = text || ;
               tip.setAttribute('aria-hidden', 'false');
               measureAndPos(el);
               tip.style.opacity = '1';
           } function hide() {
               tip.setAttribute('aria-hidden', 'true');
               tip.style.opacity = '0';
               tip.style.left = '-9999px';
               tip.style.top = '-9999px';
           } window.__globalSkillTooltip = {
               show, hide, measureAndPos, lockUntil: lockUntilRef
           };
           Array.from(document.querySelectorAll('.icon-bar .skill-icon')).forEach(icon => {
               if (icon.dataset.weaponToggle === '1' || icon.classList.contains('weapon-bar-toggle')) return;
               if (icon.dataset.tipwired) return;
               icon.dataset.tipwired = '1';
               const label = icon.dataset.nome || icon.dataset.name || icon.title || ;
               if (label && !icon.hasAttribute('aria-label')) icon.setAttribute('aria-label', label);
               if (icon.hasAttribute('title')) icon.removeAttribute('title');
               const img = icon.querySelector('img');
               if (img) {
                   const imgAlt = img.getAttribute('alt') || ;
                   const imgTitle = img.getAttribute('title') || ;
                   if (!label && (imgAlt || imgTitle)) icon.setAttribute('aria-label', imgAlt || imgTitle);
                   img.setAttribute('alt', );
                   if (img.hasAttribute('title')) img.removeAttribute('title');
               } icon.addEventListener('mouseenter', () => show(icon, label));
               icon.addEventListener('mousemove', () => {
                   if (performance.now() >= lockUntilRef.value) measureAndPos(icon);
               });
               icon.addEventListener('click', () => {
                   lockUntilRef.value = performance.now() + 240;
                   measureAndPos(icon);
               });
               icon.addEventListener('mouseleave', hide);
           });
           Array.from(document.querySelectorAll('.subskills-rail .subicon')).forEach(sub => {
               if (sub.dataset.tipwired) return;
               sub.dataset.tipwired = '1';
               const label = sub.getAttribute('title') || sub.getAttribute('aria-label') || ;
               if (label && !sub.hasAttribute('aria-label')) sub.setAttribute('aria-label', label);
               if (sub.hasAttribute('title')) sub.removeAttribute('title');
               sub.addEventListener('mouseenter', () => show(sub, label));
               sub.addEventListener('mousemove', () => {
                   if (performance.now() >= lockUntilRef.value) measureAndPos(sub);
               });
               sub.addEventListener('click', () => {
                   lockUntilRef.value = performance.now() + 240;
                   measureAndPos(sub);
               });
               sub.addEventListener('mouseleave', hide);
           });
           window.addEventListener('scroll', () => {
               const visible = document.querySelector('.skill-tooltip[aria-hidden="false"]');
               if (!visible) return;
               const target = document.querySelector('.subskills-rail .subicon:hover') || document.querySelector('.subskills-rail .subicon.active') || document.querySelector('.icon-bar .skill-icon:hover') || document.querySelector('.icon-bar .skill-icon.active');
               measureAndPos(target);
           }, true);
           window.addEventListener('resize', () => {
               const target = document.querySelector('.subskills-rail .subicon:hover') || document.querySelector('.subskills-rail .subicon.active') || document.querySelector('.icon-bar .skill-icon:hover') || document.querySelector('.icon-bar .skill-icon.active');
               measureAndPos(target);
           });
       })();
       (function initTabs() {
           const tabs = Array.from(document.querySelectorAll('.tab-btn'));
           if (!tabs.length) return;
           const contents = Array.from(document.querySelectorAll('.tab-content'));
           const characterBox = document.querySelector('.character-box');
           let wrapper = characterBox.querySelector('.tabs-height-wrapper');
           if (!wrapper) {
               wrapper = document.createElement('div');
               wrapper.className = 'tabs-height-wrapper';
               contents.forEach(c => {
                   wrapper.appendChild(c);
               });
               const tabsElement = characterBox.querySelector('.character-tabs');
               if (tabsElement && tabsElement.nextSibling) {
                   characterBox.insertBefore(wrapper, tabsElement.nextSibling);
               } else {
                   characterBox.appendChild(wrapper);
               }
           } async function smoothHeightTransition(fromTab, toTab) {
               if (!wrapper) return Promise.resolve();
               const scrollY = window.scrollY;
               const currentHeight = wrapper.getBoundingClientRect().height;
               await new Promise((resolve) => {
                   const videoContainers = toTab.querySelectorAll('.video-container');
                   const contentCard = toTab.querySelector('.content-card');
                   if (videoContainers.length === 0) {
                       requestAnimationFrame(() => {
                           requestAnimationFrame(() => {
                               requestAnimationFrame(() => resolve());
                           });
                       });
                       return;
                   } let lastHeight = 0;
                   let stableCount = 0;
                   const checksNeeded = 3;
                   let totalChecks = 0;
                   const maxChecks = 15;
                   function checkStability() {
                       totalChecks++;
                       const currentTabHeight = toTab.scrollHeight;
                       if (Math.abs(currentTabHeight - lastHeight) < 5) {
                           stableCount++;
                       } else {
                           stableCount = 0;
                       } lastHeight = currentTabHeight;
                       if (stableCount >= checksNeeded || totalChecks >= maxChecks) {
                           resolve();
                       } else {
                           setTimeout(checkStability, 50);
                       }
                   } setTimeout(checkStability, 50);
               });
               const nextHeight = toTab.getBoundingClientRect().height;
               const finalHeight = Math.max(nextHeight, 100);
               if (Math.abs(finalHeight - currentHeight) < 30) {
                   wrapper.style.height = ;
                   return Promise.resolve();
               } wrapper.style.overflow = 'hidden';
               wrapper.style.height = currentHeight + 'px';
               wrapper.offsetHeight;
               wrapper.style.transition = 'height 0.3s cubic-bezier(0.4, 0, 0.2, 1)';
               requestAnimationFrame(() => {
                   wrapper.style.height = finalHeight + 'px';
               });
               return new Promise(resolve => {
                   setTimeout(() => {
                       wrapper.style.height = ;
                       wrapper.style.transition = ;
                       wrapper.style.overflow = ;
                       resolve();
                   }, 320);
               });
           } tabs.forEach(btn => {
               if (btn.dataset.wiredTab) return;
               btn.dataset.wiredTab = '1';
               btn.addEventListener('click', () => {
                   const target = btn.getAttribute('data-tab');
                   const currentActive = contents.find(c => c.classList.contains('active'));
                   const nextActive = contents.find(c => c.id === target);
                   if (currentActive === nextActive) return;
                   document.body.classList.add('transitioning-tabs');
                   if (currentActive) {
                       currentActive.style.opacity = '0';
                       currentActive.style.transform = 'translateY(-8px)';
                   } setTimeout(async () => {
                       contents.forEach(c => {
                           if (c !== nextActive) {
                               c.style.display = 'none';
                               c.classList.remove('active');
                           }
                       });
                       tabs.forEach(b => b.classList.toggle('active', b === btn));
                       if (nextActive) {
                           nextActive.classList.add('active');
                           nextActive.style.display = 'block';
                           nextActive.style.opacity = '0';
                           nextActive.style.visibility = 'hidden';
                           nextActive.offsetHeight;
                           try {
                               if (target === 'skills') {
                                   const tabEl = document.getElementById(target);
                                   if (tabEl) {
                                       const activeIcon = tabEl.querySelector('.icon-bar .skill-icon.active');
                                       const firstIcon = tabEl.querySelector('.icon-bar .skill-icon');
                                       const toClick = activeIcon || firstIcon;
                                       if (toClick) {
                                           const had = document.body.dataset.suppressSkillPlay;
                                           document.body.dataset.suppressSkillPlay = '1';
                                           toClick.click();
                                           if (had) document.body.dataset.suppressSkillPlay = had;
                                       }
                                   }
                               }
                           } catch (e) {
                           }
                       } if (currentActive && nextActive) {
                           await smoothHeightTransition(currentActive, nextActive);
                       } if (nextActive) {
                           nextActive.style.visibility = ;
                           nextActive.style.transform = 'translateY(12px)';
                           requestAnimationFrame(() => {
                               nextActive.style.opacity = '1';
                               nextActive.style.transform = 'translateY(0)';
                               setTimeout(() => {
                                   nextActive.style.opacity = ;
                                   nextActive.style.transform = ;
                                   document.body.classList.remove('transitioning-tabs');
                                   try {
                                       delete document.body.dataset.suppressSkillPlay;
                                   } catch {
                                   }
                               }, 300);
                           });
                       }
                   }, 120);
                   setTimeout(() => {
                       syncDescHeight();
                       if (target === 'skins') {
                           videosCache.forEach(v => {
                               try {
                                   v.pause();
                               } catch (e) {
                               } v.style.display = 'none';
                           });
                           if (videoBox) {
                               videoBox.querySelectorAll('video.skill-video').forEach(v => {
                                   try {
                                       v.pause();
                                   } catch (e) {
                                   } v.style.display = 'none';
                               });
                           } if (window.__subskills) window.__subskills.hideAll?.(videoBox);
                           if (videoBox && placeholder) {
                               placeholder.style.display = 'none';
                               placeholder.classList.add('fade-out');
                           }
                       } else {
                           const activeIcon = document.querySelector('.icon-bar .skill-icon.active');
                           if (activeIcon) activeIcon.click();
                       }
                   }, 450);
               });
           });
       })();
       (function initSkinsArrows() {
           const carousel = $('.skins-carousel');
           const wrapper = $('.skins-carousel-wrapper');
           const left = $('.skins-arrow.left');
           const right = $('.skins-arrow.right');
           if (!carousel || !left || !right || !wrapper) return;
           if (wrapper.dataset.wired) return;
           wrapper.dataset.wired = '1';
           const scrollAmt = () => Math.round(carousel.clientWidth * 0.6);
           function setState() {
               const max = carousel.scrollWidth - carousel.clientWidth;
               const x = carousel.scrollLeft;
               const hasLeft = x > 5, hasRight = x < max - 5;
               left.style.display = hasLeft ? 'inline-block' : 'none';
               right.style.display = hasRight ? 'inline-block' : 'none';
               wrapper.classList.toggle('has-left', hasLeft);
               wrapper.classList.toggle('has-right', hasRight);
               carousel.style.justifyContent = (!hasLeft && !hasRight) ? 'center' : ;
           } function go(dir) {
               const max = carousel.scrollWidth - carousel.clientWidth;
               const next = dir < 0 ? Math.max(0, carousel.scrollLeft - scrollAmt()) : Math.min(max, carousel.scrollLeft + scrollAmt());
               carousel.scrollTo({
                   left: next, behavior: 'smooth'
               });
           } left.addEventListener('click', () => go(-1));
           right.addEventListener('click', () => go(1));
           carousel.addEventListener('scroll', setState);
           new ResizeObserver(setState).observe(carousel);
           setState();
       })();
       function renderAttributes(str) {
           const skillsRoot = document.getElementById('skills');
           const i18nMap = skillsRoot ? JSON.parse(skillsRoot.dataset.i18nAttrs || '{}') : {
           };
           const langRaw = (document.documentElement.lang || skillsRoot?.dataset.i18nDefault || 'pt').toLowerCase();
           const langKey = i18nMap[langRaw] ? langRaw : (i18nMap[langRaw.split('-')[0]] ? langRaw.split('-')[0] : 'pt');
           const L = i18nMap[langKey] || i18nMap.pt || {
               cooldown: 'Recarga', energy_gain: 'Ganho de energia', energy_cost: 'Custo de energia', power: 'Poder', power_pvp: 'Poder PvP', level: 'Nível'
           };
           const vals = (str || ).split(',').map(v => v.trim());
           // Processa valores, tratando strings vazias como NaN
           // IMPORTANTE: ordem fixa é [powerpve, powerpvp, energy, cooldown]
           const pve = (vals[0] && vals[0] !== ) ? parseFloat(vals[0]) : NaN;
           const pvp = (vals[1] && vals[1] !== ) ? parseFloat(vals[1]) : NaN;
           const ene = (vals[2] && vals[2] !== ) ? parseFloat(vals[2]) : NaN;
           const cd = (vals[3] && vals[3] !== ) ? parseFloat(vals[3]) : NaN;
           // Debug: log se houver problema na ordem
           if (str && str.includes(',') && !isNaN(cd) && !isNaN(pvp) && cd === pvp) {
               console.warn('[Skills] renderAttributes: possível problema na ordem dos atributos', { str, vals, pve, pvp, ene, cd });
           }
           const rows = [];
           // Ordem de exibição: cooldown, energy, power, power_pvp
           if (!isNaN(cd)) rows.push([L.cooldown, cd]);
           if (!isNaN(ene) && ene !== 0) {
               const label = ene > 0 ? L.energy_gain : L.energy_cost;
               rows.push([label, Math.abs(ene)]);
           }
           if (!isNaN(pve)) rows.push([L.power, pve]);
           if (!isNaN(pvp)) rows.push([L.power_pvp, pvp]);
           // Debug: log se houver valores suspeitos
           if (str && str.includes(',')) {
               console.log('[Skills] renderAttributes processed', {
                   str,
                   vals: vals.slice(0, 4),
                   parsed: { pve, pvp, ene, cd },
                   rows: rows.map(r => r[0])
               });
           }
           if (!rows.length) return ;

const html = rows.map(([label, value]) => `

${label}${value}

`).join(); return `

${html}

`;

       } function syncDescHeight() {
       } window.addEventListener('resize', syncDescHeight);
       if (videoBox) new ResizeObserver(syncDescHeight).observe(videoBox);
       iconItems.forEach(el => {
           const wired = !!el.dataset._sync_wired;
           if (wired) return;
           el.dataset._sync_wired = '1';
           el.addEventListener('click', () => {
               Promise.resolve().then(syncDescHeight);
           });
       });
       if (iconsBar) addOnce(iconsBar, 'wheel', (e) => {
           if (e.deltaY) {
               e.preventDefault();
               iconsBar.scrollLeft += e.deltaY;
           }
       });
       wireClicksForCurrentBar();
       if (iconItems.length) {
           const first = iconItems[0];
           if (first) {
               activateSkill(first, {
                   openSubs: false
               });
           }
       } setTimeout(() => {
           Array.from(document.querySelectorAll('.skill-icon')).forEach(el => {
           });
           videosCache.forEach((v, idx) => {
               const src = v.querySelector('source') ? v.querySelector('source').src : v.src;
               v.addEventListener('error', (ev) => {
               });
               v.addEventListener('loadedmetadata', () => {
               });
           });
       }, 600);
   })();

</script>