From 4d29215e71c9d6f0246247a0e3e8aabb61801990 Mon Sep 17 00:00:00 2001 From: GaboGG Date: Tue, 4 Aug 2026 16:03:56 -0400 Subject: [PATCH] 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. --- scripts/hv-unified.user.js | 37 +++++++++++++++++++++++++++---------- scripts/latest.user.js | 37 +++++++++++++++++++++++++++---------- src/config.js | 2 +- src/header.user.js | 2 +- src/init.js | 7 ++++--- src/jpx-bridge.js | 26 +++++++++++++++++++++----- 6 files changed, 81 insertions(+), 30 deletions(-) diff --git a/scripts/hv-unified.user.js b/scripts/hv-unified.user.js index a62dff3..eb87e01 100644 --- a/scripts/hv-unified.user.js +++ b/scripts/hv-unified.user.js @@ -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); diff --git a/scripts/latest.user.js b/scripts/latest.user.js index a62dff3..eb87e01 100644 --- a/scripts/latest.user.js +++ b/scripts/latest.user.js @@ -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); diff --git a/src/config.js b/src/config.js index 8755d24..91b8937 100644 --- a/src/config.js +++ b/src/config.js @@ -2,7 +2,7 @@ // CONFIG — default settings // ═══════════════════════════════════════════════════════════════════════ -const VERSION = '0.17.3'; +const VERSION = '0.17.4'; const CFG = { // — Battle strategy advisory diff --git a/src/header.user.js b/src/header.user.js index f00d29e..bebc25b 100644 --- a/src/header.user.js +++ b/src/header.user.js @@ -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/* diff --git a/src/init.js b/src/init.js index 51ab6a2..c49e5e7 100644 --- a/src/init.js +++ b/src/init.js @@ -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); diff --git a/src/jpx-bridge.js b/src/jpx-bridge.js index f0dcb66..b741ebb 100644 --- a/src/jpx-bridge.js +++ b/src/jpx-bridge.js @@ -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); } }