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

De Wiki Gla
Ir para navegação Ir para pesquisar
Etiqueta: Reversão manual
Linha 1: Linha 1:
<!-- SUBSKILLS SYSTEM -->
<!-- SUBSKILLS SYSTEM COMPATÍVEL COM MÓDULO:INFO.SKILLS -->
<script>
<script>
     (function () {
     (function () {
         const SUBVIDEO_DEBUG = false; // Desabilitado para melhor performance
         const SUBVIDEO_DEBUG = true; // Ativado para debug
         function logSubVideo(...args) {
         function logSubVideo(...args) {
             if (!SUBVIDEO_DEBUG) return;
             if (!SUBVIDEO_DEBUG) return;
             // console.log('[SubVideo]', ...args);
             console.log('[SubVideo]', ...args);
         }
         }


         const api = (window.__subskills ||= {});
         const api = (window.__subskills ||= {});
        // Cache global de elementos de vídeo para subskills - baseado em URL
         const subskillVideoElementCache = new Map();
         const subskillVideoElementCache = new Map(); // key: videoURL (string), value: HTMLVideoElement
         const imagePreloadCache = new Map();
         const imagePreloadCache = new Map();
         let subRail, subBar, spacer;
         let subRail, subBar, spacer;
        let cachedMainSkills = null;


         // Cache das skills principais (capturado na carga da página)
         // ===== DEBUG ESTRUTURA =====
         let cachedMainSkills = null;
        const DEBUG = true;
        function debugLog(...args) {
            if (DEBUG) console.log('[Subskills]', ...args);
         }


         // ===== HERANÇA DE ATRIBUTOS: busca dados das skills principais =====
         // ===== FUNÇÕES AUXILIARES =====
         function getMainSkillsMap() {
         function getMainSkillsMap() {
            // Retorna cache se já foi construído E tem dados
             if (cachedMainSkills && cachedMainSkills.byIndex.size > 0) {
             if (cachedMainSkills && cachedMainSkills.byIndex.size > 0) {
                 return cachedMainSkills;
                 return cachedMainSkills;
Linha 29: Linha 31:
             };
             };


            // Busca skills com data-index (skills principais, não subskills)
             const icons = document.querySelectorAll('.icon-bar .skill-icon[data-index][data-nome]');
             const icons = document.querySelectorAll('.icon-bar .skill-icon[data-index][data-nome]');


Linha 36: Linha 37:
                 if (!name) return;
                 if (!name) return;


                // Extrai atributos do data-atr (formato: "pve, pvp, energy, cd")
                 const atrRaw = icon.dataset.atr || '';
                 const atrRaw = icon.dataset.atr || '';
                 const parts = atrRaw.split(',').map(x => (x || '').trim());
                 const parts = atrRaw.split(',').map(x => (x || '').trim());
Linha 44: Linha 44:
                 const cooldown = parts[3] && parts[3] !== '-' ? parts[3] : '';
                 const cooldown = parts[3] && parts[3] !== '-' ? parts[3] : '';


                // Nome original do arquivo de ícone (armazenado no dataset pelo widget de skills)
                 let iconFile = (icon.dataset.iconFile || '').trim();
                 let iconFile = (icon.dataset.iconFile || '').trim();
                 if (!iconFile) {
                 if (!iconFile) {
Linha 52: Linha 51:
                 }
                 }


                // Nome original do arquivo de vídeo (caso exista)
                 let videoFile = (icon.dataset.videoFile || '').trim();
                 let videoFile = (icon.dataset.videoFile || '').trim();
                 if (!videoFile) {
                 if (!videoFile) {
Linha 63: Linha 61:


                 const data = {
                 const data = {
                     name: name, // Inclui o nome para herança completa
                     name: name,
                     icon: iconFile,
                     icon: iconFile,
                     level: icon.dataset.level || '',
                     level: icon.dataset.level || '',
Linha 73: Linha 71:
                 };
                 };


                // Mantém descrições caso precise como fallback extra
                 if (icon.dataset.descPt) data.descPt = icon.dataset.descPt;
                 if (icon.dataset.descPt) data.descPt = icon.dataset.descPt;
                 if (icon.dataset.descEn) data.descEn = icon.dataset.descEn;
                 if (icon.dataset.descEn) data.descEn = icon.dataset.descEn;
Linha 81: Linha 78:
                 maps.byName.set(name, data);
                 maps.byName.set(name, data);
                 if (index) {
                 if (index) {
                    // Guarda tanto como string quanto como número para compatibilidade
                     maps.byIndex.set(index, data);
                     maps.byIndex.set(index, data);
                     maps.byIndex.set(parseInt(index, 10), data);
                     maps.byIndex.set(parseInt(index, 10), data);
                 }
                 }
             });
             });


            // Cacheia para uso futuro (importante: as skills principais não mudam)
             cachedMainSkills = maps;
             cachedMainSkills = maps;
            debugLog('Main skills map built:', maps);
             return maps;
             return maps;
         }
         }


         // Aplica herança COMPLETA: se subskill só tem refM, busca TUDO da skill principal
         // Processa estrutura aninhada do módulo
        function processModuleNestedStructure(subs, level = 1) {
            if (!Array.isArray(subs) || level > 3) {
                debugLog('processModuleNestedStructure: invalid input or max level', { subs, level });
                return subs || [];
            }
 
            const processed = subs.map(sub => {
                if (!sub || typeof sub !== 'object') return sub;
               
                const result = { ...sub };
               
                // O módulo usa 'subs' para sub-subskills
                if (result.subs && Array.isArray(result.subs)) {
                    debugLog(`Processing nested subs at level ${level} for:`, result.n || result.name);
                    result.subs = processModuleNestedStructure(result.subs, level + 1);
                    result.hasChildren = true;
                    result.childrenCount = result.subs.length;
                } else {
                    result.hasChildren = false;
                }
               
                // Remove campos desnecessários
                delete result.subskills; // Se existir do módulo antigo
               
                return result;
            });
           
            debugLog(`Processed level ${level}:`, processed);
            return processed;
        }
 
        // Versão melhorada da herança
         function applyInheritance(sub, mainSkills) {
         function applyInheritance(sub, mainSkills) {
             let name = (sub.name || sub.n || '').trim();
             let name = (sub.name || sub.n || '').trim();
Linha 99: Linha 126:


             let main = null;
             let main = null;
            // Tenta como string
             if (refIndex && mainSkills.byIndex.has(refIndex)) {
             if (refIndex && mainSkills.byIndex.has(refIndex)) {
                 main = mainSkills.byIndex.get(refIndex);
                 main = mainSkills.byIndex.get(refIndex);
             }
             }
            // Tenta como número
             if (!main && refIndex) {
             if (!main && refIndex) {
                 const numIndex = parseInt(refIndex, 10);
                 const numIndex = parseInt(refIndex, 10);
Linha 114: Linha 139:
             }
             }


            // Se não tem main skill para herdar, retorna como está
             if (!main) {
             if (!main) {
                debugLog('No inheritance for:', { name, refIndex });
                 return sub;
                 return sub;
             }
             }


            // Se não tem nome mas tem refM, herda o nome da skill principal
             if (!name && refIndex && main.name) {
             if (!name && refIndex && main.name) {
                 name = main.name;
                 name = main.name;
             }
             }


            // Helper para verificar se um valor existe e não é string vazia
            // Para números, permite 0 como valor válido
             const hasValue = (val) => {
             const hasValue = (val) => {
                 if (val === undefined || val === null) return false;
                 if (val === undefined || val === null) return false;
                 if (typeof val === 'number') return !isNaN(val); // Permite 0, mas não NaN
                 if (typeof val === 'number') return !isNaN(val);
                 const str = String(val).trim();
                 const str = String(val).trim();
                 return str !== '' && str !== 'NaN';
                 return str !== '' && str !== 'NaN';
             };
             };


             // Vídeo NUNCA é herdado da skill principal
             // Vídeo: se existe no sub (mesmo que vazio), usa; senão, não herda
            // Se a chave 'video' existe no objeto = foi especificado pelo editor (mesmo que vazio)
            // Se a chave não existe = não foi especificado = não herda, fica vazio
             const hasOwnVideo = Object.prototype.hasOwnProperty.call(sub, 'video');
             const hasOwnVideo = Object.prototype.hasOwnProperty.call(sub, 'video');
             const finalVideo = hasOwnVideo ? (sub.video || '') : '';
             const finalVideo = hasOwnVideo ? (sub.video || '') : '';


             return {
             const inherited = {
                 ...sub,
                 ...sub,
                 name: name || main.name || sub.name,
                 name: name || main.name || sub.name,
                 icon: (sub.icon && sub.icon !== 'Nada.png' && sub.icon !== '') ? sub.icon : (main.icon || 'Nada.png'),
                 icon: (sub.icon && sub.icon !== 'Nada.png' && sub.icon !== '') ? sub.icon : (main.icon || 'Nada.png'),
                 level: hasValue(sub.level) ? sub.level : main.level,
                 level: hasValue(sub.level) ? sub.level : main.level,
                 video: finalVideo, // SIMPLIFICADO
                 video: finalVideo,
                 powerpve: (sub.powerpve !== undefined && sub.powerpve !== null) ? sub.powerpve : main.powerpve,
                 powerpve: (sub.powerpve !== undefined && sub.powerpve !== null) ? sub.powerpve : main.powerpve,
                 powerpvp: (sub.powerpvp !== undefined && sub.powerpvp !== null) ? sub.powerpvp : main.powerpvp,
                 powerpvp: (sub.powerpvp !== undefined && sub.powerpvp !== null) ? sub.powerpvp : main.powerpvp,
Linha 154: Linha 174:
                 descPl: sub.descPl || (sub.desc_i18n?.pl) || main.descPl
                 descPl: sub.descPl || (sub.desc_i18n?.pl) || main.descPl
             };
             };
            // Processa sub-subskills recursivamente
            if (sub.subs && Array.isArray(sub.subs)) {
                inherited.subs = sub.subs.map(nested => applyInheritance(nested, mainSkills));
                inherited.hasChildren = true;
                inherited.childrenCount = inherited.subs.length;
            }
            debugLog('Inheritance applied:', { original: sub.name || sub.n, inherited: inherited.name });
            return inherited;
        }
        // ===== RENDERIZAÇÃO DE NÍVEIS ANINHADOS =====
        function renderNestedSubskills(nestedSubs, parentElement, parentName, parentIdx) {
            debugLog('renderNestedSubskills called:', {
                parentName,
                nestedCount: nestedSubs?.length,
                parentElement
            });
            // Remove existente
            const existing = parentElement.querySelector('.subskills-nested');
            if (existing) existing.remove();
            if (!nestedSubs || !Array.isArray(nestedSubs) || nestedSubs.length === 0) {
                debugLog('No nested subs to render');
                return;
            }
            // Cria container
            const nestedContainer = document.createElement('div');
            nestedContainer.className = 'subskills-nested';
            nestedContainer.dataset.parent = parentName;
            nestedContainer.dataset.level = '2';
            nestedContainer.dataset.parentIdx = parentIdx;
            // Estilos
            Object.assign(nestedContainer.style, {
                position: 'absolute',
                top: '100%',
                left: '50%',
                transform: 'translateX(-50%)',
                zIndex: '1000',
                background: 'rgba(0, 0, 0, 0.95)',
                borderRadius: '8px',
                padding: '8px',
                display: 'flex',
                gap: '6px',
                flexWrap: 'wrap',
                minWidth: '200px',
                boxShadow: '0 4px 20px rgba(0,0,0,0.7)',
                border: '1px solid rgba(255, 217, 90, 0.4)',
                marginTop: '5px'
            });
            // Herança das skills principais
            const mainSkills = getMainSkillsMap();
            // Processa cada sub-subskill
            nestedSubs.forEach((sub, index) => {
                const inheritedSub = applyInheritance(sub, mainSkills);
                const subName = inheritedSub.name || inheritedSub.n || '';
                const hasNested = inheritedSub.subs && Array.isArray(inheritedSub.subs) && inheritedSub.subs.length > 0;
                // Cria elemento
                const nestedItem = document.createElement('div');
                nestedItem.className = 'subicon nested-subicon';
                nestedItem.title = subName;
                nestedItem.dataset.parent = parentName;
                nestedItem.dataset.name = subName;
                nestedItem.dataset.index = index;
                nestedItem.dataset.subskillData = JSON.stringify(inheritedSub);
                // Ícone
                const img = document.createElement('img');
                img.src = filePathURL(inheritedSub.icon || 'Nada.png');
                img.alt = '';
                img.width = 36;
                img.height = 36;
                img.decoding = 'async';
                img.loading = 'lazy';
                nestedItem.appendChild(img);
                // Indicador se tiver mais níveis
                if (hasNested) {
                    const indicator = document.createElement('div');
                    indicator.className = 'subskill-indicator nested-indicator';
                    indicator.innerHTML = '▶';
                    nestedItem.appendChild(indicator);
                    nestedItem.classList.add('has-nested-children');
                }
                // Evento de clique
                nestedItem.addEventListener('click', function(e) {
                    e.stopPropagation();
                    e.preventDefault();
                    const clickedData = JSON.parse(this.dataset.subskillData || '{}');
                    const L = getLabels();
                    const subName = clickedData.name || clickedData.n || '';
                    debugLog('Nested sub clicked:', { subName, hasNested });
                    // Atualiza descrição/vídeo
                    updateSkillDisplay(clickedData);
                    // Marca como ativa
                    nestedContainer.querySelectorAll('.nested-subicon').forEach(el => {
                        el.classList.remove('active');
                    });
                    this.classList.add('active');
                    // Se tiver sub-sub-subskills, renderiza terceiro nível
                    if (hasNested && clickedData.subs) {
                        renderThirdLevelSubskills(clickedData.subs, this, subName);
                    } else {
                        const thirdLevel = this.querySelector('.subskills-third-level');
                        if (thirdLevel) thirdLevel.remove();
                    }
                });
                nestedContainer.appendChild(nestedItem);
            });
            // Adiciona ao DOM
            parentElement.style.position = 'relative';
            parentElement.appendChild(nestedContainer);
            // Fecha ao clicar fora
            setTimeout(() => {
                const closeHandler = (e) => {
                    if (!parentElement.contains(e.target) && !nestedContainer.contains(e.target)) {
                        nestedContainer.remove();
                        document.removeEventListener('click', closeHandler);
                    }
                };
                document.addEventListener('click', closeHandler);
            }, 10);
            debugLog('Nested subskills rendered:', nestedContainer.children.length);
        }
        // Terceiro nível (sub-sub-subskills)
        function renderThirdLevelSubskills(thirdLevelSubs, parentElement, parentName) {
            const existing = parentElement.querySelector('.subskills-third-level');
            if (existing) existing.remove();
            if (!thirdLevelSubs || !Array.isArray(thirdLevelSubs) || thirdLevelSubs.length === 0) {
                return;
            }
            const thirdContainer = document.createElement('div');
            thirdContainer.className = 'subskills-third-level';
            thirdContainer.dataset.parent = parentName;
            thirdContainer.dataset.level = '3';
            Object.assign(thirdContainer.style, {
                position: 'absolute',
                top: '0',
                left: '100%',
                marginLeft: '5px',
                zIndex: '1001',
                background: 'rgba(0, 0, 0, 0.98)',
                borderRadius: '8px',
                padding: '6px',
                display: 'flex',
                flexDirection: 'column',
                gap: '4px',
                minWidth: '180px',
                boxShadow: '0 4px 20px rgba(0,0,0,0.8)',
                border: '1px solid rgba(255, 217, 90, 0.3)'
            });
            const mainSkills = getMainSkillsMap();
            thirdLevelSubs.forEach((sub, index) => {
                const inheritedSub = applyInheritance(sub, mainSkills);
                const subName = inheritedSub.name || inheritedSub.n || '';
                const thirdItem = document.createElement('div');
                thirdItem.className = 'subicon third-level-subicon';
                thirdItem.title = subName;
                thirdItem.dataset.parent = parentName;
                thirdItem.dataset.name = subName;
                thirdItem.dataset.subskillData = JSON.stringify(inheritedSub);
                Object.assign(thirdItem.style, {
                    display: 'flex',
                    alignItems: 'center',
                    gap: '8px',
                    padding: '4px 8px',
                    borderRadius: '4px',
                    cursor: 'pointer',
                    transition: 'background 0.2s'
                });
                // Ícone pequeno
                const img = document.createElement('img');
                img.src = filePathURL(inheritedSub.icon || 'Nada.png');
                img.alt = '';
                img.width = 24;
                img.height = 24;
                img.style.borderRadius = '4px';
                thirdItem.appendChild(img);
                // Nome
                const nameSpan = document.createElement('span');
                nameSpan.textContent = subName;
                nameSpan.style.color = '#fff';
                nameSpan.style.fontSize = '12px';
                thirdItem.appendChild(nameSpan);
                // Eventos
                thirdItem.addEventListener('mouseenter', () => {
                    thirdItem.style.background = 'rgba(255, 217, 90, 0.1)';
                });
                thirdItem.addEventListener('mouseleave', () => {
                    thirdItem.style.background = 'transparent';
                });
                thirdItem.addEventListener('click', function(e) {
                    e.stopPropagation();
                    e.preventDefault();
                    const clickedData = JSON.parse(this.dataset.subskillData || '{}');
                    const L = getLabels();
                    const subName = clickedData.name || clickedData.n || '';
                    updateSkillDisplay(clickedData);
                    thirdContainer.querySelectorAll('.third-level-subicon').forEach(el => {
                        el.style.background = 'transparent';
                    });
                    this.style.background = 'rgba(255, 217, 90, 0.2)';
                });
                thirdContainer.appendChild(thirdItem);
            });
            parentElement.appendChild(thirdContainer);
         }
         }


        // ===== FUNÇÕES EXISTENTES (modificadas) =====
         function filePathURL(fileName) {
         function filePathURL(fileName) {
            // Evita requisições para Nada.png que não existe
             if (!fileName || fileName.trim() === '' || fileName === 'Nada.png' || fileName.toLowerCase() === 'nada.png') {
             if (!fileName || fileName.trim() === '' || fileName === 'Nada.png' || fileName.toLowerCase() === 'nada.png') {
                 return '';
                 return '';
Linha 165: Linha 425:
                 ? mw.util.wikiScript()
                 ? mw.util.wikiScript()
                 : (window.mw && mw.config ? (mw.config.get('wgScript') || '/index.php') : '/index.php');
                 : (window.mw && mw.config ? (mw.config.get('wgScript') || '/index.php') : '/index.php');
            // Garante HTTPS para evitar Mixed Content
             let url = `${base}?title=Especial:FilePath/${f}`;
             let url = `${base}?title=Especial:FilePath/${f}`;
             if (window.location.protocol === 'https:' && url.startsWith('http://')) {
             if (window.location.protocol === 'https:' && url.startsWith('http://')) {
Linha 216: Linha 475:
         }
         }


        // Verifica se o modo weapon está ativo
         function isWeaponModeOn() {
         function isWeaponModeOn() {
             try {
             try {
Linha 225: Linha 483:
         }
         }


        // Retorna os atributos corretos (weapon ou normal)
         function getEffectiveAttrs(s) {
         function getEffectiveAttrs(s) {
             const weaponOn = isWeaponModeOn();
             const weaponOn = isWeaponModeOn();
Linha 244: Linha 501:
         }
         }


        // Retorna a descrição correta (weapon ou normal)
        // Aceita tanto desc_i18n quanto desc para compatibilidade
         function getEffectiveDesc(s) {
         function getEffectiveDesc(s) {
             const weaponOn = isWeaponModeOn();
             const weaponOn = isWeaponModeOn();
Linha 251: Linha 506:
             const lang = raw === 'pt-br' ? 'pt' : (raw.split('-')[0] || 'pt');
             const lang = raw === 'pt-br' ? 'pt' : (raw.split('-')[0] || 'pt');


            // Para weapon: aceita tanto desc_i18n quanto desc
             if (weaponOn && s.weapon) {
             if (weaponOn && s.weapon) {
                 const wDesc = s.weapon.desc_i18n || s.weapon.desc;
                 const wDesc = s.weapon.desc_i18n || s.weapon.desc;
Linha 259: Linha 513:
             }
             }


            // Para descrição base: aceita tanto desc_i18n quanto desc
             const base = s.desc_i18n || s.desc;
             const base = s.desc_i18n || s.desc;
             if (base) {
             if (base) {
Linha 265: Linha 518:
             }
             }


            // Fallback para campos individuais (compatibilidade)
             const descI18n = {
             const descI18n = {
                 pt: s.descPt || '',
                 pt: s.descPt || '',
Linha 275: Linha 527:
         }
         }


        // Retorna o vídeo correto (weapon ou normal)
         function getEffectiveVideo(s) {
         function getEffectiveVideo(s) {
             const weaponOn = isWeaponModeOn();
             const weaponOn = isWeaponModeOn();
Linha 284: Linha 535:
         }
         }


        // Função única para obtenção de vídeo de subskill - baseada em URL
         function getOrCreateSubskillVideo(videoURL) {
         function getOrCreateSubskillVideo(videoURL) {
             if (!videoURL || videoURL.trim() === '') return null;
             if (!videoURL || videoURL.trim() === '') return null;
Linha 291: Linha 541:
             if (!normalizedURL || normalizedURL.trim() === '') return null;
             if (!normalizedURL || normalizedURL.trim() === '') return null;


            // Verifica se já existe no cache
             if (subskillVideoElementCache.has(normalizedURL)) {
             if (subskillVideoElementCache.has(normalizedURL)) {
                 logSubVideo('getOrCreateSubskillVideo: cache hit', { videoURL: normalizedURL });
                 logSubVideo('getOrCreateSubskillVideo: cache hit', { videoURL: normalizedURL });
Linha 297: Linha 546:
             }
             }


            // Cria novo elemento de vídeo
             logSubVideo('getOrCreateSubskillVideo: creating new video', { videoURL: normalizedURL });
             logSubVideo('getOrCreateSubskillVideo: creating new video', { videoURL: normalizedURL });


Linha 306: Linha 554:
             v.setAttribute('preload', 'metadata');
             v.setAttribute('preload', 'metadata');
             v.setAttribute('playsinline', '');
             v.setAttribute('playsinline', '');
             v.muted = true; // Permite autoload em mais navegadores
             v.muted = true;
             v.style.display = 'none';
             v.style.display = 'none';
             v.style.width = '100%';
             v.style.width = '100%';
Linha 313: Linha 561:
             v.style.objectFit = 'cover';
             v.style.objectFit = 'cover';


            // Detectar formato do vídeo pela extensão
             const ext = (normalizedURL.split('.').pop() || '').toLowerCase().split('?')[0];
             const ext = (normalizedURL.split('.').pop() || '').toLowerCase().split('?')[0];
             const mimeTypes = {
             const mimeTypes = {
Linha 330: Linha 577:
             v.appendChild(src);
             v.appendChild(src);


            // Fallback para Safari/iOS mais antigos
             v.setAttribute('webkit-playsinline', '');
             v.setAttribute('webkit-playsinline', '');
             v.setAttribute('x-webkit-airplay', 'allow');
             v.setAttribute('x-webkit-airplay', 'allow');


            // Adiciona ao cache
             subskillVideoElementCache.set(normalizedURL, v);
             subskillVideoElementCache.set(normalizedURL, v);


            // Adiciona ao DOM imediatamente e força carregamento
             const videoBox = document.querySelector('.video-container');
             const videoBox = document.querySelector('.video-container');
             if (videoBox && !v.isConnected) {
             if (videoBox && !v.isConnected) {
                 videoBox.appendChild(v);
                 videoBox.appendChild(v);
                // Força carregamento imediato
                 v.load();
                 v.load();
             }
             }
Linha 447: Linha 690:
         }
         }


        // Função para mostrar vídeo de subskill usando cache baseado em URL
         function showSubVideo(videoURL, videoBox) {
         function showSubVideo(videoURL, videoBox) {
             logSubVideo('showSubVideo called', { videoURL, videoBoxExists: !!videoBox });
             logSubVideo('showSubVideo called', { videoURL, videoBoxExists: !!videoBox });
Linha 453: Linha 695:
             if (!videoBox) return;
             if (!videoBox) return;


            // Ocultar todos os vídeos existentes no container
             videoBox.querySelectorAll('video.skill-video[data-sub="1"]').forEach(v => {
             videoBox.querySelectorAll('video.skill-video[data-sub="1"]').forEach(v => {
                 v.style.display = 'none';
                 v.style.display = 'none';
             });
             });


            // Obter vídeo via getOrCreateSubskillVideo (única forma de obter vídeos)
             const video = getOrCreateSubskillVideo(videoURL);
             const video = getOrCreateSubskillVideo(videoURL);


Linha 467: Linha 707:
             }
             }


            // Se o vídeo não estiver conectado ao DOM, anexá-lo ao container
             if (!video.isConnected || video.parentNode !== videoBox) {
             if (!video.isConnected || video.parentNode !== videoBox) {
                 logSubVideo('video not in DOM, appending', {
                 logSubVideo('video not in DOM, appending', {
Linha 487: Linha 726:
             });
             });


            // Exibir o vídeo
             videoBox.style.display = 'block';
             videoBox.style.display = 'block';
             video.style.display = 'block';
             video.style.display = 'block';


            // Reinicia o vídeo para feedback visual imediato
             try {
             try {
                 video.currentTime = 0;
                 video.currentTime = 0;
             } catch (e) { }
             } catch (e) { }


            // Tenta dar play se o vídeo já está pronto
             if (video.readyState >= 2) {
             if (video.readyState >= 2) {
                 video.play().then(() => {
                 video.play().then(() => {
Linha 504: Linha 740:
                 });
                 });
             } else {
             } else {
                // Se não está pronto ainda, espera o evento canplay
                 logSubVideo('video not ready, waiting for canplay', { videoURL, readyState: video.readyState });
                 logSubVideo('video not ready, waiting for canplay', { videoURL, readyState: video.readyState });
                 const onCanPlay = () => {
                 const onCanPlay = () => {
Linha 514: Linha 749:
         }
         }


         // Funções antigas removidas - usar getOrCreateSubskillVideo() em vez disso
         function updateSkillDisplay(skillData) {
        // REMOVIDO: ensureSubVideoCached
            const L = getLabels();
        // REMOVIDO: ensureSubVideoInCache
            const weaponOn = isWeaponModeOn();
 
           
        api.refreshCurrentSubSafe = function () {
            const attrs = getEffectiveAttrs(skillData);
             const btn = document.querySelector('.subskills-rail .subicon.active');
            const desc = getEffectiveDesc(skillData);
             if (!btn) return false;
            const videoURL = getEffectiveVideo(skillData);
            const had = document.body.dataset.suppressSkillPlay;
           
            document.body.dataset.suppressSkillPlay = '1';
             const descBox = document.querySelector('.skill-description');
            try {
            const videoBox = document.querySelector('.video-container');
                 btn.dispatchEvent(new Event('click', { bubbles: true }));
           
            } finally {
             if (descBox) {
                if (had) document.body.dataset.suppressSkillPlay = had;
                const level = (skillData.level || '').toString().trim();
                 else delete document.body.dataset.suppressSkillPlay;
                const flagsHTML = skillData.flags ? renderFlagsRow(skillData.flags) : '';
               
                 descBox.innerHTML = `
                    <div class="skill-title"><h3>${skillData.name || skillData.n || ''}</h3></div>
                    ${level ? `<div class="skill-level-line"><span class="attr-label">${L.level} ${level}</span></div>` : ''}
                    ${renderSubAttrs(attrs, L)}
                    <div class="desc">${desc.replace(/'''(.*?)'''/g, '<b>$1</b>')}</div>
                 `;
             }
             }
             return true;
              
        };
            if (videoBox) {
 
                const oldFlags = videoBox.querySelector('.skill-flags');
        // Função auxiliar para aplicar classes de weapon nas subskills renderizadas
                if (oldFlags) oldFlags.remove();
        const applyWeaponClassesToSubskills = () => {
               
            const weaponOn = isWeaponModeOn();
                 if (skillData.flags && Array.isArray(skillData.flags) && skillData.flags.length > 0) {
            // Busca TODAS as subskills com weapon
                     const flagsHTML = renderFlagsRow(skillData.flags);
            let weaponSubs = document.querySelectorAll('.subicon[data-weapon]');
                     if (flagsHTML) {
 
                        videoBox.insertAdjacentHTML('beforeend', flagsHTML);
            weaponSubs.forEach(el => {
                         applyFlagTooltips(videoBox);
                 if (weaponOn) {
                     el.classList.add('has-weapon-available');
                     if (el.classList.contains('active')) {
                         el.classList.add('weapon-equipped');
                     }
                     }
                }
               
                if (!videoURL || videoURL.trim() === '') {
                    videoBox.style.display = 'none';
                 } else {
                 } else {
                     el.classList.remove('has-weapon-available');
                     showSubVideo(videoURL, videoBox);
                    el.classList.remove('weapon-equipped');
                 }
                 }
             });
             }
         };
         }


        // ===== API PRINCIPAL =====
         api.renderBarFrom = function (el, { iconsBar, descBox, videoBox }) {
         api.renderBarFrom = function (el, { iconsBar, descBox, videoBox }) {
             logSubVideo('api.renderBarFrom called', {
             debugLog('api.renderBarFrom START', {
                 hasEl: !!el,
                 el: !!el,
                hasIconsBar: !!iconsBar,
                hasDescBox: !!descBox,
                hasVideoBox: !!videoBox,
                 parentIndex: el?.dataset?.index || 'unknown'
                 parentIndex: el?.dataset?.index || 'unknown'
             });
             });
Linha 562: Linha 801:
             const rail = ensureRail(iconsBar);
             const rail = ensureRail(iconsBar);
             if (!rail) {
             if (!rail) {
                 logSubVideo('api.renderBarFrom: no rail found, returning');
                 debugLog('No rail found');
                 return;
                 return;
             }
             }
Linha 570: Linha 809:
             const parentIdx = el.dataset.index || '';
             const parentIdx = el.dataset.index || '';


             logSubVideo('api.renderBarFrom: parsing subs', {
             debugLog('Raw data:', { rawSubs: rawSubs.substring(0, 100) + '...', rawOrder, parentIdx });
                parentIdx,
                hasRawSubs: !!rawSubs.trim(),
                rawSubsLength: rawSubs.length
            });


             if (!rawSubs.trim()) {
             if (!rawSubs.trim()) {
Linha 585: Linha 820:


             let subs;
             let subs;
             try { subs = JSON.parse(rawSubs); } catch { subs = []; }
             try {  
                subs = JSON.parse(rawSubs);
                debugLog('Parsed subs (first 2):', subs.slice(0, 2));
               
                // DEBUG: Verifica estrutura
                subs.forEach((sub, i) => {
                    if (sub.subs || sub.subskills) {
                        debugLog(`Subskill ${i} ("${sub.n || sub.name}") has nested:`, sub.subs || sub.subskills);
                    }
                });
            } catch(e) {  
                subs = [];
                console.error('[Subskills] JSON parse error:', e, 'Raw:', rawSubs.substring(0, 200));
            }


             // NORMALIZADOR: Converte weaponPacked para weapon se necessário
             // Normaliza weapon
             subs = subs.map(sub => {
             subs = subs.map(sub => {
                // Se tem weaponPacked mas não tem weapon, tenta parsear
                 if (sub.weaponPacked && !sub.weapon && typeof sub.weaponPacked === 'string' && sub.weaponPacked.trim() !== '') {
                 if (sub.weaponPacked && !sub.weapon && typeof sub.weaponPacked === 'string' && sub.weaponPacked.trim() !== '') {
                     const parts = sub.weaponPacked.split('~');
                     const parts = sub.weaponPacked.split('~');
Linha 601: Linha 848:
                             energy: parts[5] || null
                             energy: parts[5] || null
                         };
                         };
                        // Remove valores vazios
                         Object.keys(sub.weapon).forEach(k => {
                         Object.keys(sub.weapon).forEach(k => {
                             if (sub.weapon[k] === '' || sub.weapon[k] === null) {
                             if (sub.weapon[k] === '' || sub.weapon[k] === null) {
Linha 609: Linha 855:
                     }
                     }
                 }
                 }
                // Garante que weapon seja objeto válido
                 if (sub.weapon && typeof sub.weapon === 'string') {
                 if (sub.weapon && typeof sub.weapon === 'string') {
                    // Tenta parsear como JSON primeiro
                     try {
                     try {
                         sub.weapon = JSON.parse(sub.weapon);
                         sub.weapon = JSON.parse(sub.weapon);
                     } catch {
                     } catch {
                        // Se falhar, tenta formato ~
                         const parts = sub.weapon.split('~');
                         const parts = sub.weapon.split('~');
                         if (parts.length >= 2) {
                         if (parts.length >= 2) {
Linha 647: Linha 890:
             }
             }


             // Busca mapa das skills principais para herança
             // Processa estrutura do módulo
             const mainSkills = getMainSkillsMap();
             const mainSkills = getMainSkillsMap();
 
           
             // Debug: log dos dados antes da herança
             // Passo 1: Aplica herança
            if (SUBVIDEO_DEBUG && subs.length > 0) {
                logSubVideo('subs before inheritance', {
                    subsCount: subs.length,
                    firstSub: JSON.stringify(subs[0]),
                    allSubs: subs.map(s => ({ refM: s.refM || s.M || s.m, video: s.video, hasVideo: s.hasOwnProperty('video') }))
                });
            }
 
            // Aplica herança ANTES de processar
             subs = subs.map(sub => applyInheritance(sub, mainSkills));
             subs = subs.map(sub => applyInheritance(sub, mainSkills));
 
           
             // Remove subskills que ficaram sem nome após herança
             // Passo 2: Processa estrutura aninhada
             subs = subs.filter(s => (s.name || s.n || '').trim() !== '');
             subs = processModuleNestedStructure(subs, 1);
           
            debugLog('Final processed subs:', subs);


             subRail.classList.add('hidden');
             subRail.classList.add('hidden');
             subBar.innerHTML = '';
             subBar.innerHTML = '';


            // Usa a ordem natural das subskills após herança
             let order = subs.map(s => s.name || s.n || '');
             let order = subs.map(s => s.name || s.n || '');
             if (rawOrder.trim()) {
             if (rawOrder.trim()) {
Linha 680: Linha 915:
             }
             }


             // Pré-carrega TODOS os ícones ANTES de renderizar
             // Pré-carrega ícones
             const iconPreloadPromises = [];
             const iconPreloadPromises = [];
             order.forEach(nm => {
             order.forEach(nm => {
Linha 689: Linha 924:
             });
             });


            // Vídeos serão criados e carregados sob demanda quando necessário
             // Função de renderização
 
             // Função para renderizar a barra de subskills
             const renderSubskillsBar = () => {
             const renderSubskillsBar = () => {
                 order.forEach(nm => {
                 order.forEach(nm => {
Linha 705: Linha 938:
                     const slugify = window.__skillSlugify || ((str) => (str || '').toLowerCase().replace(/[^\w]+/g, '-').replace(/^-+|-+$/g, ''));
                     const slugify = window.__skillSlugify || ((str) => (str || '').toLowerCase().replace(/[^\w]+/g, '-').replace(/^-+|-+$/g, ''));
                     item.dataset.slug = slugify(s.name || nm);
                     item.dataset.slug = slugify(s.name || nm);
                    item.dataset.subskillData = JSON.stringify(s);


                     logSubVideo('creating subicon element', {
                     debugLog('Creating subicon:', {  
                         subName: (s.name || s.n || '').trim(),
                         name: s.name || s.n,
                         parentIdx,
                         hasChildren: s.hasChildren,
                         className: item.className
                         childrenCount: s.childrenCount
                     });
                     });


Linha 724: Linha 958:
                     item.appendChild(img);
                     item.appendChild(img);


                     // Verifica weapon de forma mais robusta
                     // Adiciona indicador se tiver sub-subskills
                    if (s.hasChildren) {
                        const indicator = document.createElement('div');
                        indicator.className = 'subskill-indicator';
                        indicator.innerHTML = '▼';
                        indicator.title = `Tem ${s.childrenCount || '?'} sub-habilidade(s)`;
                        item.appendChild(indicator);
                        item.classList.add('has-children');
                        debugLog('Added children indicator for:', s.name || s.n);
                    }
 
                    // Weapon
                     const hasWeapon = s.weapon && (
                     const hasWeapon = s.weapon && (
                         (typeof s.weapon === 'object' && Object.keys(s.weapon).length > 0) ||
                         (typeof s.weapon === 'object' && Object.keys(s.weapon).length > 0) ||
Linha 733: Linha 978:


                     if (hasWeapon) {
                     if (hasWeapon) {
                        // Normaliza weapon se for string
                         let weaponObj = s.weapon;
                         let weaponObj = s.weapon;
                         if (typeof weaponObj === 'string') {
                         if (typeof weaponObj === 'string') {
Linha 739: Linha 983:
                                 weaponObj = JSON.parse(weaponObj);
                                 weaponObj = JSON.parse(weaponObj);
                             } catch {
                             } catch {
                                // Se falhar, tenta formato ~
                                 const parts = weaponObj.split('~');
                                 const parts = weaponObj.split('~');
                                 if (parts.length >= 2) {
                                 if (parts.length >= 2) {
Linha 765: Linha 1 008:
                                 item.dataset.weapon = JSON.stringify(weaponObj);
                                 item.dataset.weapon = JSON.stringify(weaponObj);
                             } catch (e) {
                             } catch (e) {
                                 console.error('[Subskills] Erro ao serializar weapon de subskill', subName, e);
                                 console.error('[Subskills] Erro ao serializar weapon:', e);
                             }
                             }


                            // Aplica classe inicial se o toggle já está ativo
                             if (isWeaponModeOn()) {
                             if (isWeaponModeOn()) {
                                 item.classList.add('has-weapon-available');
                                 item.classList.add('has-weapon-available');
Linha 775: Linha 1 017:
                     }
                     }


                     // CORREÇÃO 4: Evento de clique otimizado para não recarregar vídeos
                     // Evento de clique
                     logSubVideo('attaching click handler to subicon', {
                     item.addEventListener('click', (e) => {
                         subName: (s.name || s.n || '').trim(),
                         if (e.target.closest('.subskills-nested') || e.target.closest('.subskills-third-level')) {
                        parentIdx,
                            return;
                         itemClassName: item.className
                         }
                    });
 
                    item.addEventListener('click', () => {
                         const L = getLabels();
                         const L = getLabels();
                         const subName = (s.name || s.n || '').trim();
                         const subName = (s.name || s.n || '').trim();


                         logSubVideo('subicon click HANDLER EXECUTED', {
                         debugLog('Subicon clicked:', {  
                             subName,
                             subName,
                             parentIdx,
                             hasChildren: s.hasChildren,
                            weaponMode: isWeaponModeOn(),
                             hasNestedSubs: !!(s.subs && Array.isArray(s.subs) && s.subs.length > 0)
                             hasWeaponDataAttr: !!item.dataset.weapon,
                            rawWeaponDataset: item.dataset.weapon || null
                         });
                         });


                         // Lê weapon diretamente do atributo data-weapon
                         // Weapon logic
                         let subWeaponData = null;
                         let subWeaponData = null;
                         if (item.dataset.weapon) {
                         if (item.dataset.weapon) {
Linha 803: Linha 1 042:
                         }
                         }
                         const hasSubWeapon = !!subWeaponData;
                         const hasSubWeapon = !!subWeaponData;
                         const weaponOn = isWeaponModeOn();
                         const weaponOn = isWeaponModeOn();
                         const weaponEquipped = weaponOn && hasSubWeapon && subWeaponData;
                         const weaponEquipped = weaponOn && hasSubWeapon && subWeaponData;


                        logSubVideo('weapon status for subclick', {
                         // Descrição
                            subName,
                            weaponEquipped,
                            hasSubWeaponData: !!subWeaponData
                        });
 
                         // Determina descrição (weapon ou normal)
                         const raw = (document.documentElement.lang || 'pt').toLowerCase();
                         const raw = (document.documentElement.lang || 'pt').toLowerCase();
                         const lang = raw === 'pt-br' ? 'pt' : (raw.split('-')[0] || 'pt');
                         const lang = raw === 'pt-br' ? 'pt' : (raw.split('-')[0] || 'pt');
Linha 819: Linha 1 051:
                         let chosen = '';
                         let chosen = '';
                         if (weaponEquipped && subWeaponData) {
                         if (weaponEquipped && subWeaponData) {
                            // Descrição do weapon
                             const weaponDescPack = subWeaponData.desc_i18n || subWeaponData.desc || {};
                             const weaponDescPack = subWeaponData.desc_i18n || subWeaponData.desc || {};
                             chosen = weaponDescPack[lang] || weaponDescPack.pt || weaponDescPack.en || '';
                             chosen = weaponDescPack[lang] || weaponDescPack.pt || weaponDescPack.en || '';
                         } else {
                         } else {
                            // Descrição normal
                             const base = s.desc_i18n || s.desc || {};
                             const base = s.desc_i18n || s.desc || {};
                             chosen = base[lang] || base.pt || base.en || (s.descPt || '');
                             chosen = base[lang] || base.pt || base.en || (s.descPt || '');
                         }
                         }


                         // Determina atributos (weapon ou normal)
                         // Atributos
                         let attrsObj = {
                         let attrsObj = {
                             powerpve: s.powerpve,
                             powerpve: s.powerpve,
Linha 844: Linha 1 074:
                         }
                         }


                         // Level (weapon ou normal)
                         // Level
                         const level = (weaponEquipped && subWeaponData && subWeaponData.level)
                         const level = (weaponEquipped && subWeaponData && subWeaponData.level)
                             ? subWeaponData.level.toString().trim()
                             ? subWeaponData.level.toString().trim()
Linha 854: Linha 1 084:
                         }
                         }


                        // Atualiza descrição
                         if (descBox) {
                         if (descBox) {
                             descBox.innerHTML = `<div class="skill-title"><h3>${s.name || nm}</h3></div>${level ? `<div class="skill-level-line"><span class="attr-label">${L.level} ${level}</span></div>` : ''}${renderSubAttrs(attrsObj, L)}<div class="desc">${chosen.replace(/'''(.*?)'''/g, '<b>$1</b>')}</div>`;
                             descBox.innerHTML = `<div class="skill-title"><h3>${s.name || nm}</h3></div>${level ? `<div class="skill-level-line"><span class="attr-label">${L.level} ${level}</span></div>` : ''}${renderSubAttrs(attrsObj, L)}<div class="desc">${chosen.replace(/'''(.*?)'''/g, '<b>$1</b>')}</div>`;
Linha 867: Linha 1 098:
                         }
                         }


                         // Vídeo (weapon ou normal) - usa cache já criado
                         // Vídeo
                         const effectiveVideo = getEffectiveVideo(s);
                         const effectiveVideo = getEffectiveVideo(s);
                        logSubVideo('effectiveVideo for sub', {
                            subName,
                            parentIdx,
                            effectiveVideo,
                            weaponEquipped,
                            rawBaseVideo: s.video,
                            rawWeaponVideo: subWeaponData?.video
                        });


                         if (!effectiveVideo || effectiveVideo.trim() === '' || effectiveVideo === 'Nada.png' || effectiveVideo.toLowerCase().includes('nada.png')) {
                         if (!effectiveVideo || effectiveVideo.trim() === '' || effectiveVideo === 'Nada.png' || effectiveVideo.toLowerCase().includes('nada.png')) {
                             if (videoBox) videoBox.style.display = 'none';
                             if (videoBox) videoBox.style.display = 'none';
                         } else {
                         } else {
                            logSubVideo('calling showSubVideo from click handler', { effectiveVideo });
                            // Usa getOrCreateSubskillVideo internamente - nunca recria se já existe
                             showSubVideo(effectiveVideo, videoBox);
                             showSubVideo(effectiveVideo, videoBox);
                         }
                         }


                        // Marca como ativa e limpa outras
                         Array.from(subBar.children).forEach(c => {
                         Array.from(subBar.children).forEach(c => {
                             c.classList.remove('active');
                             c.classList.remove('active');
                             c.classList.remove('weapon-equipped');
                             c.classList.remove('weapon-equipped');
                            const nested = c.querySelector('.subskills-nested');
                            if (nested) nested.remove();
                            const thirdLevel = c.querySelector('.subskills-third-level');
                            if (thirdLevel) thirdLevel.remove();
                         });
                         });
                         item.classList.add('active');
                         item.classList.add('active');


                        // Aplica weapon-equipped se tem weapon e está ativo
                         if (weaponEquipped) {
                         if (weaponEquipped) {
                             item.classList.add('weapon-equipped');
                             item.classList.add('weapon-equipped');
                         }
                         }
                       
                         window.__lastActiveSkillIcon = item;
                         window.__lastActiveSkillIcon = item;
                        // LÓGICA PARA SUB-SUBSKILLS
                        if (s.hasChildren && s.subs && Array.isArray(s.subs) && s.subs.length > 0) {
                            debugLog('Rendering nested subs for:', subName);
                            renderNestedSubskills(s.subs, item, subName, parentIdx);
                        } else {
                            const nested = item.querySelector('.subskills-nested');
                            if (nested) nested.remove();
                            const thirdLevel = item.querySelector('.subskills-third-level');
                            if (thirdLevel) thirdLevel.remove();
                        }
                     });
                     });


Linha 913: Linha 1 148:


                     subBar.appendChild(item);
                     subBar.appendChild(item);
                     logSubVideo('subicon appended to subBar', {
                });
                         subName: (s.name || s.n || '').trim(),
 
                         parentIdx,
                // Weapon classes
                        subBarChildrenCount: subBar.children.length
                const applyWeaponClassesToSubskills = () => {
                     const weaponOn = isWeaponModeOn();
                    let weaponSubs = document.querySelectorAll('.subicon[data-weapon]');
 
                    weaponSubs.forEach(el => {
                         if (weaponOn) {
                            el.classList.add('has-weapon-available');
                            if (el.classList.contains('active')) {
                                el.classList.add('weapon-equipped');
                            }
                         } else {
                            el.classList.remove('has-weapon-available');
                            el.classList.remove('weapon-equipped');
                        }
                     });
                     });
                 });
                 };


                // Aplica classes de weapon nas subskills recém-renderizadas
                 applyWeaponClassesToSubskills();
                 applyWeaponClassesToSubskills();


                // Dispara evento para notificar que subskills estão prontas
                 window.dispatchEvent(new CustomEvent('gla:subskills:ready', { detail: { count: order.length } }));
                 window.dispatchEvent(new CustomEvent('gla:subskills:ready', { detail: { count: order.length } }));


                // Remove listener anterior se existir (evita duplicação)
                 if (subBar._weaponToggleListener) {
                 if (subBar._weaponToggleListener) {
                     window.removeEventListener('gla:weaponToggled', subBar._weaponToggleListener);
                     window.removeEventListener('gla:weaponToggled', subBar._weaponToggleListener);
                 }
                 }


                // Cria novo listener que opera no subBar atual
                 subBar._weaponToggleListener = (e) => {
                 subBar._weaponToggleListener = (e) => {
                     const enabled = e.detail?.enabled ?? false;
                     const enabled = e.detail?.enabled ?? false;
                    // Aplica classes usando a função auxiliar
                     applyWeaponClassesToSubskills();
                     applyWeaponClassesToSubskills();


                    // Atualiza a subskill ativa se houver
                     setTimeout(() => {
                     setTimeout(() => {
                         const activeSub = subBar.querySelector('.subicon[data-weapon].active')
                         const activeSub = subBar.querySelector('.subicon[data-weapon].active')
Linha 960: Linha 1 201:
             };
             };


             // Chama a função de renderização após pré-carregar ícones
             // Renderiza
             Promise.all(iconPreloadPromises).then(() => {
             Promise.all(iconPreloadPromises).then(() => {
                 setTimeout(() => {
                 setTimeout(() => {
Linha 970: Linha 1 211:
                 }, 10);
                 }, 10);
             });
             });
        };
        api.refreshCurrentSubSafe = function () {
            const btn = document.querySelector('.subskills-rail .subicon.active');
            if (!btn) return false;
            const had = document.body.dataset.suppressSkillPlay;
            document.body.dataset.suppressSkillPlay = '1';
            try {
                btn.dispatchEvent(new Event('click', { bubbles: true }));
            } finally {
                if (had) document.body.dataset.suppressSkillPlay = had;
                else delete document.body.dataset.suppressSkillPlay;
            }
            return true;
         };
         };


Linha 983: Linha 1 238:
         window.renderSubskillsBarFrom = function (el, ctx) { api.renderBarFrom(el, ctx); };
         window.renderSubskillsBarFrom = function (el, ctx) { api.renderBarFrom(el, ctx); };


         api.preloadAllSubskillImages = function () {
         // ===== INICIALIZAÇÃO =====
            const allSkillIcons = document.querySelectorAll('.icon-bar .skill-icon[data-subs]');
            const preloadPromises = [];
            let totalImages = 0;
 
            allSkillIcons.forEach(icon => {
                try {
                    const subsRaw = icon.getAttribute('data-subs');
                    if (!subsRaw) return;
                    const subs = JSON.parse(subsRaw);
                    if (!Array.isArray(subs)) return;
 
                    subs.forEach(s => {
                        if (s && s.icon && s.icon.trim() !== '' && s.icon !== 'Nada.png') {
                            preloadPromises.push(preloadImage(s.icon));
                            totalImages++;
                        }
                        if (s && Array.isArray(s.subs)) {
                            s.subs.forEach(nested => {
                                if (nested && nested.icon && nested.icon.trim() !== '' && nested.icon !== 'Nada.png') {
                                    preloadPromises.push(preloadImage(nested.icon));
                                    totalImages++;
                                }
                            });
                        }
                    });
                } catch (e) {
                    console.error('[Subskills] preloadAllSubskillImages error:', e);
                }
            });
 
            if (totalImages > 0) {
                // console.log('[Subskills] preloadAllSubskillImages: pré-carregando', totalImages, 'ícones');
                return Promise.all(preloadPromises).then(() => {
                    // console.log('[Subskills] preloadAllSubskillImages: todos os ícones carregados');
                });
            }
            return Promise.resolve();
        };
 
        // Função para pré-carregar vídeos de subskills usando getOrCreateSubskillVideo
        // Função preloadAllSubskillVideos removida - vídeos são carregados sob demanda
 
        // Função syncVideoCaches removida - não é mais necessária com cache baseado em URL
 
        // Inicialização
         function init() {
         function init() {
             logSubVideo('init: starting initialization');
             debugLog('Initializing subskills system');
 
            // Constrói cache das skills principais
             getMainSkillsMap();
             getMainSkillsMap();
            // Pré-carrega imagens das subskills IMEDIATAMENTE
            api.preloadAllSubskillImages();
            //        if (video.readyState < 2) {
            //            video.muted = true;
            //            video.load();
            //        }
            //    });
            // }, 500);


             // Escuta mudanças no localStorage
             // Escuta mudanças no localStorage
Linha 1 052: Linha 1 250:
             });
             });


            // LISTENER GLOBAL: Escuta evento de toggle
             window.addEventListener('gla:weaponToggled', (e) => {
             window.addEventListener('gla:weaponToggled', (e) => {
                 const enabled = e.detail?.enabled ?? false;
                 const enabled = e.detail?.enabled ?? false;
                // Tenta aplicar classes imediatamente
                applyWeaponClassesToSubskills();
                // Atualiza a subskill ativa se houver
                 setTimeout(() => {
                 setTimeout(() => {
                     const activeSub = document.querySelector('.subicon[data-weapon].active')
                     const activeSub = document.querySelector('.subicon[data-weapon].active')
Linha 1 071: Linha 1 263:
             });
             });


            // LISTENER: Escuta quando subskills estão prontas
             window.addEventListener('gla:subskills:ready', (e) => {
             window.addEventListener('gla:subskills:ready', (e) => {
                 // Aplica classes de weapon se o toggle estiver ativo
                 debugLog('Subskills ready:', e.detail);
                applyWeaponClassesToSubskills();
             });
             });
         }
         }
Linha 1 088: Linha 1 278:
</script>
</script>
<style>
<style>
    /* ESTILOS EXISTENTES */
     .subicon-bar {
     .subicon-bar {
         display: flex;
         display: flex;
Linha 1 093: Linha 1 284:
         padding: 6px 6px;
         padding: 6px 6px;
         overflow-x: auto;
         overflow-x: auto;
        /* Firefox */
         scrollbar-width: thin;
         scrollbar-width: thin;
         scrollbar-color: #ababab transparent;
         scrollbar-color: #ababab transparent;
Linha 1 116: Linha 1 306:
         cursor: pointer;
         cursor: pointer;
         isolation: isolate;
         isolation: isolate;
        transition: transform 0.2s ease;
     }
     }


Linha 1 161: Linha 1 352:
         box-shadow: 0 0 12px 3px var(--icon-active-glow, rgba(255, 217, 90, .30)),
         box-shadow: 0 0 12px 3px var(--icon-active-glow, rgba(255, 217, 90, .30)),
             0 0 0 calc(var(--icon-ring-w, 2px) * 2) var(--icon-active-ring, rgba(255, 217, 90, .50));
             0 0 0 calc(var(--icon-ring-w, 2px) * 2) var(--icon-active-ring, rgba(255, 217, 90, .50));
    }
    .subicon.active img {
        transform: none !important;
     }
     }


Linha 1 173: Linha 1 360:
         align-items: center;
         align-items: center;
         overflow: visible;
         overflow: visible;
    }
    .top-rail.skills .icon-bar {
        margin-bottom: 0;
        position: relative;
        z-index: 2;
     }
     }


Linha 1 203: Linha 1 384:
         transition: opacity .14s ease, transform .14s ease;
         transition: opacity .14s ease, transform .14s ease;
         opacity: 1;
         opacity: 1;
    }
    .subskills-rail::before {
        content: "";
        position: absolute;
        top: -6px;
        left: 0;
        right: 0;
        height: 6px;
        background: linear-gradient(to bottom, rgba(0, 0, 0, .20), rgba(0, 0, 0, 0));
        pointer-events: none;
     }
     }


Linha 1 231: Linha 1 401:
     }
     }


     .subskills-rail .subicon-bar {
     /* NOVOS ESTILOS PARA SUB-SUBSKILLS */
        display: inline-flex;
    .subicon.has-children {
        align-items: center;
         position: relative;
        gap: 0;
        overflow-x: auto;
        /* Firefox */
        scrollbar-width: thin;
         scrollbar-color: #ababab transparent;
     }
     }


     .subskills-rail .subicon-bar::-webkit-scrollbar {
     .subskill-indicator {
         height: 6px;
        position: absolute;
    }
        bottom: -3px;
 
        right: -3px;
    .subskills-rail .subicon-bar::-webkit-scrollbar-thumb {
        background: var(--icon-active, #FFD95A);
         background: #151515;
        color: #000;
         border-radius: 3px;
         font-size: 9px;
        width: 14px;
        height: 14px;
        border-radius: 50%;
        display: flex;
        align-items: center;
        justify-content: center;
        font-weight: bold;
         border: 1px solid #000;
         z-index: 10;
     }
     }


     .subskills-rail .subicon {
     .nested-indicator {
         width: 42px;
         background: #FF7043 !important;
         height: 42px;
         font-size: 8px;
        border-radius: 6px;
         width: 12px;
         position: relative;
         height: 12px;
         overflow: hidden;
        flex: 0 0 auto;
        cursor: pointer;
        isolation: isolate;
        -webkit-backface-visibility: hidden;
        backface-visibility: hidden;
        transform: translateZ(0);
     }
     }


     .subskills-rail .subicon+.subicon {
     .subskills-nested {
         margin-left: 4px;
        animation: fadeIn 0.2s ease;
         z-index: 1000;
     }
     }


     .subskills-rail .subicon img {
     .subskills-third-level {
         width: 100%;
         animation: fadeIn 0.2s ease;
        height: 100%;
         z-index: 1001;
        aspect-ratio: 1 / 1;
        object-fit: cover;
        display: block;
         border-radius: inherit;
     }
     }


     .subskills-rail .subicon::after {
     .nested-subicon {
         content: "";
         width: 36px !important;
        position: absolute;
         height: 36px !important;
         inset: 0;
         border-radius: 6px !important;
         border-radius: inherit;
         position: relative;
         box-shadow: inset 0 0 0 2px var(--icon-idle, #cfcfcf);
        pointer-events: none;
        z-index: 2;
        transition: box-shadow .12s ease;
     }
     }


     .subskills-rail .subicon:hover::after {
     .nested-subicon.active::after {
         box-shadow: inset 0 0 0 2px #e6e6e6;
         box-shadow: inset 0 0 0 2px var(--icon-active, #FFD95A) !important;
     }
     }


     .subskills-rail .subicon.active::after {
     .third-level-subicon:hover {
         box-shadow: inset 0 0 0 2px var(--icon-active, #FFD95A);
         background: rgba(255, 217, 90, 0.15) !important;
     }
     }


     .video-container .skill-video {
     @keyframes fadeIn {
         width: 100%;
         from { opacity: 0; transform: translateY(-5px); }
        height: auto;
         to { opacity: 1; transform: translateY(0); }
        aspect-ratio: 16 / 9;
        object-fit: cover;
         background: #000;
        border-radius: 10px;
     }
     }


    /* Responsivo */
     @media (max-width: 900px) {
     @media (max-width: 900px) {
         .subskills-rail {
         .subskills-rail {
Linha 1 316: Linha 1 473:
         .subskills-spacer {
         .subskills-spacer {
             height: 0 !important;
             height: 0 !important;
        }
       
        .subskills-nested,
        .subskills-third-level {
            position: fixed !important;
            top: 50% !important;
            left: 50% !important;
            transform: translate(-50%, -50%) !important;
            max-width: 90vw !important;
            z-index: 9999 !important;
         }
         }
     }
     }


    .skills-rail-wrap {
     /* Weapon styles (mantidos do original) */
        position: relative;
        display: block;
        width: max-content;
        margin: 0 auto;
    }
 
     /* Subskills com arma disponível - borda vermelha quando inativa */
     .character-box .top-rail.skills .subicon.has-weapon-available:not(.active)::after {
     .character-box .top-rail.skills .subicon.has-weapon-available:not(.active)::after {
         box-shadow: inset 0 0 0 2px rgba(220, 70, 70, 0.85) !important;
         box-shadow: inset 0 0 0 2px rgba(220, 70, 70, 0.85) !important;
    }
    /* Subskill com arma ATIVA - laranja/coral vibrante + brilho forte */
    .character-box .top-rail.skills .subicon.has-weapon-available.active {
        position: relative;
    }
    .character-box .top-rail.skills .subicon.has-weapon-available.active::after {
        box-shadow: none !important;
        background: linear-gradient(135deg,
                #FF5722 0%,
                #FF7043 20%,
                #FF8A65 40%,
                #FF7043 60%,
                #FF5722 80%,
                #FF3D00 100%) !important;
        background-size: 300% 300% !important;
        animation: weapon-icon-border-scan 3s 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: 2px !important;
        filter: drop-shadow(0 0 4px rgba(255, 87, 34, 0.8)) drop-shadow(0 0 8px rgba(255, 87, 34, 0.6)) !important;
    }
    .character-box .top-rail.skills .subicon.has-weapon-available.active::before {
        box-shadow:
            0 0 20px 8px rgba(255, 87, 34, 0.95),
            0 0 40px 16px rgba(255, 87, 34, 0.75),
            0 0 60px 24px rgba(255, 120, 50, 0.5),
            0 0 0 4px rgba(255, 87, 34, 0.6) !important;
        opacity: 1 !important;
    }
    /* Modo arma ON - efeito animado nos subicons */
    .character-box .top-rail.skills.weapon-mode-on .subicon.has-weapon-available {
        position: relative;
    }
    .character-box .top-rail.skills.weapon-mode-on .subicon.has-weapon-available::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-subicon-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: 2px !important;
    }
    .character-box .top-rail.skills.weapon-mode-on .subicon.has-weapon-available::before {
        content: "";
        position: absolute;
        inset: -4px;
        border-radius: calc(6px + 4px);
        pointer-events: none;
        z-index: 1;
        box-shadow:
            0 0 16px 6px rgba(255, 80, 80, 0.85),
            0 0 32px 12px rgba(255, 80, 80, 0.6),
            0 0 48px 18px rgba(255, 100, 60, 0.4) !important;
        opacity: 1 !important;
        animation: weapon-subicon-pulse 3s ease-in-out infinite !important;
    }
    /* Subskill ativa com arma - mais intenso */
    .character-box .top-rail.skills.weapon-mode-on .subicon.has-weapon-available.active::after {
        background: linear-gradient(135deg,
                #FF3D00 0%,
                #FF5722 15%,
                #FF7043 30%,
                #FF8A65 45%,
                #FF7043 60%,
                #FF5722 75%,
                #FF3D00 90%,
                #FF5722 100%) !important;
        background-size: 400% 400% !important;
        animation: weapon-icon-border-scan 2.5s linear infinite !important;
        filter: drop-shadow(0 0 6px rgba(255, 87, 34, 1)) drop-shadow(0 0 12px rgba(255, 87, 34, 0.8)) drop-shadow(0 0 18px rgba(255, 120, 50, 0.6)) !important;
    }
    .character-box .top-rail.skills.weapon-mode-on .subicon.has-weapon-available.active::before {
        box-shadow:
            0 0 28px 12px rgba(255, 87, 34, 1),
            0 0 56px 24px rgba(255, 87, 34, 0.85),
            0 0 80px 32px rgba(255, 140, 60, 0.6),
            0 0 0 5px rgba(255, 87, 34, 0.75) !important;
        animation: weapon-subicon-pulse-active 2.5s ease-in-out infinite !important;
     }
     }


     @keyframes weapon-icon-border-scan {
     @keyframes weapon-icon-border-scan {
         0% {
         0% { background-position: 0% 0%; }
            background-position: 0% 0%;
         100% { background-position: 400% 0%; }
        }
 
         100% {
            background-position: 400% 0%;
        }
     }
     }


     @keyframes weapon-subicon-border-scan {
     @keyframes weapon-subicon-border-scan {
         0% {
         0% { background-position: 0% 0%; }
            background-position: 0% 0%;
         100% { background-position: 400% 0%; }
        }
 
         100% {
            background-position: 400% 0%;
        }
    }
 
    @keyframes weapon-subicon-pulse {
 
        0%,
        100% {
            opacity: 0.95;
            box-shadow: 0 0 16px 6px rgba(255, 80, 80, 0.85), 0 0 32px 12px rgba(255, 80, 80, 0.6), 0 0 48px 18px rgba(255, 100, 60, 0.4);
        }
 
        50% {
            opacity: 1;
            box-shadow: 0 0 24px 8px rgba(255, 80, 80, 1), 0 0 48px 16px rgba(255, 80, 80, 0.75), 0 0 64px 24px rgba(255, 120, 60, 0.5);
        }
    }
 
    @keyframes weapon-subicon-pulse-active {
 
        0%,
        100% {
            opacity: 0.95;
            box-shadow: 0 0 20px 8px rgba(255, 87, 34, 0.9), 0 0 40px 16px rgba(255, 87, 34, 0.7), 0 0 60px 24px rgba(255, 120, 50, 0.5), 0 0 0 4px rgba(255, 87, 34, 0.6);
        }
 
        50% {
            opacity: 1;
            box-shadow: 0 0 28px 12px rgba(255, 87, 34, 1), 0 0 56px 24px rgba(255, 87, 34, 0.85), 0 0 80px 32px rgba(255, 140, 60, 0.6), 0 0 0 5px rgba(255, 87, 34, 0.75);
        }
     }
     }
</style>
</style>

Edição das 17h55min de 29 de dezembro de 2025

<script>

   (function () {
       const SUBVIDEO_DEBUG = true; // Ativado para debug
       function logSubVideo(...args) {
           if (!SUBVIDEO_DEBUG) return;
           console.log('[SubVideo]', ...args);
       }
       const api = (window.__subskills ||= {});
       const subskillVideoElementCache = new Map();
       const imagePreloadCache = new Map();
       let subRail, subBar, spacer;
       let cachedMainSkills = null;
       // ===== DEBUG ESTRUTURA =====
       const DEBUG = true;
       function debugLog(...args) {
           if (DEBUG) console.log('[Subskills]', ...args);
       }
       // ===== FUNÇÕES AUXILIARES =====
       function getMainSkillsMap() {
           if (cachedMainSkills && cachedMainSkills.byIndex.size > 0) {
               return cachedMainSkills;
           }
           const maps = {
               byName: new Map(),
               byIndex: new Map()
           };
           const icons = document.querySelectorAll('.icon-bar .skill-icon[data-index][data-nome]');
           icons.forEach(icon => {
               const name = (icon.dataset.nome || ).trim();
               if (!name) return;
               const atrRaw = icon.dataset.atr || ;
               const parts = atrRaw.split(',').map(x => (x || ).trim());
               const powerpve = parts[0] && parts[0] !== '-' ? parts[0] : ;
               const powerpvp = parts[1] && parts[1] !== '-' ? parts[1] : ;
               const energy = parts[2] && parts[2] !== '-' ? parts[2] : ;
               const cooldown = parts[3] && parts[3] !== '-' ? parts[3] : ;
               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) {
                   const videoUrl = icon.dataset.video || ;
                   const videoMatch = videoUrl.match(/FilePath\/([^&?]+)/);
                   videoFile = videoMatch ? decodeURIComponent(videoMatch[1]) : ;
               }
               const index = (icon.dataset.index || ).trim();
               const data = {
                   name: name,
                   icon: iconFile,
                   level: icon.dataset.level || ,
                   video: videoFile,
                   powerpve: powerpve,
                   powerpvp: powerpvp,
                   cooldown: cooldown,
                   energy: energy
               };
               if (icon.dataset.descPt) data.descPt = icon.dataset.descPt;
               if (icon.dataset.descEn) data.descEn = icon.dataset.descEn;
               if (icon.dataset.descEs) data.descEs = icon.dataset.descEs;
               if (icon.dataset.descPl) data.descPl = icon.dataset.descPl;
               maps.byName.set(name, data);
               if (index) {
                   maps.byIndex.set(index, data);
                   maps.byIndex.set(parseInt(index, 10), data);
               }
           });
           cachedMainSkills = maps;
           debugLog('Main skills map built:', maps);
           return maps;
       }
       // Processa estrutura aninhada do módulo
       function processModuleNestedStructure(subs, level = 1) {
           if (!Array.isArray(subs) || level > 3) {
               debugLog('processModuleNestedStructure: invalid input or max level', { subs, level });
               return subs || [];
           }
           const processed = subs.map(sub => {
               if (!sub || typeof sub !== 'object') return sub;
               
               const result = { ...sub };
               
               // O módulo usa 'subs' para sub-subskills
               if (result.subs && Array.isArray(result.subs)) {
                   debugLog(`Processing nested subs at level ${level} for:`, result.n || result.name);
                   result.subs = processModuleNestedStructure(result.subs, level + 1);
                   result.hasChildren = true;
                   result.childrenCount = result.subs.length;
               } else {
                   result.hasChildren = false;
               }
               
               // Remove campos desnecessários
               delete result.subskills; // Se existir do módulo antigo
               
               return result;
           });
           
           debugLog(`Processed level ${level}:`, processed);
           return processed;
       }
       // Versão melhorada da herança
       function applyInheritance(sub, mainSkills) {
           let name = (sub.name || sub.n || ).trim();
           const refIndex = ((sub.refM || sub.m || sub.M || ) + ).trim();
           let main = null;
           if (refIndex && mainSkills.byIndex.has(refIndex)) {
               main = mainSkills.byIndex.get(refIndex);
           }
           if (!main && refIndex) {
               const numIndex = parseInt(refIndex, 10);
               if (!isNaN(numIndex) && mainSkills.byIndex.has(numIndex)) {
                   main = mainSkills.byIndex.get(numIndex);
               }
           }
           if (!main && name && mainSkills.byName.has(name)) {
               main = mainSkills.byName.get(name);
           }
           if (!main) {
               debugLog('No inheritance for:', { name, refIndex });
               return sub;
           }
           if (!name && refIndex && main.name) {
               name = main.name;
           }
           const hasValue = (val) => {
               if (val === undefined || val === null) return false;
               if (typeof val === 'number') return !isNaN(val);
               const str = String(val).trim();
               return str !==  && str !== 'NaN';
           };
           // Vídeo: se existe no sub (mesmo que vazio), usa; senão, não herda
           const hasOwnVideo = Object.prototype.hasOwnProperty.call(sub, 'video');
           const finalVideo = hasOwnVideo ? (sub.video || ) : ;
           const inherited = {
               ...sub,
               name: name || main.name || sub.name,
               icon: (sub.icon && sub.icon !== 'Nada.png' && sub.icon !== ) ? sub.icon : (main.icon || 'Nada.png'),
               level: hasValue(sub.level) ? sub.level : main.level,
               video: finalVideo,
               powerpve: (sub.powerpve !== undefined && sub.powerpve !== null) ? sub.powerpve : main.powerpve,
               powerpvp: (sub.powerpvp !== undefined && sub.powerpvp !== null) ? sub.powerpvp : main.powerpvp,
               cooldown: (sub.cooldown !== undefined && sub.cooldown !== null) ? sub.cooldown : main.cooldown,
               energy: (sub.energy !== undefined && sub.energy !== null) ? sub.energy : main.energy,
               descPt: sub.descPt || (sub.desc_i18n?.pt) || main.descPt,
               descEn: sub.descEn || (sub.desc_i18n?.en) || main.descEn,
               descEs: sub.descEs || (sub.desc_i18n?.es) || main.descEs,
               descPl: sub.descPl || (sub.desc_i18n?.pl) || main.descPl
           };
           // Processa sub-subskills recursivamente
           if (sub.subs && Array.isArray(sub.subs)) {
               inherited.subs = sub.subs.map(nested => applyInheritance(nested, mainSkills));
               inherited.hasChildren = true;
               inherited.childrenCount = inherited.subs.length;
           }
           debugLog('Inheritance applied:', { original: sub.name || sub.n, inherited: inherited.name });
           return inherited;
       }
       // ===== RENDERIZAÇÃO DE NÍVEIS ANINHADOS =====
       function renderNestedSubskills(nestedSubs, parentElement, parentName, parentIdx) {
           debugLog('renderNestedSubskills called:', { 
               parentName, 
               nestedCount: nestedSubs?.length,
               parentElement 
           });
           // Remove existente
           const existing = parentElement.querySelector('.subskills-nested');
           if (existing) existing.remove();
           if (!nestedSubs || !Array.isArray(nestedSubs) || nestedSubs.length === 0) {
               debugLog('No nested subs to render');
               return;
           }
           // Cria container
           const nestedContainer = document.createElement('div');
           nestedContainer.className = 'subskills-nested';
           nestedContainer.dataset.parent = parentName;
           nestedContainer.dataset.level = '2';
           nestedContainer.dataset.parentIdx = parentIdx;
           // Estilos
           Object.assign(nestedContainer.style, {
               position: 'absolute',
               top: '100%',
               left: '50%',
               transform: 'translateX(-50%)',
               zIndex: '1000',
               background: 'rgba(0, 0, 0, 0.95)',
               borderRadius: '8px',
               padding: '8px',
               display: 'flex',
               gap: '6px',
               flexWrap: 'wrap',
               minWidth: '200px',
               boxShadow: '0 4px 20px rgba(0,0,0,0.7)',
               border: '1px solid rgba(255, 217, 90, 0.4)',
               marginTop: '5px'
           });
           // Herança das skills principais
           const mainSkills = getMainSkillsMap();
           // Processa cada sub-subskill
           nestedSubs.forEach((sub, index) => {
               const inheritedSub = applyInheritance(sub, mainSkills);
               const subName = inheritedSub.name || inheritedSub.n || ;
               const hasNested = inheritedSub.subs && Array.isArray(inheritedSub.subs) && inheritedSub.subs.length > 0;
               // Cria elemento
               const nestedItem = document.createElement('div');
               nestedItem.className = 'subicon nested-subicon';
               nestedItem.title = subName;
               nestedItem.dataset.parent = parentName;
               nestedItem.dataset.name = subName;
               nestedItem.dataset.index = index;
               nestedItem.dataset.subskillData = JSON.stringify(inheritedSub);
               // Ícone
               const img = document.createElement('img');
               img.src = filePathURL(inheritedSub.icon || 'Nada.png');
               img.alt = ;
               img.width = 36;
               img.height = 36;
               img.decoding = 'async';
               img.loading = 'lazy';
               nestedItem.appendChild(img);
               // Indicador se tiver mais níveis
               if (hasNested) {
                   const indicator = document.createElement('div');
                   indicator.className = 'subskill-indicator nested-indicator';
                   indicator.innerHTML = '▶';
                   nestedItem.appendChild(indicator);
                   nestedItem.classList.add('has-nested-children');
               }
               // Evento de clique
               nestedItem.addEventListener('click', function(e) {
                   e.stopPropagation();
                   e.preventDefault();
                   const clickedData = JSON.parse(this.dataset.subskillData || '{}');
                   const L = getLabels();
                   const subName = clickedData.name || clickedData.n || ;
                   debugLog('Nested sub clicked:', { subName, hasNested });
                   // Atualiza descrição/vídeo
                   updateSkillDisplay(clickedData);
                   // Marca como ativa
                   nestedContainer.querySelectorAll('.nested-subicon').forEach(el => {
                       el.classList.remove('active');
                   });
                   this.classList.add('active');
                   // Se tiver sub-sub-subskills, renderiza terceiro nível
                   if (hasNested && clickedData.subs) {
                       renderThirdLevelSubskills(clickedData.subs, this, subName);
                   } else {
                       const thirdLevel = this.querySelector('.subskills-third-level');
                       if (thirdLevel) thirdLevel.remove();
                   }
               });
               nestedContainer.appendChild(nestedItem);
           });
           // Adiciona ao DOM
           parentElement.style.position = 'relative';
           parentElement.appendChild(nestedContainer);
           // Fecha ao clicar fora
           setTimeout(() => {
               const closeHandler = (e) => {
                   if (!parentElement.contains(e.target) && !nestedContainer.contains(e.target)) {
                       nestedContainer.remove();
                       document.removeEventListener('click', closeHandler);
                   }
               };
               document.addEventListener('click', closeHandler);
           }, 10);
           debugLog('Nested subskills rendered:', nestedContainer.children.length);
       }
       // Terceiro nível (sub-sub-subskills)
       function renderThirdLevelSubskills(thirdLevelSubs, parentElement, parentName) {
           const existing = parentElement.querySelector('.subskills-third-level');
           if (existing) existing.remove();
           if (!thirdLevelSubs || !Array.isArray(thirdLevelSubs) || thirdLevelSubs.length === 0) {
               return;
           }
           const thirdContainer = document.createElement('div');
           thirdContainer.className = 'subskills-third-level';
           thirdContainer.dataset.parent = parentName;
           thirdContainer.dataset.level = '3';
           Object.assign(thirdContainer.style, {
               position: 'absolute',
               top: '0',
               left: '100%',
               marginLeft: '5px',
               zIndex: '1001',
               background: 'rgba(0, 0, 0, 0.98)',
               borderRadius: '8px',
               padding: '6px',
               display: 'flex',
               flexDirection: 'column',
               gap: '4px',
               minWidth: '180px',
               boxShadow: '0 4px 20px rgba(0,0,0,0.8)',
               border: '1px solid rgba(255, 217, 90, 0.3)'
           });
           const mainSkills = getMainSkillsMap();
           thirdLevelSubs.forEach((sub, index) => {
               const inheritedSub = applyInheritance(sub, mainSkills);
               const subName = inheritedSub.name || inheritedSub.n || ;
               const thirdItem = document.createElement('div');
               thirdItem.className = 'subicon third-level-subicon';
               thirdItem.title = subName;
               thirdItem.dataset.parent = parentName;
               thirdItem.dataset.name = subName;
               thirdItem.dataset.subskillData = JSON.stringify(inheritedSub);
               Object.assign(thirdItem.style, {
                   display: 'flex',
                   alignItems: 'center',
                   gap: '8px',
                   padding: '4px 8px',
                   borderRadius: '4px',
                   cursor: 'pointer',
                   transition: 'background 0.2s'
               });
               // Ícone pequeno
               const img = document.createElement('img');
               img.src = filePathURL(inheritedSub.icon || 'Nada.png');
               img.alt = ;
               img.width = 24;
               img.height = 24;
               img.style.borderRadius = '4px';
               thirdItem.appendChild(img);
               // Nome
               const nameSpan = document.createElement('span');
               nameSpan.textContent = subName;
               nameSpan.style.color = '#fff';
               nameSpan.style.fontSize = '12px';
               thirdItem.appendChild(nameSpan);
               // Eventos
               thirdItem.addEventListener('mouseenter', () => {
                   thirdItem.style.background = 'rgba(255, 217, 90, 0.1)';
               });
               thirdItem.addEventListener('mouseleave', () => {
                   thirdItem.style.background = 'transparent';
               });
               thirdItem.addEventListener('click', function(e) {
                   e.stopPropagation();
                   e.preventDefault();
                   const clickedData = JSON.parse(this.dataset.subskillData || '{}');
                   const L = getLabels();
                   const subName = clickedData.name || clickedData.n || ;
                   updateSkillDisplay(clickedData);
                   thirdContainer.querySelectorAll('.third-level-subicon').forEach(el => {
                       el.style.background = 'transparent';
                   });
                   this.style.background = 'rgba(255, 217, 90, 0.2)';
               });
               thirdContainer.appendChild(thirdItem);
           });
           parentElement.appendChild(thirdContainer);
       }
       // ===== FUNÇÕES EXISTENTES (modificadas) =====
       function filePathURL(fileName) {
           if (!fileName || fileName.trim() ===  || fileName === 'Nada.png' || fileName.toLowerCase() === 'nada.png') {
               return ;
           }
           const f = encodeURIComponent(fileName.replace(/^Arquivo:|^File:/, ));
           const base = (window.mw && mw.util && typeof mw.util.wikiScript === 'function')
               ? mw.util.wikiScript()
               : (window.mw && mw.config ? (mw.config.get('wgScript') || '/index.php') : '/index.php');
           let url = `${base}?title=Especial:FilePath/${f}`;
           if (window.location.protocol === 'https:' && url.startsWith('http://')) {
               url = url.replace('http://', 'https://');
           }
           return url;
       }
       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 preloadImage(iconFile) {
           const url = filePathURL(iconFile || 'Nada.png');
           if (imagePreloadCache.has(url)) {
               return imagePreloadCache.get(url);
           }
           const promise = new Promise((resolve, reject) => {
               const img = new Image();
               img.decoding = 'async';
               img.loading = 'eager';
               img.referrerPolicy = 'same-origin';
               img.onload = () => resolve(url);
               img.onerror = () => resolve(url);
               img.src = url;
           });
           imagePreloadCache.set(url, promise);
           return promise;
       }
       function getLabels() {
           const skillsRoot = document.getElementById('skills');
           const i18nMap = skillsRoot ? JSON.parse(skillsRoot.dataset.i18nAttrs || '{}') : {};
           const raw = (document.documentElement.lang || skillsRoot?.dataset.i18nDefault || 'pt').toLowerCase();
           const lang = raw === 'pt-br' ? 'pt' : (raw.split('-')[0] || 'pt');
           return i18nMap[lang] || i18nMap.pt || {
               cooldown: 'Recarga',
               energy_gain: 'Ganho de energia',
               energy_cost: 'Custo de energia',
               power: 'Poder',
               power_pvp: 'Poder PvP',
               level: 'Nível'
           };
       }
       function isWeaponModeOn() {
           try {
               return localStorage.getItem('glaWeaponEnabled') === '1';
           } catch (e) {
               return false;
           }
       }
       function getEffectiveAttrs(s) {
           const weaponOn = isWeaponModeOn();
           if (weaponOn && s.weapon) {
               return {
                   powerpve: s.weapon.powerpve || s.powerpve,
                   powerpvp: s.weapon.powerpvp || s.powerpvp,
                   energy: s.weapon.energy || s.energy,
                   cooldown: s.weapon.cooldown || s.cooldown
               };
           }
           return {
               powerpve: s.powerpve,
               powerpvp: s.powerpvp,
               energy: s.energy,
               cooldown: s.cooldown
           };
       }
       function getEffectiveDesc(s) {
           const weaponOn = isWeaponModeOn();
           const raw = (document.documentElement.lang || 'pt').toLowerCase();
           const lang = raw === 'pt-br' ? 'pt' : (raw.split('-')[0] || 'pt');
           if (weaponOn && s.weapon) {
               const wDesc = s.weapon.desc_i18n || s.weapon.desc;
               if (wDesc) {
                   return wDesc[lang] || wDesc.pt || wDesc.en || ;
               }
           }
           const base = s.desc_i18n || s.desc;
           if (base) {
               return base[lang] || base.pt || base.en || ;
           }
           const descI18n = {
               pt: s.descPt || ,
               en: s.descEn || ,
               es: s.descEs || ,
               pl: s.descPl || 
           };
           return descI18n[lang] || descI18n.pt || ;
       }
       function getEffectiveVideo(s) {
           const weaponOn = isWeaponModeOn();
           if (weaponOn && s.weapon && s.weapon.video && s.weapon.video.trim() !== ) {
               return s.weapon.video;
           }
           return s.video || ;
       }
       function getOrCreateSubskillVideo(videoURL) {
           if (!videoURL || videoURL.trim() === ) return null;
           const normalizedURL = normalizeFileURL(videoURL);
           if (!normalizedURL || normalizedURL.trim() === ) return null;
           if (subskillVideoElementCache.has(normalizedURL)) {
               logSubVideo('getOrCreateSubskillVideo: cache hit', { videoURL: normalizedURL });
               return subskillVideoElementCache.get(normalizedURL);
           }
           logSubVideo('getOrCreateSubskillVideo: creating new video', { videoURL: normalizedURL });
           const v = document.createElement('video');
           v.className = 'skill-video';
           v.dataset.sub = '1';
           v.setAttribute('controls', );
           v.setAttribute('preload', 'metadata');
           v.setAttribute('playsinline', );
           v.muted = true;
           v.style.display = 'none';
           v.style.width = '100%';
           v.style.height = 'auto';
           v.style.aspectRatio = '16/9';
           v.style.objectFit = 'cover';
           const ext = (normalizedURL.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 = normalizedURL;
           src.type = mimeType;
           v.appendChild(src);
           v.setAttribute('webkit-playsinline', );
           v.setAttribute('x-webkit-airplay', 'allow');
           subskillVideoElementCache.set(normalizedURL, v);
           const videoBox = document.querySelector('.video-container');
           if (videoBox && !v.isConnected) {
               videoBox.appendChild(v);
               v.load();
           }
           return v;
       }
       function renderSubAttrs(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 renderFlagsRow(flags) {
           const map = {
               aggro: 'Enemyaggro-icon.png',
               bridge: 'Bridgemaker-icon.png',
               wall: 'Destroywall-icon.png',
               quickcast: 'Quickcast-icon.png',
               wallpass: 'Passthroughwall-icon.png'
           };
           const arr = (flags || []).filter(Boolean);
           if (!arr.length) return ;
           const items = arr.map(k => `<img class="skill-flag" data-flag="${k}" alt="" src="${filePathURL(map[k])}">`).join();

return `

${items}

`;

       }
       function applyFlagTooltips(container) {
           const skillsRoot = document.getElementById('skills');
           if (!skillsRoot) return;
           let pack = {};
           try { pack = JSON.parse(skillsRoot.dataset.i18nFlags || '{}'); } catch (e) { }
           const raw = (document.documentElement.lang || 'pt').toLowerCase();
           const lang = raw === 'pt-br' ? 'pt' : (raw.split('-')[0] || 'pt');
           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();
               });
           });
       }
       function ensureRail(iconsBar) {
           const rail = iconsBar.closest('.top-rail');
           if (!rail) {
               return null;
           }
           if (!subRail) {
               subRail = document.createElement('div');
               subRail.className = 'subskills-rail collapsed hidden';
               rail.appendChild(subRail);
           }
           if (!subBar) {
               subBar = document.createElement('div');
               subBar.className = 'subicon-bar';
               subRail.appendChild(subBar);
           }
           if (!spacer) {
               spacer = document.createElement('div');
               spacer.className = 'subskills-spacer';
               rail.parentNode.insertBefore(spacer, rail.nextSibling);
           }
           return rail;
       }
       function showSubVideo(videoURL, videoBox) {
           logSubVideo('showSubVideo called', { videoURL, videoBoxExists: !!videoBox });
           if (!videoBox) return;
           videoBox.querySelectorAll('video.skill-video[data-sub="1"]').forEach(v => {
               v.style.display = 'none';
           });
           const video = getOrCreateSubskillVideo(videoURL);
           if (!video) {
               logSubVideo('no video found for URL, hiding box', { videoURL });
               videoBox.style.display = 'none';
               return;
           }
           if (!video.isConnected || video.parentNode !== videoBox) {
               logSubVideo('video not in DOM, appending', {
                   videoURL,
                   isConnected: video.isConnected,
                   parentNode: video.parentNode
               });
               if (video.parentNode) {
                   video.parentNode.removeChild(video);
               }
               videoBox.appendChild(video);
           }
           logSubVideo('showing video', {
               videoURL,
               readyState: video.readyState,
               paused: video.paused,
               currentTime: video.currentTime
           });
           videoBox.style.display = 'block';
           video.style.display = 'block';
           try {
               video.currentTime = 0;
           } catch (e) { }
           if (video.readyState >= 2) {
               video.play().then(() => {
                   logSubVideo('play() resolved', { videoURL, currentTime: video.currentTime, readyState: video.readyState });
               }).catch(err => {
                   logSubVideo('play() rejected', { videoURL, error: err });
               });
           } else {
               logSubVideo('video not ready, waiting for canplay', { videoURL, readyState: video.readyState });
               const onCanPlay = () => {
                   video.removeEventListener('canplay', onCanPlay);
                   video.play().catch(() => { });
               };
               video.addEventListener('canplay', onCanPlay, { once: true });
           }
       }
       function updateSkillDisplay(skillData) {
           const L = getLabels();
           const weaponOn = isWeaponModeOn();
           
           const attrs = getEffectiveAttrs(skillData);
           const desc = getEffectiveDesc(skillData);
           const videoURL = getEffectiveVideo(skillData);
           
           const descBox = document.querySelector('.skill-description');
           const videoBox = document.querySelector('.video-container');
           
           if (descBox) {
               const level = (skillData.level || ).toString().trim();
               const flagsHTML = skillData.flags ? renderFlagsRow(skillData.flags) : ;
               
               descBox.innerHTML = `

${skillData.name || skillData.n || }

${level ? `

${L.level} ${level}

` : }

                   ${renderSubAttrs(attrs, L)}
${desc.replace(/(.*?)/g, '$1')}
               `;
           }
           
           if (videoBox) {
               const oldFlags = videoBox.querySelector('.skill-flags');
               if (oldFlags) oldFlags.remove();
               
               if (skillData.flags && Array.isArray(skillData.flags) && skillData.flags.length > 0) {
                   const flagsHTML = renderFlagsRow(skillData.flags);
                   if (flagsHTML) {
                       videoBox.insertAdjacentHTML('beforeend', flagsHTML);
                       applyFlagTooltips(videoBox);
                   }
               }
               
               if (!videoURL || videoURL.trim() === ) {
                   videoBox.style.display = 'none';
               } else {
                   showSubVideo(videoURL, videoBox);
               }
           }
       }
       // ===== API PRINCIPAL =====
       api.renderBarFrom = function (el, { iconsBar, descBox, videoBox }) {
           debugLog('api.renderBarFrom START', {
               el: !!el,
               parentIndex: el?.dataset?.index || 'unknown'
           });
           const rail = ensureRail(iconsBar);
           if (!rail) {
               debugLog('No rail found');
               return;
           }
           const rawSubs = el.getAttribute('data-subs') || ;
           const rawOrder = el.getAttribute('data-suborder') || ;
           const parentIdx = el.dataset.index || ;
           debugLog('Raw data:', { rawSubs: rawSubs.substring(0, 100) + '...', rawOrder, parentIdx });
           if (!rawSubs.trim()) {
               if (subRail) subRail.classList.add('collapsed');
               if (subRail) subRail.classList.add('hidden');
               if (subBar) subBar.innerHTML = ;
               if (spacer) spacer.style.height = '0px';
               return;
           }
           let subs;
           try { 
               subs = JSON.parse(rawSubs);
               debugLog('Parsed subs (first 2):', subs.slice(0, 2));
               
               // DEBUG: Verifica estrutura
               subs.forEach((sub, i) => {
                   if (sub.subs || sub.subskills) {
                       debugLog(`Subskill ${i} ("${sub.n || sub.name}") has nested:`, sub.subs || sub.subskills);
                   }
               });
           } catch(e) { 
               subs = [];
               console.error('[Subskills] JSON parse error:', e, 'Raw:', rawSubs.substring(0, 200));
           }
           // Normaliza weapon
           subs = subs.map(sub => {
               if (sub.weaponPacked && !sub.weapon && typeof sub.weaponPacked === 'string' && sub.weaponPacked.trim() !== ) {
                   const parts = sub.weaponPacked.split('~');
                   if (parts.length >= 2) {
                       sub.weapon = {
                           icon: parts[0] || 'Nada.png',
                           powerpve: parts[1] || null,
                           powerpvp: parts[2] || null,
                           cooldown: parts[3] || null,
                           video: parts[4] || ,
                           energy: parts[5] || null
                       };
                       Object.keys(sub.weapon).forEach(k => {
                           if (sub.weapon[k] ===  || sub.weapon[k] === null) {
                               delete sub.weapon[k];
                           }
                       });
                   }
               }
               if (sub.weapon && typeof sub.weapon === 'string') {
                   try {
                       sub.weapon = JSON.parse(sub.weapon);
                   } catch {
                       const parts = sub.weapon.split('~');
                       if (parts.length >= 2) {
                           sub.weapon = {
                               icon: parts[0] || 'Nada.png',
                               powerpve: parts[1] || null,
                               powerpvp: parts[2] || null,
                               cooldown: parts[3] || null,
                               video: parts[4] || ,
                               energy: parts[5] || null
                           };
                           Object.keys(sub.weapon).forEach(k => {
                               if (sub.weapon[k] ===  || sub.weapon[k] === null) {
                                   delete sub.weapon[k];
                               }
                           });
                       } else {
                           sub.weapon = null;
                       }
                   }
               }
               return sub;
           });
           if (!Array.isArray(subs) || subs.length === 0) {
               subRail.classList.add('collapsed');
               subRail.classList.add('hidden');
               subBar.innerHTML = ;
               if (spacer) spacer.style.height = '0px';
               return;
           }
           // Processa estrutura do módulo
           const mainSkills = getMainSkillsMap();
           
           // Passo 1: Aplica herança
           subs = subs.map(sub => applyInheritance(sub, mainSkills));
           
           // Passo 2: Processa estrutura aninhada
           subs = processModuleNestedStructure(subs, 1);
           
           debugLog('Final processed subs:', subs);
           subRail.classList.add('hidden');
           subBar.innerHTML = ;
           let order = subs.map(s => s.name || s.n || );
           if (rawOrder.trim()) {
               try {
                   const preferred = JSON.parse(rawOrder);
                   if (Array.isArray(preferred) && preferred.length) {
                       const byName = new Map(subs.map(s => [(s.name || s.n || ), s]));
                       order = preferred.filter(n => byName.has(n));
                   }
               } catch { }
           }
           // Pré-carrega ícones
           const iconPreloadPromises = [];
           order.forEach(nm => {
               const s = subs.find(x => (x.name || x.n || ) === nm);
               if (s && s.icon && s.icon.trim() !==  && s.icon !== 'Nada.png') {
                   iconPreloadPromises.push(preloadImage(s.icon));
               }
           });
           // Função de renderização
           const renderSubskillsBar = () => {
               order.forEach(nm => {
                   const s = subs.find(x => (x.name || x.n || ) === nm);
                   if (!s) {
                       return;
                   }
                   const item = document.createElement('div');
                   item.className = 'subicon';
                   item.title = s.name || nm;
                   const slugify = window.__skillSlugify || ((str) => (str || ).toLowerCase().replace(/[^\w]+/g, '-').replace(/^-+|-+$/g, ));
                   item.dataset.slug = slugify(s.name || nm);
                   item.dataset.subskillData = JSON.stringify(s);
                   debugLog('Creating subicon:', { 
                       name: s.name || s.n,
                       hasChildren: s.hasChildren,
                       childrenCount: s.childrenCount 
                   });
                   const img = document.createElement('img');
                   img.alt = ;
                   const iconUrl = filePathURL(s.icon || 'Nada.png');
                   if (iconUrl) {
                       img.src = iconUrl;
                   }
                   img.decoding = 'async';
                   img.loading = 'lazy';
                   img.width = 42;
                   img.height = 42;
                   item.appendChild(img);
                   // Adiciona indicador se tiver sub-subskills
                   if (s.hasChildren) {
                       const indicator = document.createElement('div');
                       indicator.className = 'subskill-indicator';
                       indicator.innerHTML = '▼';
                       indicator.title = `Tem ${s.childrenCount || '?'} sub-habilidade(s)`;
                       item.appendChild(indicator);
                       item.classList.add('has-children');
                       debugLog('Added children indicator for:', s.name || s.n);
                   }
                   // Weapon
                   const hasWeapon = s.weapon && (
                       (typeof s.weapon === 'object' && Object.keys(s.weapon).length > 0) ||
                       (typeof s.weapon === 'string' && s.weapon.trim() !== )
                   );
                   const subName = (s.name || s.n || ).trim();
                   if (hasWeapon) {
                       let weaponObj = s.weapon;
                       if (typeof weaponObj === 'string') {
                           try {
                               weaponObj = JSON.parse(weaponObj);
                           } catch {
                               const parts = weaponObj.split('~');
                               if (parts.length >= 2) {
                                   weaponObj = {
                                       icon: parts[0] || 'Nada.png',
                                       powerpve: parts[1] || null,
                                       powerpvp: parts[2] || null,
                                       cooldown: parts[3] || null,
                                       video: parts[4] || ,
                                       energy: parts[5] || null
                                   };
                                   Object.keys(weaponObj).forEach(k => {
                                       if (weaponObj[k] ===  || weaponObj[k] === null) {
                                           delete weaponObj[k];
                                       }
                                   });
                               } else {
                                   weaponObj = null;
                               }
                           }
                       }
                       if (weaponObj && typeof weaponObj === 'object' && Object.keys(weaponObj).length > 0) {
                           try {
                               item.dataset.weapon = JSON.stringify(weaponObj);
                           } catch (e) {
                               console.error('[Subskills] Erro ao serializar weapon:', e);
                           }
                           if (isWeaponModeOn()) {
                               item.classList.add('has-weapon-available');
                           }
                       }
                   }
                   // Evento de clique
                   item.addEventListener('click', (e) => {
                       if (e.target.closest('.subskills-nested') || e.target.closest('.subskills-third-level')) {
                           return;
                       }
                       const L = getLabels();
                       const subName = (s.name || s.n || ).trim();
                       debugLog('Subicon clicked:', { 
                           subName,
                           hasChildren: s.hasChildren,
                           hasNestedSubs: !!(s.subs && Array.isArray(s.subs) && s.subs.length > 0)
                       });
                       // Weapon logic
                       let subWeaponData = null;
                       if (item.dataset.weapon) {
                           try {
                               subWeaponData = JSON.parse(item.dataset.weapon);
                           } catch (e) {
                               subWeaponData = null;
                           }
                       }
                       const hasSubWeapon = !!subWeaponData;
                       const weaponOn = isWeaponModeOn();
                       const weaponEquipped = weaponOn && hasSubWeapon && subWeaponData;
                       // Descrição
                       const raw = (document.documentElement.lang || 'pt').toLowerCase();
                       const lang = raw === 'pt-br' ? 'pt' : (raw.split('-')[0] || 'pt');
                       let chosen = ;
                       if (weaponEquipped && subWeaponData) {
                           const weaponDescPack = subWeaponData.desc_i18n || subWeaponData.desc || {};
                           chosen = weaponDescPack[lang] || weaponDescPack.pt || weaponDescPack.en || ;
                       } else {
                           const base = s.desc_i18n || s.desc || {};
                           chosen = base[lang] || base.pt || base.en || (s.descPt || );
                       }
                       // Atributos
                       let attrsObj = {
                           powerpve: s.powerpve,
                           powerpvp: s.powerpvp,
                           energy: s.energy,
                           cooldown: s.cooldown
                       };
                       if (weaponEquipped && subWeaponData) {
                           attrsObj = {
                               powerpve: subWeaponData.powerpve || s.powerpve,
                               powerpvp: subWeaponData.powerpvp || s.powerpvp,
                               energy: subWeaponData.energy || s.energy,
                               cooldown: subWeaponData.cooldown || s.cooldown
                           };
                       }
                       // Level
                       const level = (weaponEquipped && subWeaponData && subWeaponData.level)
                           ? subWeaponData.level.toString().trim()
                           : (s.level || ).toString().trim();
                       let flagsHTML = ;
                       if (Array.isArray(s.flags) && s.flags.length > 0) {
                           flagsHTML = renderFlagsRow(s.flags);
                       }
                       // Atualiza descrição
                       if (descBox) {

descBox.innerHTML = `

${s.name || nm}

${level ? `

${L.level} ${level}

` : }${renderSubAttrs(attrsObj, L)}

${chosen.replace(/(.*?)/g, '$1')}

`;

                       }
                       if (videoBox) {
                           const oldFlags = videoBox.querySelector('.skill-flags');
                           if (oldFlags) oldFlags.remove();
                           if (flagsHTML) {
                               videoBox.insertAdjacentHTML('beforeend', flagsHTML);
                               applyFlagTooltips(videoBox);
                           }
                       }
                       // Vídeo
                       const effectiveVideo = getEffectiveVideo(s);
                       if (!effectiveVideo || effectiveVideo.trim() ===  || effectiveVideo === 'Nada.png' || effectiveVideo.toLowerCase().includes('nada.png')) {
                           if (videoBox) videoBox.style.display = 'none';
                       } else {
                           showSubVideo(effectiveVideo, videoBox);
                       }
                       // Marca como ativa e limpa outras
                       Array.from(subBar.children).forEach(c => {
                           c.classList.remove('active');
                           c.classList.remove('weapon-equipped');
                           const nested = c.querySelector('.subskills-nested');
                           if (nested) nested.remove();
                           const thirdLevel = c.querySelector('.subskills-third-level');
                           if (thirdLevel) thirdLevel.remove();
                       });
                       item.classList.add('active');
                       if (weaponEquipped) {
                           item.classList.add('weapon-equipped');
                       }
                       
                       window.__lastActiveSkillIcon = item;
                       // LÓGICA PARA SUB-SUBSKILLS
                       if (s.hasChildren && s.subs && Array.isArray(s.subs) && s.subs.length > 0) {
                           debugLog('Rendering nested subs for:', subName);
                           renderNestedSubskills(s.subs, item, subName, parentIdx);
                       } else {
                           const nested = item.querySelector('.subskills-nested');
                           if (nested) nested.remove();
                           const thirdLevel = item.querySelector('.subskills-third-level');
                           if (thirdLevel) thirdLevel.remove();
                       }
                   });
                   if (window.__globalSkillTooltip) {
                       const { show, hide, measureAndPos, lockUntil } = window.__globalSkillTooltip;
                       const label = item.title || ;
                       item.setAttribute('aria-label', label);
                       if (item.hasAttribute('title')) item.removeAttribute('title');
                       item.addEventListener('mouseenter', () => show(item, label));
                       item.addEventListener('mousemove', () => { if (performance.now() >= lockUntil.value) measureAndPos(item); });
                       item.addEventListener('click', () => { lockUntil.value = performance.now() + 240; measureAndPos(item); });
                       item.addEventListener('mouseleave', hide);
                   }
                   subBar.appendChild(item);
               });
               // Weapon classes
               const applyWeaponClassesToSubskills = () => {
                   const weaponOn = isWeaponModeOn();
                   let weaponSubs = document.querySelectorAll('.subicon[data-weapon]');
                   weaponSubs.forEach(el => {
                       if (weaponOn) {
                           el.classList.add('has-weapon-available');
                           if (el.classList.contains('active')) {
                               el.classList.add('weapon-equipped');
                           }
                       } else {
                           el.classList.remove('has-weapon-available');
                           el.classList.remove('weapon-equipped');
                       }
                   });
               };
               applyWeaponClassesToSubskills();
               window.dispatchEvent(new CustomEvent('gla:subskills:ready', { detail: { count: order.length } }));
               if (subBar._weaponToggleListener) {
                   window.removeEventListener('gla:weaponToggled', subBar._weaponToggleListener);
               }
               subBar._weaponToggleListener = (e) => {
                   const enabled = e.detail?.enabled ?? false;
                   applyWeaponClassesToSubskills();
                   setTimeout(() => {
                       const activeSub = subBar.querySelector('.subicon[data-weapon].active')
                           || subBar.querySelector('.subicon.active');
                       if (activeSub) {
                           activeSub.dispatchEvent(new Event('click', { bubbles: true }));
                       } else {
                           api.refreshCurrentSubSafe();
                       }
                   }, 50);
               };
               window.addEventListener('gla:weaponToggled', subBar._weaponToggleListener);
               requestAnimationFrame(() => {
                   subRail.classList.remove('collapsed');
                   subRail.classList.remove('hidden');
                   const h = subRail.offsetHeight || 48;
                   if (spacer) spacer.style.height = h + 'px';
               });
           };
           // Renderiza
           Promise.all(iconPreloadPromises).then(() => {
               setTimeout(() => {
                   renderSubskillsBar();
               }, 10);
           }).catch(() => {
               setTimeout(() => {
                   renderSubskillsBar();
               }, 10);
           });
       };
       api.refreshCurrentSubSafe = function () {
           const btn = document.querySelector('.subskills-rail .subicon.active');
           if (!btn) return false;
           const had = document.body.dataset.suppressSkillPlay;
           document.body.dataset.suppressSkillPlay = '1';
           try {
               btn.dispatchEvent(new Event('click', { bubbles: true }));
           } finally {
               if (had) document.body.dataset.suppressSkillPlay = had;
               else delete document.body.dataset.suppressSkillPlay;
           }
           return true;
       };
       api.hideAll = function (videoBox) {
           const videos = videoBox?.querySelectorAll('.skill-video[data-sub="1"]') || [];
           logSubVideo('api.hideAll called', { videoBoxExists: !!videoBox, videoCount: videos.length });
           videos.forEach(v => {
               try { v.pause(); } catch { }
               v.style.display = 'none';
           });
       };
       window.renderSubskillsBarFrom = function (el, ctx) { api.renderBarFrom(el, ctx); };
       // ===== INICIALIZAÇÃO =====
       function init() {
           debugLog('Initializing subskills system');
           getMainSkillsMap();
           // Escuta mudanças no localStorage
           window.addEventListener('storage', (e) => {
               if (e.key === 'glaWeaponEnabled') {
                   setTimeout(() => api.refreshCurrentSubSafe(), 50);
               }
           });
           window.addEventListener('gla:weaponToggled', (e) => {
               const enabled = e.detail?.enabled ?? false;
               setTimeout(() => {
                   const activeSub = document.querySelector('.subicon[data-weapon].active')
                       || document.querySelector('.subicon.active');
                   if (activeSub) {
                       activeSub.dispatchEvent(new Event('click', { bubbles: true }));
                   } else {
                       api.refreshCurrentSubSafe();
                   }
               }, 50);
           });
           window.addEventListener('gla:subskills:ready', (e) => {
               debugLog('Subskills ready:', e.detail);
           });
       }
       if (document.readyState === 'loading') {
           document.addEventListener('DOMContentLoaded', () => {
               setTimeout(init, 100);
           });
       } else {
           setTimeout(init, 100);
       }
   })();

</script> <style>

   /* ESTILOS EXISTENTES */
   .subicon-bar {
       display: flex;
       gap: 10px;
       padding: 6px 6px;
       overflow-x: auto;
       scrollbar-width: thin;
       scrollbar-color: #ababab transparent;
   }
   .subicon-bar::-webkit-scrollbar {
       height: 6px;
   }
   .subicon-bar::-webkit-scrollbar-thumb {
       background: #151515;
       border-radius: 3px;
   }
   .subicon {
       width: var(--icon-size, 42px);
       height: var(--icon-size, 42px);
       border-radius: var(--icon-radius, 10px);
       overflow: hidden;
       position: relative;
       flex: 0 0 auto;
       cursor: pointer;
       isolation: isolate;
       transition: transform 0.2s ease;
   }
   .subicon img {
       width: 100%;
       height: 100%;
       aspect-ratio: 1 / 1;
       object-fit: cover;
       display: block;
       border-radius: inherit;
   }
   .subicon::after {
       content: "";
       position: absolute;
       inset: 0;
       border-radius: inherit;
       box-shadow: inset 0 0 0 var(--icon-ring-w, 2px) var(--icon-idle, #cfcfcf);
       pointer-events: none;
       z-index: 2;
       transition: box-shadow .12s ease;
   }
   .subicon:hover::after {
       box-shadow: inset 0 0 0 var(--icon-ring-w, 2px) #e6e6e6;
   }
   .subicon.active::after {
       box-shadow: inset 0 0 0 var(--icon-ring-w, 2px) var(--icon-active, #FFD95A);
   }
   .subicon.active {
       transform: scale(1.10);
       z-index: 5;
   }
   .subicon.active::before {
       content: "";
       position: absolute;
       inset: -4px;
       border-radius: calc(var(--icon-radius, 10px) + 4px);
       pointer-events: none;
       z-index: 1;
       opacity: 1;
       box-shadow: 0 0 12px 3px var(--icon-active-glow, rgba(255, 217, 90, .30)),
           0 0 0 calc(var(--icon-ring-w, 2px) * 2) var(--icon-active-ring, rgba(255, 217, 90, .50));
   }
   .top-rail.skills {
       position: relative;
       display: flex;
       flex-direction: column;
       align-items: center;
       overflow: visible;
   }
   .subskills-rail {
       position: absolute;
       left: 50%;
       transform: translateX(-50%);
       top: calc(100% - 1px);
       z-index: 3;
       display: inline-flex;
       justify-content: center;
       align-items: center;
       width: auto;
       max-width: 100%;
       padding: 3px 5px;
       background: rgba(0, 0, 0, .38);
       border: 1px solid rgba(255, 255, 255, .10);
       border-top: 1px solid rgba(255, 255, 255, .08);
       border-radius: 0 0 10px 10px;
       box-shadow: 0 3px 9px rgba(0, 0, 0, .22);
       -webkit-backdrop-filter: blur(2px);
       backdrop-filter: blur(2px);
       overflow: hidden;
       transition: opacity .14s ease, transform .14s ease;
       opacity: 1;
   }
   .subskills-rail.collapsed {
       opacity: 0;
       pointer-events: none;
       transform: translate(-50%, -6px);
   }
   .subskills-rail.hidden {
       visibility: hidden;
   }
   .subskills-spacer {
       height: 0;
       transition: height .2s ease;
   }
   /* NOVOS ESTILOS PARA SUB-SUBSKILLS */
   .subicon.has-children {
       position: relative;
   }
   .subskill-indicator {
       position: absolute;
       bottom: -3px;
       right: -3px;
       background: var(--icon-active, #FFD95A);
       color: #000;
       font-size: 9px;
       width: 14px;
       height: 14px;
       border-radius: 50%;
       display: flex;
       align-items: center;
       justify-content: center;
       font-weight: bold;
       border: 1px solid #000;
       z-index: 10;
   }
   .nested-indicator {
       background: #FF7043 !important;
       font-size: 8px;
       width: 12px;
       height: 12px;
   }
   .subskills-nested {
       animation: fadeIn 0.2s ease;
       z-index: 1000;
   }
   .subskills-third-level {
       animation: fadeIn 0.2s ease;
       z-index: 1001;
   }
   .nested-subicon {
       width: 36px !important;
       height: 36px !important;
       border-radius: 6px !important;
       position: relative;
   }
   .nested-subicon.active::after {
       box-shadow: inset 0 0 0 2px var(--icon-active, #FFD95A) !important;
   }
   .third-level-subicon:hover {
       background: rgba(255, 217, 90, 0.15) !important;
   }
   @keyframes fadeIn {
       from { opacity: 0; transform: translateY(-5px); }
       to { opacity: 1; transform: translateY(0); }
   }
   /* Responsivo */
   @media (max-width: 900px) {
       .subskills-rail {
           position: static;
           transform: none;
           margin-top: -2px;
           border-top: 0;
           border-radius: 0 0 10px 10px;
       }
       .subskills-spacer {
           height: 0 !important;
       }
       
       .subskills-nested,
       .subskills-third-level {
           position: fixed !important;
           top: 50% !important;
           left: 50% !important;
           transform: translate(-50%, -50%) !important;
           max-width: 90vw !important;
           z-index: 9999 !important;
       }
   }
   /* Weapon styles (mantidos do original) */
   .character-box .top-rail.skills .subicon.has-weapon-available:not(.active)::after {
       box-shadow: inset 0 0 0 2px rgba(220, 70, 70, 0.85) !important;
   }
   @keyframes weapon-icon-border-scan {
       0% { background-position: 0% 0%; }
       100% { background-position: 400% 0%; }
   }
   @keyframes weapon-subicon-border-scan {
       0% { background-position: 0% 0%; }
       100% { background-position: 400% 0%; }
   }

</style>