v0.17.2 - Fix chainState ref, lazy jpx detection, diag visibility

- Fix: chainState/chain used renamed var (jpxChainEnabled vs jpxChainStarted)
- Fix: jpxChainStart lazy-retries (10x/600ms) since jpx creates #ctrl-widget lazily
- Remove duplicate jpxChainStart definition
- HV.diag() now reports scripts.jpx + jpxBridge state for field diagnosis
This commit is contained in:
GaboGG 2026-08-04 15:59:08 -04:00
parent 68b44e022a
commit 30bade19bd
6 changed files with 105 additions and 54 deletions

View file

@ -1,7 +1,7 @@
// ==UserScript==
// @name HV Unified
// @namespace hvunified
// @version 0.17.1
// @version 0.17.2
// @description HV Unified — Bridge & Strategy/Gear Advisor for HentaiVerse (complements Monsterbation & HVUtils)
// @author GaboGG + Hermes
// @match *://*.hentaiverse.org/*
@ -18,7 +18,7 @@
// CONFIG — default settings
// ═══════════════════════════════════════════════════════════════════════
const VERSION = '0.17.1';
const VERSION = '0.17.2';
const CFG = {
// — Battle strategy advisory
@ -1568,6 +1568,29 @@ function jpxPresent() {
document.querySelector('#ctrl-widget, .ctrl-widget'));
}
// Lazy-start: retry until jpx's widget actually appears (it's created lazily
// by jpx after its init, so our first check may be too early).
function jpxChainStart() {
if (!CFG.autoChainJpx) return;
if (jpxChainEnabled) return;
if (!jpxPresent()) {
// Retry a few times over ~6s before giving up
if (!jpxChainRetryCount) jpxChainRetryCount = 0;
if (jpxChainRetryCount < 10) {
jpxChainRetryCount++;
setTimeout(jpxChainStart, 600);
}
return;
}
jpxChainRetryCount = 0;
jpxChainEnabled = true;
jpxChainRounds = 0;
jpxChainCooldown = Date.now();
// Start the first auto round (equivalent to pressing M once)
setTimeout(jpxTriggerAuto, 800);
console.log('%c[HV] ⛓ jpx auto-chain engaged (re-triggering M each round)', 'color:#0f0');
}
// Trigger jpx's auto-battle toggle (equivalent to pressing M).
// jpx listens for keydown on document (capture phase) and maps key 'm' to
// toggleActive via userKeybinds. A synthetic KeyboardEvent works because
@ -1603,20 +1626,6 @@ function jpxBattleOver() {
return !jpxMonstersPresent();
}
// Start the chain (called after battle initializes)
function jpxChainStart() {
if (!CFG.autoChainJpx) return;
if (jpxChainEnabled) return;
if (!jpxPresent()) return;
jpxChainEnabled = true;
jpxChainRounds = 0;
jpxChainCooldown = Date.now();
// Start the first auto round (equivalent to pressing M once)
setTimeout(jpxTriggerAuto, 800);
console.log('%c[HV] ⛓ jpx auto-chain engaged (re-triggering M each round)', 'color:#0f0');
}
// Called when jpx reports a state change via jpx_ctrlWidget_update
function jpxChainOnState(detail) {
if (!CFG.autoChainJpx) return;
@ -3765,8 +3774,8 @@ window.HV = {
stats: () => JSON.parse(localStorage[SP + 'stats'] || '{"battles":0,"credits":0,"drops":0}'),
settings: () => toggleSettings(),
// jpx bridge control
chain: (on) => { CFG.autoChainJpx = !!on; saveConfig(); if (on) { jpxChainStart(); if (!jpxChainTimer) jpxChainTimer = setInterval(jpxChainTick, 1500); } else { jpxChainStop('manual'); } return 'jpx chain: ' + CFG.autoChainJpx; },
chainState: () => ({ active: jpxChainStarted, rounds: jpxChainRounds, jpx: jpxPresent(), maxRounds: CFG.chainMaxRounds, minHP: CFG.chainMinHP }),
chain: (on) => { CFG.autoChainJpx = !!on; saveConfig(); if (on) { jpxBridgeWire(); jpxChainStart(); } else { jpxChainStop('manual'); } return 'jpx chain: ' + CFG.autoChainJpx; },
chainState: () => ({ active: jpxChainEnabled, rounds: jpxChainRounds, jpx: jpxPresent(), maxRounds: CFG.chainMaxRounds, minHP: CFG.chainMinHP }),
advice: () => {
const s = detectFightingStyle();
return { style: s, attrs: getAttrAdvice(s), spells: getSpellAdvice(), difficulty: getDifficultyAdvice() };
@ -3805,8 +3814,16 @@ window.HV = {
document.getElementById('hvut') ||
window.HVUT ||
document.querySelector('[class*="hvut"], [id*="hvut"]')),
jpx: jpxPresent(),
unified: true,
},
// jpx bridge state
jpxBridge: {
chainEnabled: jpxChainEnabled,
chainRounds: jpxChainRounds,
jpxLastActive: jpxLastActive,
retryCount: jpxChainRetryCount || 0,
},
// State availability
state_ready: !!STATE && !!STATE.page,
monsters: STATE.monsters ? STATE.monsters.length : null,

View file

@ -1,7 +1,7 @@
// ==UserScript==
// @name HV Unified
// @namespace hvunified
// @version 0.17.1
// @version 0.17.2
// @description HV Unified — Bridge & Strategy/Gear Advisor for HentaiVerse (complements Monsterbation & HVUtils)
// @author GaboGG + Hermes
// @match *://*.hentaiverse.org/*
@ -18,7 +18,7 @@
// CONFIG — default settings
// ═══════════════════════════════════════════════════════════════════════
const VERSION = '0.17.1';
const VERSION = '0.17.2';
const CFG = {
// — Battle strategy advisory
@ -1568,6 +1568,29 @@ function jpxPresent() {
document.querySelector('#ctrl-widget, .ctrl-widget'));
}
// Lazy-start: retry until jpx's widget actually appears (it's created lazily
// by jpx after its init, so our first check may be too early).
function jpxChainStart() {
if (!CFG.autoChainJpx) return;
if (jpxChainEnabled) return;
if (!jpxPresent()) {
// Retry a few times over ~6s before giving up
if (!jpxChainRetryCount) jpxChainRetryCount = 0;
if (jpxChainRetryCount < 10) {
jpxChainRetryCount++;
setTimeout(jpxChainStart, 600);
}
return;
}
jpxChainRetryCount = 0;
jpxChainEnabled = true;
jpxChainRounds = 0;
jpxChainCooldown = Date.now();
// Start the first auto round (equivalent to pressing M once)
setTimeout(jpxTriggerAuto, 800);
console.log('%c[HV] ⛓ jpx auto-chain engaged (re-triggering M each round)', 'color:#0f0');
}
// Trigger jpx's auto-battle toggle (equivalent to pressing M).
// jpx listens for keydown on document (capture phase) and maps key 'm' to
// toggleActive via userKeybinds. A synthetic KeyboardEvent works because
@ -1603,20 +1626,6 @@ function jpxBattleOver() {
return !jpxMonstersPresent();
}
// Start the chain (called after battle initializes)
function jpxChainStart() {
if (!CFG.autoChainJpx) return;
if (jpxChainEnabled) return;
if (!jpxPresent()) return;
jpxChainEnabled = true;
jpxChainRounds = 0;
jpxChainCooldown = Date.now();
// Start the first auto round (equivalent to pressing M once)
setTimeout(jpxTriggerAuto, 800);
console.log('%c[HV] ⛓ jpx auto-chain engaged (re-triggering M each round)', 'color:#0f0');
}
// Called when jpx reports a state change via jpx_ctrlWidget_update
function jpxChainOnState(detail) {
if (!CFG.autoChainJpx) return;
@ -3765,8 +3774,8 @@ window.HV = {
stats: () => JSON.parse(localStorage[SP + 'stats'] || '{"battles":0,"credits":0,"drops":0}'),
settings: () => toggleSettings(),
// jpx bridge control
chain: (on) => { CFG.autoChainJpx = !!on; saveConfig(); if (on) { jpxChainStart(); if (!jpxChainTimer) jpxChainTimer = setInterval(jpxChainTick, 1500); } else { jpxChainStop('manual'); } return 'jpx chain: ' + CFG.autoChainJpx; },
chainState: () => ({ active: jpxChainStarted, rounds: jpxChainRounds, jpx: jpxPresent(), maxRounds: CFG.chainMaxRounds, minHP: CFG.chainMinHP }),
chain: (on) => { CFG.autoChainJpx = !!on; saveConfig(); if (on) { jpxBridgeWire(); jpxChainStart(); } else { jpxChainStop('manual'); } return 'jpx chain: ' + CFG.autoChainJpx; },
chainState: () => ({ active: jpxChainEnabled, rounds: jpxChainRounds, jpx: jpxPresent(), maxRounds: CFG.chainMaxRounds, minHP: CFG.chainMinHP }),
advice: () => {
const s = detectFightingStyle();
return { style: s, attrs: getAttrAdvice(s), spells: getSpellAdvice(), difficulty: getDifficultyAdvice() };
@ -3805,8 +3814,16 @@ window.HV = {
document.getElementById('hvut') ||
window.HVUT ||
document.querySelector('[class*="hvut"], [id*="hvut"]')),
jpx: jpxPresent(),
unified: true,
},
// jpx bridge state
jpxBridge: {
chainEnabled: jpxChainEnabled,
chainRounds: jpxChainRounds,
jpxLastActive: jpxLastActive,
retryCount: jpxChainRetryCount || 0,
},
// State availability
state_ready: !!STATE && !!STATE.page,
monsters: STATE.monsters ? STATE.monsters.length : null,

View file

@ -2,7 +2,7 @@
// CONFIG — default settings
// ═══════════════════════════════════════════════════════════════════════
const VERSION = '0.17.1';
const VERSION = '0.17.2';
const CFG = {
// — Battle strategy advisory

View file

@ -1,7 +1,7 @@
// ==UserScript==
// @name HV Unified
// @namespace hvunified
// @version 0.17.1
// @version 0.17.2
// @description HV Unified — Bridge & Strategy/Gear Advisor for HentaiVerse (complements Monsterbation & HVUtils)
// @author GaboGG + Hermes
// @match *://*.hentaiverse.org/*

View file

@ -21,6 +21,29 @@ function jpxPresent() {
document.querySelector('#ctrl-widget, .ctrl-widget'));
}
// Lazy-start: retry until jpx's widget actually appears (it's created lazily
// by jpx after its init, so our first check may be too early).
function jpxChainStart() {
if (!CFG.autoChainJpx) return;
if (jpxChainEnabled) return;
if (!jpxPresent()) {
// Retry a few times over ~6s before giving up
if (!jpxChainRetryCount) jpxChainRetryCount = 0;
if (jpxChainRetryCount < 10) {
jpxChainRetryCount++;
setTimeout(jpxChainStart, 600);
}
return;
}
jpxChainRetryCount = 0;
jpxChainEnabled = true;
jpxChainRounds = 0;
jpxChainCooldown = Date.now();
// Start the first auto round (equivalent to pressing M once)
setTimeout(jpxTriggerAuto, 800);
console.log('%c[HV] ⛓ jpx auto-chain engaged (re-triggering M each round)', 'color:#0f0');
}
// Trigger jpx's auto-battle toggle (equivalent to pressing M).
// jpx listens for keydown on document (capture phase) and maps key 'm' to
// toggleActive via userKeybinds. A synthetic KeyboardEvent works because
@ -56,20 +79,6 @@ function jpxBattleOver() {
return !jpxMonstersPresent();
}
// Start the chain (called after battle initializes)
function jpxChainStart() {
if (!CFG.autoChainJpx) return;
if (jpxChainEnabled) return;
if (!jpxPresent()) return;
jpxChainEnabled = true;
jpxChainRounds = 0;
jpxChainCooldown = Date.now();
// Start the first auto round (equivalent to pressing M once)
setTimeout(jpxTriggerAuto, 800);
console.log('%c[HV] ⛓ jpx auto-chain engaged (re-triggering M each round)', 'color:#0f0');
}
// Called when jpx reports a state change via jpx_ctrlWidget_update
function jpxChainOnState(detail) {
if (!CFG.autoChainJpx) return;

View file

@ -12,8 +12,8 @@ window.HV = {
stats: () => JSON.parse(localStorage[SP + 'stats'] || '{"battles":0,"credits":0,"drops":0}'),
settings: () => toggleSettings(),
// jpx bridge control
chain: (on) => { CFG.autoChainJpx = !!on; saveConfig(); if (on) { jpxChainStart(); if (!jpxChainTimer) jpxChainTimer = setInterval(jpxChainTick, 1500); } else { jpxChainStop('manual'); } return 'jpx chain: ' + CFG.autoChainJpx; },
chainState: () => ({ active: jpxChainStarted, rounds: jpxChainRounds, jpx: jpxPresent(), maxRounds: CFG.chainMaxRounds, minHP: CFG.chainMinHP }),
chain: (on) => { CFG.autoChainJpx = !!on; saveConfig(); if (on) { jpxBridgeWire(); jpxChainStart(); } else { jpxChainStop('manual'); } return 'jpx chain: ' + CFG.autoChainJpx; },
chainState: () => ({ active: jpxChainEnabled, rounds: jpxChainRounds, jpx: jpxPresent(), maxRounds: CFG.chainMaxRounds, minHP: CFG.chainMinHP }),
advice: () => {
const s = detectFightingStyle();
return { style: s, attrs: getAttrAdvice(s), spells: getSpellAdvice(), difficulty: getDifficultyAdvice() };
@ -52,8 +52,16 @@ window.HV = {
document.getElementById('hvut') ||
window.HVUT ||
document.querySelector('[class*="hvut"], [id*="hvut"]')),
jpx: jpxPresent(),
unified: true,
},
// jpx bridge state
jpxBridge: {
chainEnabled: jpxChainEnabled,
chainRounds: jpxChainRounds,
jpxLastActive: jpxLastActive,
retryCount: jpxChainRetryCount || 0,
},
// State availability
state_ready: !!STATE && !!STATE.page,
monsters: STATE.monsters ? STATE.monsters.length : null,