v0.13.15 - Fix stale Mystic Gem state, add debug logging to Q press

- Mystic Gem usage now tracked with STATE._mysticUsed flag
  Prevents re-triggering the gem check after it's been consumed
  (stale STATE.itemsKnown was causing infinite 'item → p' loops)
- Flag resets on new battle initialization
- Added buff durations + channeling status to Q-key console output
  Shows: ch=true/false mp=X% oc=X buffs: heartseeker=47 haste=24 ...
This commit is contained in:
GaboGG 2026-07-26 14:01:48 -04:00
parent 23f00450be
commit 25ea8b4543
9 changed files with 54 additions and 24 deletions

View file

@ -1,7 +1,7 @@
// ==UserScript==
// @name HV Unified
// @namespace hvunified
// @version 0.13.14
// @version 0.13.15
// @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.14';
const VERSION = '0.13.15';
const CFG = {
// — Battle automation
@ -77,6 +77,8 @@ const STATE = {
inBattle: false,
battleInitialized: false,
_mysticUsed: false, // Track if Mystic Gem has been used this battle
maxBuffDur: {}, // Tracks highest duration seen per buff icon
hoverTarget: -1,
@ -967,10 +969,10 @@ function getSmartAction() {
// ── Novice (1-50): survival first ──
function strategyNovice() {
// 0. Mystic Gem for channeling — free OC
// 0. Mystic Gem for channeling — only use once per battle
// (channeling status is tracked from the battle log)
const gem = hasUsableGem('channel');
if (gem && !STATE.channeling)
if (gem && !STATE.channeling && !STATE._mysticUsed)
return { type: 'item', id: gem.id, selfTarget: true };
// 1. Health items — safe to use early
@ -1088,9 +1090,9 @@ function strategyNovice() {
// ── Adept (50-150): building power ──
function strategyAdept() {
// 0. Mystic Gem for channeling — free OC
// 0. Mystic Gem for channeling — only use once per battle
const gem = hasUsableGem('channel');
if (gem && !STATE.channeling)
if (gem && !STATE.channeling && !STATE._mysticUsed)
return { type: 'item', id: gem.id, selfTarget: true };
// 1. Health items — safe to use early
@ -1220,9 +1222,9 @@ function strategyAdept() {
// ── Veteran (150-300) / Master (300+): full rotation ──
function strategyVeteran() {
// 0. Mystic Gem for channeling
// 0. Mystic Gem for channeling — only use once per battle
const gem = hasUsableGem('channel');
if (gem && !STATE.channeling)
if (gem && !STATE.channeling && !STATE._mysticUsed)
return { type: 'item', id: gem.id, selfTarget: true };
// 1. Health items — safe to use early
@ -1410,6 +1412,8 @@ function castTargetSpell(n, i) {
function useItem(id) {
const it = document.getElementById('ikey_' + id);
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();
@ -1516,6 +1520,11 @@ function setupKeybindings() {
if (!isInputFocused() && isBattlePage()) {
console.log('%c[HV] Q pressed — checking action...', 'color:#888');
parseBattleState();
// Debug: show active buffs and channeling status
const activeBuffs = Object.entries(STATE.buffs)
.filter(([k, v]) => v > 0 && k !== 'regen' || v > 0)
.map(([k, v]) => `${k}=${v}`).join(' ');
console.log(`%c[HV] ch=${STATE.channeling} mp=${(STATE.mp*100).toFixed(0)}% oc=${STATE.oc.toFixed(0)} buffs: ${activeBuffs}`, 'color:#888');
const a = getRecommendedAction();
if (a) {
e.preventDefault();
@ -3270,6 +3279,7 @@ function initializeBattle() {
if (STATE.battleInitialized && STATE.page === 'battle') return;
STATE.page = 'battle';
STATE.inBattle = true;
STATE._mysticUsed = false; // Reset Mystic Gem tracking on new battle
parseBattleState();
// Force tier update from cached level (battle page may not have level_readout)

View file

@ -1,7 +1,7 @@
// ==UserScript==
// @name HV Unified
// @namespace hvunified
// @version 0.13.14
// @version 0.13.15
// @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.14';
const VERSION = '0.13.15';
const CFG = {
// — Battle automation
@ -77,6 +77,8 @@ const STATE = {
inBattle: false,
battleInitialized: false,
_mysticUsed: false, // Track if Mystic Gem has been used this battle
maxBuffDur: {}, // Tracks highest duration seen per buff icon
hoverTarget: -1,
@ -967,10 +969,10 @@ function getSmartAction() {
// ── Novice (1-50): survival first ──
function strategyNovice() {
// 0. Mystic Gem for channeling — free OC
// 0. Mystic Gem for channeling — only use once per battle
// (channeling status is tracked from the battle log)
const gem = hasUsableGem('channel');
if (gem && !STATE.channeling)
if (gem && !STATE.channeling && !STATE._mysticUsed)
return { type: 'item', id: gem.id, selfTarget: true };
// 1. Health items — safe to use early
@ -1088,9 +1090,9 @@ function strategyNovice() {
// ── Adept (50-150): building power ──
function strategyAdept() {
// 0. Mystic Gem for channeling — free OC
// 0. Mystic Gem for channeling — only use once per battle
const gem = hasUsableGem('channel');
if (gem && !STATE.channeling)
if (gem && !STATE.channeling && !STATE._mysticUsed)
return { type: 'item', id: gem.id, selfTarget: true };
// 1. Health items — safe to use early
@ -1220,9 +1222,9 @@ function strategyAdept() {
// ── Veteran (150-300) / Master (300+): full rotation ──
function strategyVeteran() {
// 0. Mystic Gem for channeling
// 0. Mystic Gem for channeling — only use once per battle
const gem = hasUsableGem('channel');
if (gem && !STATE.channeling)
if (gem && !STATE.channeling && !STATE._mysticUsed)
return { type: 'item', id: gem.id, selfTarget: true };
// 1. Health items — safe to use early
@ -1410,6 +1412,8 @@ function castTargetSpell(n, i) {
function useItem(id) {
const it = document.getElementById('ikey_' + id);
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();
@ -1516,6 +1520,11 @@ function setupKeybindings() {
if (!isInputFocused() && isBattlePage()) {
console.log('%c[HV] Q pressed — checking action...', 'color:#888');
parseBattleState();
// Debug: show active buffs and channeling status
const activeBuffs = Object.entries(STATE.buffs)
.filter(([k, v]) => v > 0 && k !== 'regen' || v > 0)
.map(([k, v]) => `${k}=${v}`).join(' ');
console.log(`%c[HV] ch=${STATE.channeling} mp=${(STATE.mp*100).toFixed(0)}% oc=${STATE.oc.toFixed(0)} buffs: ${activeBuffs}`, 'color:#888');
const a = getRecommendedAction();
if (a) {
e.preventDefault();
@ -3270,6 +3279,7 @@ function initializeBattle() {
if (STATE.battleInitialized && STATE.page === 'battle') return;
STATE.page = 'battle';
STATE.inBattle = true;
STATE._mysticUsed = false; // Reset Mystic Gem tracking on new battle
parseBattleState();
// Force tier update from cached level (battle page may not have level_readout)

View file

@ -47,6 +47,8 @@ function castTargetSpell(n, i) {
function useItem(id) {
const it = document.getElementById('ikey_' + id);
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();

View file

@ -2,7 +2,7 @@
// CONFIG — default settings
// ═══════════════════════════════════════════════════════════════════════
const VERSION = '0.13.14';
const VERSION = '0.13.15';
const CFG = {
// — Battle automation

View file

@ -1,7 +1,7 @@
// ==UserScript==
// @name HV Unified
// @namespace hvunified
// @version 0.13.14
// @version 0.13.15
// @description HV Unified — battle automation, guidance, and UI enhancements for HentaiVerse
// @author GaboGG + Hermes
// @match *://*.hentaiverse.org/*

View file

@ -49,6 +49,7 @@ function initializeBattle() {
if (STATE.battleInitialized && STATE.page === 'battle') return;
STATE.page = 'battle';
STATE.inBattle = true;
STATE._mysticUsed = false; // Reset Mystic Gem tracking on new battle
parseBattleState();
// Force tier update from cached level (battle page may not have level_readout)

View file

@ -23,6 +23,11 @@ function setupKeybindings() {
if (!isInputFocused() && isBattlePage()) {
console.log('%c[HV] Q pressed — checking action...', 'color:#888');
parseBattleState();
// Debug: show active buffs and channeling status
const activeBuffs = Object.entries(STATE.buffs)
.filter(([k, v]) => v > 0 && k !== 'regen' || v > 0)
.map(([k, v]) => `${k}=${v}`).join(' ');
console.log(`%c[HV] ch=${STATE.channeling} mp=${(STATE.mp*100).toFixed(0)}% oc=${STATE.oc.toFixed(0)} buffs: ${activeBuffs}`, 'color:#888');
const a = getRecommendedAction();
if (a) {
e.preventDefault();

View file

@ -13,6 +13,8 @@ const STATE = {
inBattle: false,
battleInitialized: false,
_mysticUsed: false, // Track if Mystic Gem has been used this battle
maxBuffDur: {}, // Tracks highest duration seen per buff icon
hoverTarget: -1,

View file

@ -308,10 +308,10 @@ function getSmartAction() {
// ── Novice (1-50): survival first ──
function strategyNovice() {
// 0. Mystic Gem for channeling — free OC
// 0. Mystic Gem for channeling — only use once per battle
// (channeling status is tracked from the battle log)
const gem = hasUsableGem('channel');
if (gem && !STATE.channeling)
if (gem && !STATE.channeling && !STATE._mysticUsed)
return { type: 'item', id: gem.id, selfTarget: true };
// 1. Health items — safe to use early
@ -429,9 +429,9 @@ function strategyNovice() {
// ── Adept (50-150): building power ──
function strategyAdept() {
// 0. Mystic Gem for channeling — free OC
// 0. Mystic Gem for channeling — only use once per battle
const gem = hasUsableGem('channel');
if (gem && !STATE.channeling)
if (gem && !STATE.channeling && !STATE._mysticUsed)
return { type: 'item', id: gem.id, selfTarget: true };
// 1. Health items — safe to use early
@ -561,9 +561,9 @@ function strategyAdept() {
// ── Veteran (150-300) / Master (300+): full rotation ──
function strategyVeteran() {
// 0. Mystic Gem for channeling
// 0. Mystic Gem for channeling — only use once per battle
const gem = hasUsableGem('channel');
if (gem && !STATE.channeling)
if (gem && !STATE.channeling && !STATE._mysticUsed)
return { type: 'item', id: gem.id, selfTarget: true };
// 1. Health items — safe to use early