// ═══════════════════════════════════════════════════════════════════════ // INIT — bootstrap everything // ═══════════════════════════════════════════════════════════════════════ function init() { loadConfig(); detectLevel(); STATE.page = detectPage(); // Always-on features setupRETimer(); addConfigButton(); renderProgressPanel(); if (STATE.page === 'battle') { initializeBattle(); autoCheckTask('battle'); } else { // Non-battle page enhancements if (STATE.page === 'itemshop') { enhanceItemShop(); autoCheckTask('buy-health'); } if (STATE.page === 'equipshop') { enhanceEquipShopWithAdvice(); } if (STATE.page === 'shrine') enhanceShrine(); if (STATE.page === 'training') { enhanceTraining(); autoCheckTask('training'); } if (STATE.page === 'monsterlab') { enhanceMonsterLab(); autoCheckTask('feed'); } if (STATE.page === 'arena') { autoCheckTask('arenas'); autoCheckTask('first-blood'); } if (STATE.page === 'character') showGuidancePanel(); if (STATE.page === 'settings') enhanceSettings(); if (STATE.page === 'armory') enhanceArmory(); if (STATE.page === 'abilities') enhanceAbilities(); if (STATE.page === 'monster') enhanceMonsterLab(); } // Dawn initialization if (!localStorage[SP + 'lastDawn']) { try { localStorage[SP + 'lastDawn'] = JSON.stringify(Date.now()); } catch (e) {} } updateRETimer(); // Watch for dynamic page transitions (arena/grindfest → battle) const bodyObserver = new MutationObserver(() => { if (!STATE.inBattle && isBattlePage()) { initializeBattle(); } }); bodyObserver.observe(document.body, { childList: true, subtree: true }); } function initializeBattle() { if (STATE.battleInitialized && STATE.page === 'battle') return; STATE.page = 'battle'; STATE.inBattle = true; STATE._mysticUsed = false; // Reset Mystic Gem tracking on new battle parseBattleState(); // Force tier update from cached level (battle page may not have level_readout) if (STATE.level > 1) updateTier(); setupHover(); setupKeybindings(); addMonsterNumbers(); refreshUI(); addConfigButton(); showDifficultyBanner(); // Watch battle log const log = document.getElementById('textlog'); if (log && !log.dataset.hvObserved) { log.dataset.hvObserved = '1'; const obs = new MutationObserver(() => { parseBattleState(); saveMonsterData(); logRound(); // Log this round's data for analysis if (CFG.hoverEnabled && !STATE.interruptHover && !STATE.interruptAlert && STATE.hoverTarget >= 0) { const a = getSmartAction(); if (a) executeAction(a); } refreshUI(); updateConfigButton(); }); obs.observe(log, { childList: true, subtree: true, characterData: true }); } // Watch vitals pane const vitals = document.getElementById('pane_vitals'); if (vitals && !vitals.dataset.hvObserved) { vitals.dataset.hvObserved = '1'; const vobs = new MutationObserver(() => { parseBattleState(); logRound(); // Also log on vitals changes (catches end-of-round) refreshUI(); updateConfigButton(); // Check if battle ended (completion pane appeared) if (document.getElementById('pane_completion')) { // Battle ended — handled by log observer } }); vobs.observe(vitals, { childList: true, subtree: true, attributes: true }); } // RiddleMaster detection if (document.getElementById('riddlemaster')) { STATE.interruptHover = true; } STATE.battleInitialized = true; } // ── Bootstrap ── if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } console.log('%c🛡️ HV Unified v' + VERSION + '%c | Q=action H=hover C=cure ,=settings', 'color:#fdcb00;font-weight:bold', ''); console.log('%c HV.advice() for guidance | HV.difficulty() for difficulty check', 'color:#888;font-size:10px'); })();