- 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)
132 lines
5.7 KiB
JavaScript
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();
|
|
}
|