v0.17.4 - jpx chain: wait for next-round monsters before pressing M

The chain was pressing M at round-END (continue button up, no monsters),
so jpx read it as 'advance' and never started auto-play on the next round.
Now: on jpx inactive, poll up to 8s until the next round's monsters spawn,
THEN trigger M. Manual M worked because user pressed it after round load.
This commit is contained in:
GaboGG 2026-08-04 16:03:56 -04:00
parent 6a7d5a435b
commit 4d29215e71
6 changed files with 81 additions and 30 deletions

View file

@ -1,7 +1,7 @@
// ==UserScript==
// @name HV Unified
// @namespace hvunified
// @version 0.17.3
// @version 0.17.4
// @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.3';
const VERSION = '0.17.4';
const CFG = {
// — Battle strategy advisory
@ -1563,6 +1563,7 @@ let jpxChainTimer = null;
let jpxLastActive = null; // last known isActiveBattle from jpx
let jpxChainCooldown = 0; // timestamp guard to avoid double-trigger
let jpxChainRetryCount = 0; // lazy-start retry counter
let jpxChainWaiting = false; // waiting for next-round monsters to spawn
function jpxPresent() {
return !!(document.getElementById('ctrl-widget') ||
@ -1651,15 +1652,30 @@ function jpxChainOnState(detail) {
return;
}
// Re-trigger for the next round — with a small delay so jpx finishes
// its re-init (reDoBattle resets state) before we press M again.
// Guard against double-firing within 2.5s.
// Don't press M yet — jpx would read it as "advance" (round already
// ended, no monsters). Wait until the NEXT round's monsters spawn,
// then trigger auto. Poll up to ~8s.
jpxChainWaiting = true;
const now = Date.now();
if (now - jpxChainCooldown < 2500) return;
jpxChainCooldown = now;
jpxChainRounds++;
setTimeout(jpxTriggerAuto, 600);
console.log(`%c[HV] ⛓ chaining round ${jpxChainRounds}${CFG.chainMaxRounds ? '/' + CFG.chainMaxRounds : ''}`, 'color:#0f0');
let waited = 0;
const timer = setInterval(() => {
waited += 400;
if (jpxMonstersPresent()) {
clearInterval(timer);
jpxChainWaiting = false;
setTimeout(jpxTriggerAuto, 200);
console.log(`%c[HV] ⛓ chaining round ${jpxChainRounds}${CFG.chainMaxRounds ? '/' + CFG.chainMaxRounds : ''}`, 'color:#0f0');
} else if (waited > 8000 || jpxBattleOver()) {
clearInterval(timer);
jpxChainWaiting = false;
if (!jpxBattleOver()) {
console.log('%c[HV] ⛓ timeout waiting for next round', 'color:#f80');
}
}
}, 400);
}
}
@ -3921,9 +3937,10 @@ function initializeBattle() {
jpxChainStart(); // start first auto round
if (!jpxChainTimer) {
jpxChainTimer = setInterval(() => {
// Fallback poll: if jpx reports inactive but we missed the event
// (page reload etc.), check monsters + continue button
if (jpxChainEnabled && jpxLastActive === false && jpxMonstersPresent() && !jpxBattleOver()) {
// Fallback: if jpx went inactive but we missed the event
// (page reload etc.), re-run the state handler to re-arm the
// wait-for-monsters chain (guarded by the cooldown inside).
if (jpxChainEnabled && jpxLastActive === false && !jpxChainWaiting && jpxMonstersPresent() && !jpxBattleOver()) {
jpxChainOnState({ active: false });
}
}, 2000);

View file

@ -1,7 +1,7 @@
// ==UserScript==
// @name HV Unified
// @namespace hvunified
// @version 0.17.3
// @version 0.17.4
// @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.3';
const VERSION = '0.17.4';
const CFG = {
// — Battle strategy advisory
@ -1563,6 +1563,7 @@ let jpxChainTimer = null;
let jpxLastActive = null; // last known isActiveBattle from jpx
let jpxChainCooldown = 0; // timestamp guard to avoid double-trigger
let jpxChainRetryCount = 0; // lazy-start retry counter
let jpxChainWaiting = false; // waiting for next-round monsters to spawn
function jpxPresent() {
return !!(document.getElementById('ctrl-widget') ||
@ -1651,15 +1652,30 @@ function jpxChainOnState(detail) {
return;
}
// Re-trigger for the next round — with a small delay so jpx finishes
// its re-init (reDoBattle resets state) before we press M again.
// Guard against double-firing within 2.5s.
// Don't press M yet — jpx would read it as "advance" (round already
// ended, no monsters). Wait until the NEXT round's monsters spawn,
// then trigger auto. Poll up to ~8s.
jpxChainWaiting = true;
const now = Date.now();
if (now - jpxChainCooldown < 2500) return;
jpxChainCooldown = now;
jpxChainRounds++;
setTimeout(jpxTriggerAuto, 600);
console.log(`%c[HV] ⛓ chaining round ${jpxChainRounds}${CFG.chainMaxRounds ? '/' + CFG.chainMaxRounds : ''}`, 'color:#0f0');
let waited = 0;
const timer = setInterval(() => {
waited += 400;
if (jpxMonstersPresent()) {
clearInterval(timer);
jpxChainWaiting = false;
setTimeout(jpxTriggerAuto, 200);
console.log(`%c[HV] ⛓ chaining round ${jpxChainRounds}${CFG.chainMaxRounds ? '/' + CFG.chainMaxRounds : ''}`, 'color:#0f0');
} else if (waited > 8000 || jpxBattleOver()) {
clearInterval(timer);
jpxChainWaiting = false;
if (!jpxBattleOver()) {
console.log('%c[HV] ⛓ timeout waiting for next round', 'color:#f80');
}
}
}, 400);
}
}
@ -3921,9 +3937,10 @@ function initializeBattle() {
jpxChainStart(); // start first auto round
if (!jpxChainTimer) {
jpxChainTimer = setInterval(() => {
// Fallback poll: if jpx reports inactive but we missed the event
// (page reload etc.), check monsters + continue button
if (jpxChainEnabled && jpxLastActive === false && jpxMonstersPresent() && !jpxBattleOver()) {
// Fallback: if jpx went inactive but we missed the event
// (page reload etc.), re-run the state handler to re-arm the
// wait-for-monsters chain (guarded by the cooldown inside).
if (jpxChainEnabled && jpxLastActive === false && !jpxChainWaiting && jpxMonstersPresent() && !jpxBattleOver()) {
jpxChainOnState({ active: false });
}
}, 2000);

View file

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

View file

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

View file

@ -73,9 +73,10 @@ function initializeBattle() {
jpxChainStart(); // start first auto round
if (!jpxChainTimer) {
jpxChainTimer = setInterval(() => {
// Fallback poll: if jpx reports inactive but we missed the event
// (page reload etc.), check monsters + continue button
if (jpxChainEnabled && jpxLastActive === false && jpxMonstersPresent() && !jpxBattleOver()) {
// Fallback: if jpx went inactive but we missed the event
// (page reload etc.), re-run the state handler to re-arm the
// wait-for-monsters chain (guarded by the cooldown inside).
if (jpxChainEnabled && jpxLastActive === false && !jpxChainWaiting && jpxMonstersPresent() && !jpxBattleOver()) {
jpxChainOnState({ active: false });
}
}, 2000);

View file

@ -16,6 +16,7 @@ let jpxChainTimer = null;
let jpxLastActive = null; // last known isActiveBattle from jpx
let jpxChainCooldown = 0; // timestamp guard to avoid double-trigger
let jpxChainRetryCount = 0; // lazy-start retry counter
let jpxChainWaiting = false; // waiting for next-round monsters to spawn
function jpxPresent() {
return !!(document.getElementById('ctrl-widget') ||
@ -104,15 +105,30 @@ function jpxChainOnState(detail) {
return;
}
// Re-trigger for the next round — with a small delay so jpx finishes
// its re-init (reDoBattle resets state) before we press M again.
// Guard against double-firing within 2.5s.
// Don't press M yet — jpx would read it as "advance" (round already
// ended, no monsters). Wait until the NEXT round's monsters spawn,
// then trigger auto. Poll up to ~8s.
jpxChainWaiting = true;
const now = Date.now();
if (now - jpxChainCooldown < 2500) return;
jpxChainCooldown = now;
jpxChainRounds++;
setTimeout(jpxTriggerAuto, 600);
console.log(`%c[HV] ⛓ chaining round ${jpxChainRounds}${CFG.chainMaxRounds ? '/' + CFG.chainMaxRounds : ''}`, 'color:#0f0');
let waited = 0;
const timer = setInterval(() => {
waited += 400;
if (jpxMonstersPresent()) {
clearInterval(timer);
jpxChainWaiting = false;
setTimeout(jpxTriggerAuto, 200);
console.log(`%c[HV] ⛓ chaining round ${jpxChainRounds}${CFG.chainMaxRounds ? '/' + CFG.chainMaxRounds : ''}`, 'color:#0f0');
} else if (waited > 8000 || jpxBattleOver()) {
clearInterval(timer);
jpxChainWaiting = false;
if (!jpxBattleOver()) {
console.log('%c[HV] ⛓ timeout waiting for next round', 'color:#f80');
}
}
}, 400);
}
}