// ═══════════════════════════════════════════════════════════════════════ // UI OVERLAYS — cooldowns, durations, alerts, monster numbers, monster HP // ═══════════════════════════════════════════════════════════════════════ function updateCooldownDisplay() { if (!CFG.showCooldowns) return; $$('.hv-cd-overlay').forEach(e => e.remove()); $$('.btqb').forEach(el => { const img = el.querySelector('img'); if (!img) return; const src = (img.src || '').toLowerCase(); for (const [k, v] of Object.entries(STATE.cooldowns)) { if (v <= 0) continue; if (src.includes(k.toLowerCase()) || src.includes(k.replace(/_/g, ''))) { const o = document.createElement('div'); o.className = 'hv-cd-overlay'; o.style.cssText = 'position:absolute;top:0;right:0;background:rgba(0,0,0,0.7);color:#f00;font-size:9px;font-weight:bold;padding:0 2px;border-radius:2px;pointer-events:none'; o.textContent = v; el.style.position = 'relative'; el.appendChild(o); break; } } }); } function updateDurationDisplay() { if (!CFG.showDurations) return; $$('.hv-dur-overlay').forEach(e => e.remove()); const pane = document.getElementById('pane_effects'); if (!pane) return; $$('img[onmouseover]', pane).forEach(img => { const omo = img.getAttribute('onmouseover') || ''; const dur = omo.match(/(\d+)\s*turns?\s*rem/); if (!dur) return; const t = parseInt(dur[1]); const st = omo.match(/(\d+)\s*stacks?/i); const o = document.createElement('div'); o.className = 'hv-dur-overlay'; o.style.cssText = `position:absolute;bottom:-2px;left:50%;transform:translateX(-50%);background:${t < 3 ? '#f44' : '#44f'};color:#fff;font-size:8px;font-weight:bold;padding:0 2px;border-radius:2px;pointer-events:none;z-index:1`; o.textContent = st ? `${t}x${parseInt(st[1])}` : t; img.parentElement.style.position = 'relative'; img.parentElement.appendChild(o); }); } function updateAlertColours() { if (!CFG.alertColours) return; let bg = '#EDEBDF'; STATE.interruptAlert = false; if (!$('img[src$="bar_dgreen.png"]') && $('img[src$="fallenshield.png"]')) { bg = 'magenta'; STATE.interruptAlert = true; } else if (STATE.hp < 0.25) { bg = '#ff4488'; STATE.interruptAlert = true; } else if (STATE.mp < 0.15) { bg = '#4444aa'; } else if (Object.values(STATE.buffs).some(d => d < 2 && d > 0)) { bg = '#88ccff'; } const v = document.getElementById('pane_vitals'); if (v) v.style.background = bg; } function addMonsterNumbers() { if (!CFG.showMonsterNumbers) return; STATE.monsters.forEach((m, i) => { if (!m || !m.querySelector || m.querySelector('.hv-monster-num')) return; const b = m.querySelector('.btm1'); if (!b) return; const s = document.createElement('span'); s.className = 'hv-monster-num'; s.style.cssText = 'font-weight:bold;margin-right:3px;color:#999'; s.textContent = (i + 1); b.insertBefore(s, b.firstChild); }); } function updateMonsterHPDisplay() { if (!CFG.showMonsterHP) return; STATE.monsters.forEach((m, i) => { if (!m || !m.hasAttribute || !m.hasAttribute('onclick')) return; const n = m.querySelector('.btm1'); if (!n) return; const nm = (n.textContent || '').replace(/^\d+\s*/, '').trim(); const md = STATE.monsterData[nm]; if (!md || !md.hp || md.seen < 3) return; const hpBar = m.querySelector('img[src$="nbargreen.png"]'); if (!hpBar) return; const r = clamp(parseInt(hpBar.style.width || '') / 120, 0.01, 1); const txt = `HP: ${Math.max(1, Math.round(r * md.hp)).toLocaleString()} / ${md.hp.toLocaleString()}`; const ex = m.querySelector('.hv-monster-hp'); if (ex) { ex.textContent = txt; return; } const d = document.createElement('div'); d.className = 'hv-monster-hp'; d.style.cssText = 'display:inline-block;position:relative;left:204px;top:-20px;font-size:9px;color:#888'; d.textContent = txt; m.appendChild(d); }); } function saveMonsterData() { STATE.monsters.forEach((m, i) => { if (!m || !m.hasAttribute || !m.hasAttribute('onclick')) return; const n = m.querySelector('.btm1'); if (!n) return; const nm = (n.textContent || '').replace(/^\d+\s*/, '').trim(); if (!nm) return; const hpBar = m.querySelector('img[src$="nbargreen.png"]'); if (hpBar) { const r = clamp(parseInt(hpBar.style.width || '') / 120, 0.01, 1); if (!STATE.monsterData[nm]) STATE.monsterData[nm] = { hp: 0, seen: 0 }; STATE.monsterData[nm].hp = Math.max( STATE.monsterData[nm].hp, Math.round(STATE.monsterData[nm].hp * 0.7 + 1000 / r * 0.3) ); STATE.monsterData[nm].seen++; } }); try { localStorage[SP + 'monsters'] = JSON.stringify(STATE.monsterData); } catch (e) {} } function refreshUI() { if (CFG.showDurations) updateDurationDisplay(); if (CFG.showCooldowns) updateCooldownDisplay(); if (CFG.alertColours) updateAlertColours(); if (CFG.showMonsterNumbers) addMonsterNumbers(); if (CFG.showMonsterHP) updateMonsterHPDisplay(); }