v0.13.22 - Cache base MP costs at battle start (dynamic, not hardcoded)
- Removed SPELL_COSTS hardcoded table - Base MP costs are now read from the magic pane's onmouseover tooltip at battle init and cached in STATE._baseMpCosts - Cache is populated during parseBattleState() and persists for the entire battle, avoiding channeling's 1-MP display - Cache resets on new battle, so level-ups or stat changes are picked up when starting a new arena
This commit is contained in:
parent
80fc77600a
commit
31799a7261
8 changed files with 78 additions and 45 deletions
|
|
@ -1,7 +1,7 @@
|
|||
// ==UserScript==
|
||||
// @name HV Unified
|
||||
// @namespace hvunified
|
||||
// @version 0.13.21
|
||||
// @version 0.13.22
|
||||
// @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.21';
|
||||
const VERSION = '0.13.22';
|
||||
|
||||
const CFG = {
|
||||
// — Battle automation
|
||||
|
|
@ -79,6 +79,8 @@ const STATE = {
|
|||
|
||||
_mysticUsed: false, // Track if Mystic Gem has been used this battle
|
||||
|
||||
_baseMpCosts: {}, // Cached base MP costs for buff spells (read from magic pane at battle start)
|
||||
|
||||
maxBuffDur: {}, // Tracks highest duration seen per buff icon
|
||||
|
||||
hoverTarget: -1,
|
||||
|
|
@ -433,7 +435,13 @@ function parseBattleState() {
|
|||
const omo = el.getAttribute('onmouseover') || '';
|
||||
if (!omo) return;
|
||||
const m = omo.match(/battle\.set_infopane_spell\('([^']+)',\s*'[^']*',\s*'[^']*',\s*(\d+),\s*(\d+),\s*(\d+)\)/);
|
||||
if (m) STATE.spellsKnown.push({ n: m[1], mp: parseInt(m[2]), chr: parseInt(m[3]), cd: parseInt(m[4]) });
|
||||
if (m) {
|
||||
STATE.spellsKnown.push({ n: m[1], mp: parseInt(m[2]), chr: parseInt(m[3]), cd: parseInt(m[4]) });
|
||||
// Cache the base MP cost (from magic pane, unaffected by channeling/Conservation)
|
||||
if (!STATE._baseMpCosts[m[1]]) {
|
||||
STATE._baseMpCosts[m[1]] = parseInt(m[2]);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -915,19 +923,21 @@ function findBestChannelingTarget() {
|
|||
const dur = buffDuration(b.icon);
|
||||
if (b.icon === 'regen' && STATE.hp >= CFG.cureRegenHP) continue;
|
||||
const sp = findSpell(b.spell);
|
||||
// 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;
|
||||
// Use cached base MP cost (snapshotted at battle start from the magic pane)
|
||||
// This avoids the channeling 1-MP display issue and Mana Conservation fluctuation
|
||||
let mpCost = STATE._baseMpCosts[b.spell] || 0;
|
||||
if (mpCost === 0 && sp) {
|
||||
// Not cached yet — read from magic pane directly
|
||||
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]);
|
||||
if (costM) mpCost = parseInt(costM[1]);
|
||||
}
|
||||
if (mpCost === 0) mpCost = sp.mp; // last resort: use what parseBattleState gave us
|
||||
STATE._baseMpCosts[b.spell] = mpCost;
|
||||
}
|
||||
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;
|
||||
|
|
@ -3317,6 +3327,7 @@ function initializeBattle() {
|
|||
STATE.page = 'battle';
|
||||
STATE.inBattle = true;
|
||||
STATE._mysticUsed = false; // Reset Mystic Gem tracking on new battle
|
||||
STATE._baseMpCosts = {}; // Reset base cost cache
|
||||
|
||||
parseBattleState();
|
||||
// Force tier update from cached level (battle page may not have level_readout)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// ==UserScript==
|
||||
// @name HV Unified
|
||||
// @namespace hvunified
|
||||
// @version 0.13.21
|
||||
// @version 0.13.22
|
||||
// @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.21';
|
||||
const VERSION = '0.13.22';
|
||||
|
||||
const CFG = {
|
||||
// — Battle automation
|
||||
|
|
@ -79,6 +79,8 @@ const STATE = {
|
|||
|
||||
_mysticUsed: false, // Track if Mystic Gem has been used this battle
|
||||
|
||||
_baseMpCosts: {}, // Cached base MP costs for buff spells (read from magic pane at battle start)
|
||||
|
||||
maxBuffDur: {}, // Tracks highest duration seen per buff icon
|
||||
|
||||
hoverTarget: -1,
|
||||
|
|
@ -433,7 +435,13 @@ function parseBattleState() {
|
|||
const omo = el.getAttribute('onmouseover') || '';
|
||||
if (!omo) return;
|
||||
const m = omo.match(/battle\.set_infopane_spell\('([^']+)',\s*'[^']*',\s*'[^']*',\s*(\d+),\s*(\d+),\s*(\d+)\)/);
|
||||
if (m) STATE.spellsKnown.push({ n: m[1], mp: parseInt(m[2]), chr: parseInt(m[3]), cd: parseInt(m[4]) });
|
||||
if (m) {
|
||||
STATE.spellsKnown.push({ n: m[1], mp: parseInt(m[2]), chr: parseInt(m[3]), cd: parseInt(m[4]) });
|
||||
// Cache the base MP cost (from magic pane, unaffected by channeling/Conservation)
|
||||
if (!STATE._baseMpCosts[m[1]]) {
|
||||
STATE._baseMpCosts[m[1]] = parseInt(m[2]);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -915,19 +923,21 @@ function findBestChannelingTarget() {
|
|||
const dur = buffDuration(b.icon);
|
||||
if (b.icon === 'regen' && STATE.hp >= CFG.cureRegenHP) continue;
|
||||
const sp = findSpell(b.spell);
|
||||
// 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;
|
||||
// Use cached base MP cost (snapshotted at battle start from the magic pane)
|
||||
// This avoids the channeling 1-MP display issue and Mana Conservation fluctuation
|
||||
let mpCost = STATE._baseMpCosts[b.spell] || 0;
|
||||
if (mpCost === 0 && sp) {
|
||||
// Not cached yet — read from magic pane directly
|
||||
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]);
|
||||
if (costM) mpCost = parseInt(costM[1]);
|
||||
}
|
||||
if (mpCost === 0) mpCost = sp.mp; // last resort: use what parseBattleState gave us
|
||||
STATE._baseMpCosts[b.spell] = mpCost;
|
||||
}
|
||||
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;
|
||||
|
|
@ -3317,6 +3327,7 @@ function initializeBattle() {
|
|||
STATE.page = 'battle';
|
||||
STATE.inBattle = true;
|
||||
STATE._mysticUsed = false; // Reset Mystic Gem tracking on new battle
|
||||
STATE._baseMpCosts = {}; // Reset base cost cache
|
||||
|
||||
parseBattleState();
|
||||
// Force tier update from cached level (battle page may not have level_readout)
|
||||
|
|
|
|||
|
|
@ -91,7 +91,13 @@ function parseBattleState() {
|
|||
const omo = el.getAttribute('onmouseover') || '';
|
||||
if (!omo) return;
|
||||
const m = omo.match(/battle\.set_infopane_spell\('([^']+)',\s*'[^']*',\s*'[^']*',\s*(\d+),\s*(\d+),\s*(\d+)\)/);
|
||||
if (m) STATE.spellsKnown.push({ n: m[1], mp: parseInt(m[2]), chr: parseInt(m[3]), cd: parseInt(m[4]) });
|
||||
if (m) {
|
||||
STATE.spellsKnown.push({ n: m[1], mp: parseInt(m[2]), chr: parseInt(m[3]), cd: parseInt(m[4]) });
|
||||
// Cache the base MP cost (from magic pane, unaffected by channeling/Conservation)
|
||||
if (!STATE._baseMpCosts[m[1]]) {
|
||||
STATE._baseMpCosts[m[1]] = parseInt(m[2]);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
// CONFIG — default settings
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
|
||||
const VERSION = '0.13.21';
|
||||
const VERSION = '0.13.22';
|
||||
|
||||
const CFG = {
|
||||
// — Battle automation
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// ==UserScript==
|
||||
// @name HV Unified
|
||||
// @namespace hvunified
|
||||
// @version 0.13.21
|
||||
// @version 0.13.22
|
||||
// @description HV Unified — battle automation, guidance, and UI enhancements for HentaiVerse
|
||||
// @author GaboGG + Hermes
|
||||
// @match *://*.hentaiverse.org/*
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ function initializeBattle() {
|
|||
STATE.page = 'battle';
|
||||
STATE.inBattle = true;
|
||||
STATE._mysticUsed = false; // Reset Mystic Gem tracking on new battle
|
||||
STATE._baseMpCosts = {}; // Reset base cost cache
|
||||
|
||||
parseBattleState();
|
||||
// Force tier update from cached level (battle page may not have level_readout)
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@ const STATE = {
|
|||
|
||||
_mysticUsed: false, // Track if Mystic Gem has been used this battle
|
||||
|
||||
_baseMpCosts: {}, // Cached base MP costs for buff spells (read from magic pane at battle start)
|
||||
|
||||
maxBuffDur: {}, // Tracks highest duration seen per buff icon
|
||||
|
||||
hoverTarget: -1,
|
||||
|
|
|
|||
|
|
@ -238,19 +238,21 @@ function findBestChannelingTarget() {
|
|||
const dur = buffDuration(b.icon);
|
||||
if (b.icon === 'regen' && STATE.hp >= CFG.cureRegenHP) continue;
|
||||
const sp = findSpell(b.spell);
|
||||
// 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;
|
||||
// Use cached base MP cost (snapshotted at battle start from the magic pane)
|
||||
// This avoids the channeling 1-MP display issue and Mana Conservation fluctuation
|
||||
let mpCost = STATE._baseMpCosts[b.spell] || 0;
|
||||
if (mpCost === 0 && sp) {
|
||||
// Not cached yet — read from magic pane directly
|
||||
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]);
|
||||
if (costM) mpCost = parseInt(costM[1]);
|
||||
}
|
||||
if (mpCost === 0) mpCost = sp.mp; // last resort: use what parseBattleState gave us
|
||||
STATE._baseMpCosts[b.spell] = mpCost;
|
||||
}
|
||||
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;
|
||||
|
|
|
|||
Loading…
Reference in a new issue