// ═══════════════════════════════════════════════════════════════════════ // ACTION EXECUTOR — carry out recommended actions // ═══════════════════════════════════════════════════════════════════════ function executeAction(a) { if (!a) return false; let result = false; switch (a.type) { case 'attack': result = attackMonster(a.target); break; case 'spell': result = a.selfTarget ? castSelfSpell(a.name) : castTargetSpell(a.name, a.target); break; case 'skill': result = useSkill(a.name, a.target); break; case 'item': result = useItem(a.id); break; } if (result) { STATE._lastActionTime = Date.now(); } return result; } function attackMonster(i) { if (i < 0 || i >= STATE.monsters.length) return false; const m = STATE.monsters[i]; if (!m || !m.hasAttribute || !m.hasAttribute('onclick')) return false; m.click(); return true; } function castSelfSpell(n) { const s = $$('.btsd[onclick]').find(el => (el.getAttribute('onmouseover') || '').includes(`set_infopane_spell('${n}'`)); if (!s) return false; s.click(); return true; } function castTargetSpell(n, i) { const s = $$('.btsd[onclick]').find(el => (el.getAttribute('onmouseover') || '').includes(`set_infopane_spell('${n}'`)); if (!s) return false; s.click(); // Target selection: click immediately, no setTimeout delay needed. // HV's native lock_action handles the state transition synchronously. if (i >= 0 && i < STATE.monsters.length) { const m = STATE.monsters[i]; if (m && m.hasAttribute && m.hasAttribute('onclick')) m.click(); } return true; } function useItem(id) { let it = document.getElementById('ikey_' + id); // Fallback for auto_ slot IDs (on-cooldown items without explicit DOM id): // search for element with matching onmouseover item ID if (!it && typeof id === 'string' && id.startsWith('auto_')) { const rawId = id.replace('auto_', ''); const itemPane = document.getElementById('pane_item'); if (itemPane) { it = $$('div[onmouseover*="set_infopane_item(' + rawId + ')"]', itemPane)[0]; } } if (!it) return false; // Track that we used the P-slot gem so we don't retry stale state if (id === 'p') STATE._mysticUsed = true; dummy.setAttribute('onclick', it.getAttribute('onmouseover')); dummy.click(); it.click(); return true; } function useSkill(n, i) { let el = null; // Try quickbar first for (const e of $$('#quickbar > .btqs[onclick]')) { if ((e.getAttribute('onmouseover') || '').includes(`set_infopane_spell('${n}'`)) { el = e; break; } } // Try spellbook if (!el) { el = $$('.btsd[onclick]').find(e => (e.getAttribute('onmouseover') || '').includes(`set_infopane_spell('${n}'`)); } // Fallback: onclick match if (!el) { for (const e of $$('.btqb[onclick]')) { if ((e.getAttribute('onclick') || '').includes(n)) { el = e; break; } } } // Fallback: image match if (!el) { for (const e of $$('.btqb img')) { if ((e.src || '').toLowerCase().includes(n.toLowerCase().replace(/\s+/g, '_'))) { el = e.parentElement; break; } } } if (!el) return false; el.click(); // Click target immediately — no setTimeout needed if (i >= 0 && i < STATE.monsters.length) { const m = STATE.monsters[i]; if (m && m.hasAttribute && m.hasAttribute('onclick')) m.click(); } return true; }