hv-unified/src/ui-overlays.js
GaboGG 402db6bb2f Initial commit: HV Unified v0.11.0 structure
- 23 source files in src/ (build via scripts/build.sh)
- Forum-sourced player knowledge in references/
- DESIGN.md with architecture and corrections
- References to existing scripts (Monsterbation, jpx, HV Utils)
2026-07-20 19:30:26 -04:00

132 lines
5.7 KiB
JavaScript

// ═══════════════════════════════════════════════════════════════════════
// 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();
}