v0.13.21 - Dynamic base cost read + affordable MP check for channeling
- Removed hardcoded SPELL_COSTS table (was brittle) - Now reads base MP cost directly from the magic pane's onmouseover tooltip (always shows true base cost, unaffected by channeling's 1-MP display or Mana Conservation) - Added affordable check: skip spells where base cost exceeds current MP, even though channeling would make it cost 1 - The game's native MP check (shown by "You do not have enough") checks against base cost, not channeling-reduced cost
This commit is contained in:
parent
e20fe0495c
commit
80fc77600a
5 changed files with 60 additions and 30 deletions
|
|
@ -1,7 +1,7 @@
|
||||||
// ==UserScript==
|
// ==UserScript==
|
||||||
// @name HV Unified
|
// @name HV Unified
|
||||||
// @namespace hvunified
|
// @namespace hvunified
|
||||||
// @version 0.13.20
|
// @version 0.13.21
|
||||||
// @description HV Unified — battle automation, guidance, and UI enhancements for HentaiVerse
|
// @description HV Unified — battle automation, guidance, and UI enhancements for HentaiVerse
|
||||||
// @author GaboGG + Hermes
|
// @author GaboGG + Hermes
|
||||||
// @match *://*.hentaiverse.org/*
|
// @match *://*.hentaiverse.org/*
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
// CONFIG — default settings
|
// CONFIG — default settings
|
||||||
// ═══════════════════════════════════════════════════════════════════════
|
// ═══════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
const VERSION = '0.13.20';
|
const VERSION = '0.13.21';
|
||||||
|
|
||||||
const CFG = {
|
const CFG = {
|
||||||
// — Battle automation
|
// — Battle automation
|
||||||
|
|
@ -915,15 +915,23 @@ function findBestChannelingTarget() {
|
||||||
const dur = buffDuration(b.icon);
|
const dur = buffDuration(b.icon);
|
||||||
if (b.icon === 'regen' && STATE.hp >= CFG.cureRegenHP) continue;
|
if (b.icon === 'regen' && STATE.hp >= CFG.cureRegenHP) continue;
|
||||||
const sp = findSpell(b.spell);
|
const sp = findSpell(b.spell);
|
||||||
// Use a reasonable MP cost: ignore Mana Conservation-reduced values
|
// Read base MP cost from the magic pane tooltip (always the full base cost,
|
||||||
// (the DOM may show 1 MP at high proficiency, but the real base cost
|
// unaffected by channeling's 1-MP display or Mana Conservation)
|
||||||
// determines the savings from a free channeling cast)
|
let baseCost = 0;
|
||||||
let mpCost = sp ? sp.mp : 0;
|
const paneEl = $$('.btsd[onmouseover*="' + b.spell.replace(/'/g, "\\'") + '"]',
|
||||||
if (mpCost > 0 && mpCost < 5 && sp) {
|
document.getElementById('pane_magic'))[0];
|
||||||
// Try to get base cost from knowledge-base
|
if (paneEl) {
|
||||||
const kb = SPELL_COSTS[b.spell];
|
const costM = (paneEl.getAttribute('onmouseover') || '')
|
||||||
if (kb) mpCost = kb;
|
.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
|
// Fill level: how empty is this buff relative to its max observed duration
|
||||||
let maxDur = STATE.maxBuffDur[b.icon] || 0;
|
let maxDur = STATE.maxBuffDur[b.icon] || 0;
|
||||||
// If max matches current (first observation, no learning yet), assume min 30 turns
|
// If max matches current (first observation, no learning yet), assume min 30 turns
|
||||||
|
|
@ -1141,6 +1149,8 @@ function strategyAdept() {
|
||||||
if (ib) return ib;
|
if (ib) return ib;
|
||||||
}
|
}
|
||||||
// If channeling is active, prioritize most expensive buff for best value
|
// 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) {
|
if (STATE.channeling && STATE.mp > 0.10) {
|
||||||
const ch = findBestChannelingTarget();
|
const ch = findBestChannelingTarget();
|
||||||
if (ch) return ch;
|
if (ch) return ch;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// ==UserScript==
|
// ==UserScript==
|
||||||
// @name HV Unified
|
// @name HV Unified
|
||||||
// @namespace hvunified
|
// @namespace hvunified
|
||||||
// @version 0.13.20
|
// @version 0.13.21
|
||||||
// @description HV Unified — battle automation, guidance, and UI enhancements for HentaiVerse
|
// @description HV Unified — battle automation, guidance, and UI enhancements for HentaiVerse
|
||||||
// @author GaboGG + Hermes
|
// @author GaboGG + Hermes
|
||||||
// @match *://*.hentaiverse.org/*
|
// @match *://*.hentaiverse.org/*
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
// CONFIG — default settings
|
// CONFIG — default settings
|
||||||
// ═══════════════════════════════════════════════════════════════════════
|
// ═══════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
const VERSION = '0.13.20';
|
const VERSION = '0.13.21';
|
||||||
|
|
||||||
const CFG = {
|
const CFG = {
|
||||||
// — Battle automation
|
// — Battle automation
|
||||||
|
|
@ -915,15 +915,23 @@ function findBestChannelingTarget() {
|
||||||
const dur = buffDuration(b.icon);
|
const dur = buffDuration(b.icon);
|
||||||
if (b.icon === 'regen' && STATE.hp >= CFG.cureRegenHP) continue;
|
if (b.icon === 'regen' && STATE.hp >= CFG.cureRegenHP) continue;
|
||||||
const sp = findSpell(b.spell);
|
const sp = findSpell(b.spell);
|
||||||
// Use a reasonable MP cost: ignore Mana Conservation-reduced values
|
// Read base MP cost from the magic pane tooltip (always the full base cost,
|
||||||
// (the DOM may show 1 MP at high proficiency, but the real base cost
|
// unaffected by channeling's 1-MP display or Mana Conservation)
|
||||||
// determines the savings from a free channeling cast)
|
let baseCost = 0;
|
||||||
let mpCost = sp ? sp.mp : 0;
|
const paneEl = $$('.btsd[onmouseover*="' + b.spell.replace(/'/g, "\\'") + '"]',
|
||||||
if (mpCost > 0 && mpCost < 5 && sp) {
|
document.getElementById('pane_magic'))[0];
|
||||||
// Try to get base cost from knowledge-base
|
if (paneEl) {
|
||||||
const kb = SPELL_COSTS[b.spell];
|
const costM = (paneEl.getAttribute('onmouseover') || '')
|
||||||
if (kb) mpCost = kb;
|
.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
|
// Fill level: how empty is this buff relative to its max observed duration
|
||||||
let maxDur = STATE.maxBuffDur[b.icon] || 0;
|
let maxDur = STATE.maxBuffDur[b.icon] || 0;
|
||||||
// If max matches current (first observation, no learning yet), assume min 30 turns
|
// If max matches current (first observation, no learning yet), assume min 30 turns
|
||||||
|
|
@ -1141,6 +1149,8 @@ function strategyAdept() {
|
||||||
if (ib) return ib;
|
if (ib) return ib;
|
||||||
}
|
}
|
||||||
// If channeling is active, prioritize most expensive buff for best value
|
// 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) {
|
if (STATE.channeling && STATE.mp > 0.10) {
|
||||||
const ch = findBestChannelingTarget();
|
const ch = findBestChannelingTarget();
|
||||||
if (ch) return ch;
|
if (ch) return ch;
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
// CONFIG — default settings
|
// CONFIG — default settings
|
||||||
// ═══════════════════════════════════════════════════════════════════════
|
// ═══════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
const VERSION = '0.13.20';
|
const VERSION = '0.13.21';
|
||||||
|
|
||||||
const CFG = {
|
const CFG = {
|
||||||
// — Battle automation
|
// — Battle automation
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// ==UserScript==
|
// ==UserScript==
|
||||||
// @name HV Unified
|
// @name HV Unified
|
||||||
// @namespace hvunified
|
// @namespace hvunified
|
||||||
// @version 0.13.20
|
// @version 0.13.21
|
||||||
// @description HV Unified — battle automation, guidance, and UI enhancements for HentaiVerse
|
// @description HV Unified — battle automation, guidance, and UI enhancements for HentaiVerse
|
||||||
// @author GaboGG + Hermes
|
// @author GaboGG + Hermes
|
||||||
// @match *://*.hentaiverse.org/*
|
// @match *://*.hentaiverse.org/*
|
||||||
|
|
|
||||||
|
|
@ -238,15 +238,23 @@ function findBestChannelingTarget() {
|
||||||
const dur = buffDuration(b.icon);
|
const dur = buffDuration(b.icon);
|
||||||
if (b.icon === 'regen' && STATE.hp >= CFG.cureRegenHP) continue;
|
if (b.icon === 'regen' && STATE.hp >= CFG.cureRegenHP) continue;
|
||||||
const sp = findSpell(b.spell);
|
const sp = findSpell(b.spell);
|
||||||
// Use a reasonable MP cost: ignore Mana Conservation-reduced values
|
// Read base MP cost from the magic pane tooltip (always the full base cost,
|
||||||
// (the DOM may show 1 MP at high proficiency, but the real base cost
|
// unaffected by channeling's 1-MP display or Mana Conservation)
|
||||||
// determines the savings from a free channeling cast)
|
let baseCost = 0;
|
||||||
let mpCost = sp ? sp.mp : 0;
|
const paneEl = $$('.btsd[onmouseover*="' + b.spell.replace(/'/g, "\\'") + '"]',
|
||||||
if (mpCost > 0 && mpCost < 5 && sp) {
|
document.getElementById('pane_magic'))[0];
|
||||||
// Try to get base cost from knowledge-base
|
if (paneEl) {
|
||||||
const kb = SPELL_COSTS[b.spell];
|
const costM = (paneEl.getAttribute('onmouseover') || '')
|
||||||
if (kb) mpCost = kb;
|
.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
|
// Fill level: how empty is this buff relative to its max observed duration
|
||||||
let maxDur = STATE.maxBuffDur[b.icon] || 0;
|
let maxDur = STATE.maxBuffDur[b.icon] || 0;
|
||||||
// If max matches current (first observation, no learning yet), assume min 30 turns
|
// If max matches current (first observation, no learning yet), assume min 30 turns
|
||||||
|
|
@ -464,6 +472,8 @@ function strategyAdept() {
|
||||||
if (ib) return ib;
|
if (ib) return ib;
|
||||||
}
|
}
|
||||||
// If channeling is active, prioritize most expensive buff for best value
|
// 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) {
|
if (STATE.channeling && STATE.mp > 0.10) {
|
||||||
const ch = findBestChannelingTarget();
|
const ch = findBestChannelingTarget();
|
||||||
if (ch) return ch;
|
if (ch) return ch;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue