v0.13.26 - Stricter Q debounce: reset timer after each successful action
- executeAction now sets STATE._lastActionTime after dispatching - Q handler checks this instead of a local var - When an action executes, the Q debounce resets so the next Q press waits the full 300ms before attempting again - Prevents stale-state queueing when game hasn't processed the previous action yet (was sending 12+ 'skill' commands to the game, though only 1 actually ran)
This commit is contained in:
parent
8c21920d4b
commit
1a4f9a80a3
7 changed files with 51 additions and 33 deletions
|
|
@ -1,7 +1,7 @@
|
||||||
// ==UserScript==
|
// ==UserScript==
|
||||||
// @name HV Unified
|
// @name HV Unified
|
||||||
// @namespace hvunified
|
// @namespace hvunified
|
||||||
// @version 0.13.25
|
// @version 0.13.26
|
||||||
// @description HV Unified — battle automation, guidance, and UI enhancements for HentaiVerse
|
// @description HV Unified — battle automation, guidance, and UI enhancements for HentaiVerse
|
||||||
// @author GaboGG + Hermes
|
// @author GaboGG + Hermes
|
||||||
// @match *://*.hentaiverse.org/*
|
// @match *://*.hentaiverse.org/*
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
// CONFIG — default settings
|
// CONFIG — default settings
|
||||||
// ═══════════════════════════════════════════════════════════════════════
|
// ═══════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
const VERSION = '0.13.25';
|
const VERSION = '0.13.26';
|
||||||
|
|
||||||
const CFG = {
|
const CFG = {
|
||||||
// — Battle automation
|
// — Battle automation
|
||||||
|
|
@ -78,6 +78,7 @@ const STATE = {
|
||||||
battleInitialized: false,
|
battleInitialized: false,
|
||||||
|
|
||||||
_mysticUsed: false, // Track if Mystic Gem has been used this battle
|
_mysticUsed: false, // Track if Mystic Gem has been used this battle
|
||||||
|
_lastActionTime: 0, // Timestamp of last dispatched action (for Q debounce)
|
||||||
|
|
||||||
_baseMpCosts: {}, // Cached base MP costs for buff spells (read from magic pane at battle start)
|
_baseMpCosts: {}, // Cached base MP costs for buff spells (read from magic pane at battle start)
|
||||||
|
|
||||||
|
|
@ -1418,14 +1419,20 @@ function strategyVeteran() {
|
||||||
|
|
||||||
function executeAction(a) {
|
function executeAction(a) {
|
||||||
if (!a) return false;
|
if (!a) return false;
|
||||||
|
let result = false;
|
||||||
switch (a.type) {
|
switch (a.type) {
|
||||||
case 'attack': return attackMonster(a.target);
|
case 'attack': result = attackMonster(a.target); break;
|
||||||
case 'spell': return a.selfTarget ? castSelfSpell(a.name) : castTargetSpell(a.name, a.target);
|
case 'spell': result = a.selfTarget ? castSelfSpell(a.name) : castTargetSpell(a.name, a.target); break;
|
||||||
case 'skill': return useSkill(a.name, a.target);
|
case 'skill': result = useSkill(a.name, a.target); break;
|
||||||
case 'item': return useItem(a.id);
|
case 'item': result = useItem(a.id); break;
|
||||||
case 'toggle_spirit': return toggleSpiritStance();
|
case 'toggle_spirit': result = toggleSpiritStance(); break;
|
||||||
}
|
}
|
||||||
return false;
|
if (result) {
|
||||||
|
// After dispatching an action, stagger the debounce so we don't queue
|
||||||
|
// another action while the game processes this one (typically ~500-1000ms)
|
||||||
|
STATE._lastActionTime = Date.now();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
function attackMonster(i) {
|
function attackMonster(i) {
|
||||||
|
|
@ -1549,7 +1556,6 @@ function setupHover() {
|
||||||
// ═══════════════════════════════════════════════════════════════════════
|
// ═══════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
let keybindingsSetup = false;
|
let keybindingsSetup = false;
|
||||||
let _lastQTime = 0;
|
|
||||||
const Q_DEBOUNCE_MS = 300; // Ignore Q repeats faster than this
|
const Q_DEBOUNCE_MS = 300; // Ignore Q repeats faster than this
|
||||||
|
|
||||||
function setupKeybindings() {
|
function setupKeybindings() {
|
||||||
|
|
@ -1572,8 +1578,8 @@ function setupKeybindings() {
|
||||||
if (!isInputFocused() && isBattlePage()) {
|
if (!isInputFocused() && isBattlePage()) {
|
||||||
// Debounce: ignore key-repeats until game processes the action
|
// Debounce: ignore key-repeats until game processes the action
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
if (now - _lastQTime < Q_DEBOUNCE_MS) return;
|
if (now - (STATE._lastActionTime || 0) < Q_DEBOUNCE_MS) return;
|
||||||
_lastQTime = now;
|
STATE._lastActionTime = now;
|
||||||
|
|
||||||
console.log('%c[HV] Q pressed — checking action...', 'color:#888');
|
console.log('%c[HV] Q pressed — checking action...', 'color:#888');
|
||||||
parseBattleState();
|
parseBattleState();
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// ==UserScript==
|
// ==UserScript==
|
||||||
// @name HV Unified
|
// @name HV Unified
|
||||||
// @namespace hvunified
|
// @namespace hvunified
|
||||||
// @version 0.13.25
|
// @version 0.13.26
|
||||||
// @description HV Unified — battle automation, guidance, and UI enhancements for HentaiVerse
|
// @description HV Unified — battle automation, guidance, and UI enhancements for HentaiVerse
|
||||||
// @author GaboGG + Hermes
|
// @author GaboGG + Hermes
|
||||||
// @match *://*.hentaiverse.org/*
|
// @match *://*.hentaiverse.org/*
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
// CONFIG — default settings
|
// CONFIG — default settings
|
||||||
// ═══════════════════════════════════════════════════════════════════════
|
// ═══════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
const VERSION = '0.13.25';
|
const VERSION = '0.13.26';
|
||||||
|
|
||||||
const CFG = {
|
const CFG = {
|
||||||
// — Battle automation
|
// — Battle automation
|
||||||
|
|
@ -78,6 +78,7 @@ const STATE = {
|
||||||
battleInitialized: false,
|
battleInitialized: false,
|
||||||
|
|
||||||
_mysticUsed: false, // Track if Mystic Gem has been used this battle
|
_mysticUsed: false, // Track if Mystic Gem has been used this battle
|
||||||
|
_lastActionTime: 0, // Timestamp of last dispatched action (for Q debounce)
|
||||||
|
|
||||||
_baseMpCosts: {}, // Cached base MP costs for buff spells (read from magic pane at battle start)
|
_baseMpCosts: {}, // Cached base MP costs for buff spells (read from magic pane at battle start)
|
||||||
|
|
||||||
|
|
@ -1418,14 +1419,20 @@ function strategyVeteran() {
|
||||||
|
|
||||||
function executeAction(a) {
|
function executeAction(a) {
|
||||||
if (!a) return false;
|
if (!a) return false;
|
||||||
|
let result = false;
|
||||||
switch (a.type) {
|
switch (a.type) {
|
||||||
case 'attack': return attackMonster(a.target);
|
case 'attack': result = attackMonster(a.target); break;
|
||||||
case 'spell': return a.selfTarget ? castSelfSpell(a.name) : castTargetSpell(a.name, a.target);
|
case 'spell': result = a.selfTarget ? castSelfSpell(a.name) : castTargetSpell(a.name, a.target); break;
|
||||||
case 'skill': return useSkill(a.name, a.target);
|
case 'skill': result = useSkill(a.name, a.target); break;
|
||||||
case 'item': return useItem(a.id);
|
case 'item': result = useItem(a.id); break;
|
||||||
case 'toggle_spirit': return toggleSpiritStance();
|
case 'toggle_spirit': result = toggleSpiritStance(); break;
|
||||||
}
|
}
|
||||||
return false;
|
if (result) {
|
||||||
|
// After dispatching an action, stagger the debounce so we don't queue
|
||||||
|
// another action while the game processes this one (typically ~500-1000ms)
|
||||||
|
STATE._lastActionTime = Date.now();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
function attackMonster(i) {
|
function attackMonster(i) {
|
||||||
|
|
@ -1549,7 +1556,6 @@ function setupHover() {
|
||||||
// ═══════════════════════════════════════════════════════════════════════
|
// ═══════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
let keybindingsSetup = false;
|
let keybindingsSetup = false;
|
||||||
let _lastQTime = 0;
|
|
||||||
const Q_DEBOUNCE_MS = 300; // Ignore Q repeats faster than this
|
const Q_DEBOUNCE_MS = 300; // Ignore Q repeats faster than this
|
||||||
|
|
||||||
function setupKeybindings() {
|
function setupKeybindings() {
|
||||||
|
|
@ -1572,8 +1578,8 @@ function setupKeybindings() {
|
||||||
if (!isInputFocused() && isBattlePage()) {
|
if (!isInputFocused() && isBattlePage()) {
|
||||||
// Debounce: ignore key-repeats until game processes the action
|
// Debounce: ignore key-repeats until game processes the action
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
if (now - _lastQTime < Q_DEBOUNCE_MS) return;
|
if (now - (STATE._lastActionTime || 0) < Q_DEBOUNCE_MS) return;
|
||||||
_lastQTime = now;
|
STATE._lastActionTime = now;
|
||||||
|
|
||||||
console.log('%c[HV] Q pressed — checking action...', 'color:#888');
|
console.log('%c[HV] Q pressed — checking action...', 'color:#888');
|
||||||
parseBattleState();
|
parseBattleState();
|
||||||
|
|
|
||||||
|
|
@ -4,14 +4,20 @@
|
||||||
|
|
||||||
function executeAction(a) {
|
function executeAction(a) {
|
||||||
if (!a) return false;
|
if (!a) return false;
|
||||||
|
let result = false;
|
||||||
switch (a.type) {
|
switch (a.type) {
|
||||||
case 'attack': return attackMonster(a.target);
|
case 'attack': result = attackMonster(a.target); break;
|
||||||
case 'spell': return a.selfTarget ? castSelfSpell(a.name) : castTargetSpell(a.name, a.target);
|
case 'spell': result = a.selfTarget ? castSelfSpell(a.name) : castTargetSpell(a.name, a.target); break;
|
||||||
case 'skill': return useSkill(a.name, a.target);
|
case 'skill': result = useSkill(a.name, a.target); break;
|
||||||
case 'item': return useItem(a.id);
|
case 'item': result = useItem(a.id); break;
|
||||||
case 'toggle_spirit': return toggleSpiritStance();
|
case 'toggle_spirit': result = toggleSpiritStance(); break;
|
||||||
}
|
}
|
||||||
return false;
|
if (result) {
|
||||||
|
// After dispatching an action, stagger the debounce so we don't queue
|
||||||
|
// another action while the game processes this one (typically ~500-1000ms)
|
||||||
|
STATE._lastActionTime = Date.now();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
function attackMonster(i) {
|
function attackMonster(i) {
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
// CONFIG — default settings
|
// CONFIG — default settings
|
||||||
// ═══════════════════════════════════════════════════════════════════════
|
// ═══════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
const VERSION = '0.13.25';
|
const VERSION = '0.13.26';
|
||||||
|
|
||||||
const CFG = {
|
const CFG = {
|
||||||
// — Battle automation
|
// — Battle automation
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// ==UserScript==
|
// ==UserScript==
|
||||||
// @name HV Unified
|
// @name HV Unified
|
||||||
// @namespace hvunified
|
// @namespace hvunified
|
||||||
// @version 0.13.25
|
// @version 0.13.26
|
||||||
// @description HV Unified — battle automation, guidance, and UI enhancements for HentaiVerse
|
// @description HV Unified — battle automation, guidance, and UI enhancements for HentaiVerse
|
||||||
// @author GaboGG + Hermes
|
// @author GaboGG + Hermes
|
||||||
// @match *://*.hentaiverse.org/*
|
// @match *://*.hentaiverse.org/*
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@
|
||||||
// ═══════════════════════════════════════════════════════════════════════
|
// ═══════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
let keybindingsSetup = false;
|
let keybindingsSetup = false;
|
||||||
let _lastQTime = 0;
|
|
||||||
const Q_DEBOUNCE_MS = 300; // Ignore Q repeats faster than this
|
const Q_DEBOUNCE_MS = 300; // Ignore Q repeats faster than this
|
||||||
|
|
||||||
function setupKeybindings() {
|
function setupKeybindings() {
|
||||||
|
|
@ -26,8 +25,8 @@ function setupKeybindings() {
|
||||||
if (!isInputFocused() && isBattlePage()) {
|
if (!isInputFocused() && isBattlePage()) {
|
||||||
// Debounce: ignore key-repeats until game processes the action
|
// Debounce: ignore key-repeats until game processes the action
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
if (now - _lastQTime < Q_DEBOUNCE_MS) return;
|
if (now - (STATE._lastActionTime || 0) < Q_DEBOUNCE_MS) return;
|
||||||
_lastQTime = now;
|
STATE._lastActionTime = now;
|
||||||
|
|
||||||
console.log('%c[HV] Q pressed — checking action...', 'color:#888');
|
console.log('%c[HV] Q pressed — checking action...', 'color:#888');
|
||||||
parseBattleState();
|
parseBattleState();
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ const STATE = {
|
||||||
battleInitialized: false,
|
battleInitialized: false,
|
||||||
|
|
||||||
_mysticUsed: false, // Track if Mystic Gem has been used this battle
|
_mysticUsed: false, // Track if Mystic Gem has been used this battle
|
||||||
|
_lastActionTime: 0, // Timestamp of last dispatched action (for Q debounce)
|
||||||
|
|
||||||
_baseMpCosts: {}, // Cached base MP costs for buff spells (read from magic pane at battle start)
|
_baseMpCosts: {}, // Cached base MP costs for buff spells (read from magic pane at battle start)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue