diff --git a/scripts/hv-unified.user.js b/scripts/hv-unified.user.js index 77b2594..72cf818 100644 --- a/scripts/hv-unified.user.js +++ b/scripts/hv-unified.user.js @@ -1,7 +1,7 @@ // ==UserScript== // @name HV Unified // @namespace hvunified -// @version 0.13.20 +// @version 0.13.21 // @description HV Unified — battle automation, guidance, and UI enhancements for HentaiVerse // @author GaboGG + Hermes // @match *://*.hentaiverse.org/* @@ -17,7 +17,7 @@ // CONFIG — default settings // ═══════════════════════════════════════════════════════════════════════ -const VERSION = '0.13.20'; +const VERSION = '0.13.21'; const CFG = { // — Battle automation @@ -915,15 +915,23 @@ function findBestChannelingTarget() { const dur = buffDuration(b.icon); if (b.icon === 'regen' && STATE.hp >= CFG.cureRegenHP) continue; const sp = findSpell(b.spell); - // Use a reasonable MP cost: ignore Mana Conservation-reduced values - // (the DOM may show 1 MP at high proficiency, but the real base cost - // determines the savings from a free channeling cast) - let mpCost = sp ? sp.mp : 0; - if (mpCost > 0 && mpCost < 5 && sp) { - // Try to get base cost from knowledge-base - const kb = SPELL_COSTS[b.spell]; - if (kb) mpCost = kb; + // Read base MP cost from the magic pane tooltip (always the full base cost, + // unaffected by channeling's 1-MP display or Mana Conservation) + let baseCost = 0; + const paneEl = $$('.btsd[onmouseover*="' + b.spell.replace(/'/g, "\\'") + '"]', + document.getElementById('pane_magic'))[0]; + if (paneEl) { + const costM = (paneEl.getAttribute('onmouseover') || '') + .match(/set_infopane_spell\('[^']+',\s*'[^']*',\s*'[^']*',\s*(\d+)/); + if (costM) baseCost = parseInt(costM[1]); } + let mpCost = sp ? sp.mp : 0; + // Use base cost from DOM instead of Mana Conservation-reduced cost + if (baseCost > 0) mpCost = baseCost; + // Skip if we can't afford the base cost — game pre-checks even when + // channeling makes the effective cost 1 MP + const maxMp = parseInt((document.getElementById('vrhd') || {}).textContent) || 414; + if (mpCost > 0 && (mpCost / maxMp) > STATE.mp) continue; // Fill level: how empty is this buff relative to its max observed duration let maxDur = STATE.maxBuffDur[b.icon] || 0; // If max matches current (first observation, no learning yet), assume min 30 turns @@ -1141,6 +1149,8 @@ function strategyAdept() { if (ib) return ib; } // If channeling is active, prioritize most expensive buff for best value + // Note: channeling reduces cost to ~1, but the game pre-checks base MP, + // so we skip spells we can't afford at base cost if (STATE.channeling && STATE.mp > 0.10) { const ch = findBestChannelingTarget(); if (ch) return ch; diff --git a/scripts/latest.user.js b/scripts/latest.user.js index 77b2594..72cf818 100644 --- a/scripts/latest.user.js +++ b/scripts/latest.user.js @@ -1,7 +1,7 @@ // ==UserScript== // @name HV Unified // @namespace hvunified -// @version 0.13.20 +// @version 0.13.21 // @description HV Unified — battle automation, guidance, and UI enhancements for HentaiVerse // @author GaboGG + Hermes // @match *://*.hentaiverse.org/* @@ -17,7 +17,7 @@ // CONFIG — default settings // ═══════════════════════════════════════════════════════════════════════ -const VERSION = '0.13.20'; +const VERSION = '0.13.21'; const CFG = { // — Battle automation @@ -915,15 +915,23 @@ function findBestChannelingTarget() { const dur = buffDuration(b.icon); if (b.icon === 'regen' && STATE.hp >= CFG.cureRegenHP) continue; const sp = findSpell(b.spell); - // Use a reasonable MP cost: ignore Mana Conservation-reduced values - // (the DOM may show 1 MP at high proficiency, but the real base cost - // determines the savings from a free channeling cast) - let mpCost = sp ? sp.mp : 0; - if (mpCost > 0 && mpCost < 5 && sp) { - // Try to get base cost from knowledge-base - const kb = SPELL_COSTS[b.spell]; - if (kb) mpCost = kb; + // Read base MP cost from the magic pane tooltip (always the full base cost, + // unaffected by channeling's 1-MP display or Mana Conservation) + let baseCost = 0; + const paneEl = $$('.btsd[onmouseover*="' + b.spell.replace(/'/g, "\\'") + '"]', + document.getElementById('pane_magic'))[0]; + if (paneEl) { + const costM = (paneEl.getAttribute('onmouseover') || '') + .match(/set_infopane_spell\('[^']+',\s*'[^']*',\s*'[^']*',\s*(\d+)/); + if (costM) baseCost = parseInt(costM[1]); } + let mpCost = sp ? sp.mp : 0; + // Use base cost from DOM instead of Mana Conservation-reduced cost + if (baseCost > 0) mpCost = baseCost; + // Skip if we can't afford the base cost — game pre-checks even when + // channeling makes the effective cost 1 MP + const maxMp = parseInt((document.getElementById('vrhd') || {}).textContent) || 414; + if (mpCost > 0 && (mpCost / maxMp) > STATE.mp) continue; // Fill level: how empty is this buff relative to its max observed duration let maxDur = STATE.maxBuffDur[b.icon] || 0; // If max matches current (first observation, no learning yet), assume min 30 turns @@ -1141,6 +1149,8 @@ function strategyAdept() { if (ib) return ib; } // If channeling is active, prioritize most expensive buff for best value + // Note: channeling reduces cost to ~1, but the game pre-checks base MP, + // so we skip spells we can't afford at base cost if (STATE.channeling && STATE.mp > 0.10) { const ch = findBestChannelingTarget(); if (ch) return ch; diff --git a/src/config.js b/src/config.js index efe273a..89a26f4 100644 --- a/src/config.js +++ b/src/config.js @@ -2,7 +2,7 @@ // CONFIG — default settings // ═══════════════════════════════════════════════════════════════════════ -const VERSION = '0.13.20'; +const VERSION = '0.13.21'; const CFG = { // — Battle automation diff --git a/src/header.user.js b/src/header.user.js index 39f3de4..af55819 100644 --- a/src/header.user.js +++ b/src/header.user.js @@ -1,7 +1,7 @@ // ==UserScript== // @name HV Unified // @namespace hvunified -// @version 0.13.20 +// @version 0.13.21 // @description HV Unified — battle automation, guidance, and UI enhancements for HentaiVerse // @author GaboGG + Hermes // @match *://*.hentaiverse.org/* diff --git a/src/strategy-engine.js b/src/strategy-engine.js index be3a344..a0dc481 100644 --- a/src/strategy-engine.js +++ b/src/strategy-engine.js @@ -238,15 +238,23 @@ function findBestChannelingTarget() { const dur = buffDuration(b.icon); if (b.icon === 'regen' && STATE.hp >= CFG.cureRegenHP) continue; const sp = findSpell(b.spell); - // Use a reasonable MP cost: ignore Mana Conservation-reduced values - // (the DOM may show 1 MP at high proficiency, but the real base cost - // determines the savings from a free channeling cast) - let mpCost = sp ? sp.mp : 0; - if (mpCost > 0 && mpCost < 5 && sp) { - // Try to get base cost from knowledge-base - const kb = SPELL_COSTS[b.spell]; - if (kb) mpCost = kb; + // Read base MP cost from the magic pane tooltip (always the full base cost, + // unaffected by channeling's 1-MP display or Mana Conservation) + let baseCost = 0; + const paneEl = $$('.btsd[onmouseover*="' + b.spell.replace(/'/g, "\\'") + '"]', + document.getElementById('pane_magic'))[0]; + if (paneEl) { + const costM = (paneEl.getAttribute('onmouseover') || '') + .match(/set_infopane_spell\('[^']+',\s*'[^']*',\s*'[^']*',\s*(\d+)/); + if (costM) baseCost = parseInt(costM[1]); } + let mpCost = sp ? sp.mp : 0; + // Use base cost from DOM instead of Mana Conservation-reduced cost + if (baseCost > 0) mpCost = baseCost; + // Skip if we can't afford the base cost — game pre-checks even when + // channeling makes the effective cost 1 MP + const maxMp = parseInt((document.getElementById('vrhd') || {}).textContent) || 414; + if (mpCost > 0 && (mpCost / maxMp) > STATE.mp) continue; // Fill level: how empty is this buff relative to its max observed duration let maxDur = STATE.maxBuffDur[b.icon] || 0; // If max matches current (first observation, no learning yet), assume min 30 turns @@ -464,6 +472,8 @@ function strategyAdept() { if (ib) return ib; } // If channeling is active, prioritize most expensive buff for best value + // Note: channeling reduces cost to ~1, but the game pre-checks base MP, + // so we skip spells we can't afford at base cost if (STATE.channeling && STATE.mp > 0.10) { const ch = findBestChannelingTarget(); if (ch) return ch;