// ═══════════════════════════════════════════════════════════════════════
// ABILITIES — spell & skill unlock guide for abilities page
// ═══════════════════════════════════════════════════════════════════════
function enhanceAbilities() {
if (document.getElementById('hv-abilities')) return;
const lv = STATE.level || 1;
// Detect current tree
const url = window.location.href || '';
const treeMap = {
'tree=general': 'General',
'tree=onehanded': 'One-Handed',
'tree=twohanded': 'Two-Handed',
'tree=dualwield': 'Dual Wield',
'tree=niten': 'Niten',
'tree=staff': 'Staff',
'tree=cloth': 'Cloth',
'tree=light': 'Light',
'tree=heavy': 'Heavy',
'tree=deprecating1': 'Deprecating 1',
'tree=deprecating2': 'Deprecating 2',
'tree=supportive1': 'Supportive 1',
'tree=supportive2': 'Supportive 2',
'tree=elemental': 'Elemental',
'tree=forbidden': 'Forbidden',
'tree=divine': 'Divine',
};
let tree = 'General';
for (const [k, v] of Object.entries(treeMap)) {
if (url.includes(k)) { tree = v; break; }
}
// Read AP from CSS-digit counter
let ap = 0;
$$('#ability_top [class*="f4b"]').some(div => {
const txt = div.textContent || '';
if (txt.includes('Ability') || txt.includes('Mastery')) {
const d = readCSSDigits(div);
if (d !== null) ap = d;
return true;
}
});
// Parse all visible abilities
const abilities = [];
$$('[id^="slot_"][onmouseover]').forEach(el => {
const mo = el.getAttribute('onmouseover') || '';
const m = mo.match(/overability\((\d+),\s*'([^']+)',\s*'([^']*)',\s*'([^']*)',\s*'([^']*)'/);
if (!m) return;
const name = m[2];
const status = m[4];
const nextHtml = m[5];
const lvlMatch = nextHtml.match(/Level\s*(\d+)/i);
const lvlReq = lvlMatch ? parseInt(lvlMatch[1]) : 0;
const apMatch = nextHtml.match(/(\d+)\s*Ability\s*Points?/i);
const apCost = apMatch ? parseInt(apMatch[1]) : 0;
const tierMatch = status.match(/Tier\s*(\d+)/i);
const curTier = tierMatch ? parseInt(tierMatch[1]) : 0;
const acquired = status !== 'Not Acquired' && status !== 'Locked';
abilities.push({ name, status, acquired, curTier, lvlReq, apCost, el });
});
// Free slots
const freeSlots = $$('#ability_top [id^="slot_"]').filter(el => {
return (el.style.backgroundImage || '').includes('t/0.png');
}).length;
// Build panel
let body = `💎 AP: ${ap}
`;
if (freeSlots > 0) {
body += `
| Ability | AP | Lv | Status |
|---|---|---|---|
| ${a.name} | ${a.apCost} | ${a.lvlReq} | ${lbl} |