v0.13.17 - Fix fill-level scoring when maxBuffDur hasn't been learned

- When maxBuffDur for a buff equals its current duration (first observation
  in a battle), the emptiness was 0 — making the script think all buffs
  are full and skipping them
- Now: if maxDur <= current dur, assume a minimum of 30 turns as the max
- This ensures buffs like Shadow Veil at 8/8 are treated as 8/30 (73% empty)
  instead of 8/8 (0% empty)
This commit is contained in:
GaboGG 2026-07-26 14:08:50 -04:00
parent 3f865ee6fd
commit e74c97f87a
5 changed files with 15 additions and 9 deletions

View file

@ -1,7 +1,7 @@
// ==UserScript== // ==UserScript==
// @name HV Unified // @name HV Unified
// @namespace hvunified // @namespace hvunified
// @version 0.13.16 // @version 0.13.17
// @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.16'; const VERSION = '0.13.17';
const CFG = { const CFG = {
// — Battle automation // — Battle automation
@ -903,7 +903,9 @@ function findBestChannelingTarget() {
const sp = findSpell(b.spell); const sp = findSpell(b.spell);
const mpCost = sp ? sp.mp : 0; const mpCost = sp ? sp.mp : 0;
// Fill level: how empty is this buff relative to its max observed duration // Fill level: how empty is this buff relative to its max observed duration
const maxDur = STATE.maxBuffDur[b.icon] || (dur + 10); // fallback if unknown let maxDur = STATE.maxBuffDur[b.icon] || 0;
// If max matches current (first observation, no learning yet), assume min 30 turns
if (maxDur <= dur) maxDur = Math.max(dur + 1, 30);
const emptiness = maxDur > 0 ? Math.min(1, (maxDur - dur) / maxDur) : 1; const emptiness = maxDur > 0 ? Math.min(1, (maxDur - dur) / maxDur) : 1;
// Score: emptiness × mp_cost — an empty expensive buff is best // Score: emptiness × mp_cost — an empty expensive buff is best
const score = emptiness * mpCost; const score = emptiness * mpCost;

View file

@ -1,7 +1,7 @@
// ==UserScript== // ==UserScript==
// @name HV Unified // @name HV Unified
// @namespace hvunified // @namespace hvunified
// @version 0.13.16 // @version 0.13.17
// @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.16'; const VERSION = '0.13.17';
const CFG = { const CFG = {
// — Battle automation // — Battle automation
@ -903,7 +903,9 @@ function findBestChannelingTarget() {
const sp = findSpell(b.spell); const sp = findSpell(b.spell);
const mpCost = sp ? sp.mp : 0; const mpCost = sp ? sp.mp : 0;
// Fill level: how empty is this buff relative to its max observed duration // Fill level: how empty is this buff relative to its max observed duration
const maxDur = STATE.maxBuffDur[b.icon] || (dur + 10); // fallback if unknown let maxDur = STATE.maxBuffDur[b.icon] || 0;
// If max matches current (first observation, no learning yet), assume min 30 turns
if (maxDur <= dur) maxDur = Math.max(dur + 1, 30);
const emptiness = maxDur > 0 ? Math.min(1, (maxDur - dur) / maxDur) : 1; const emptiness = maxDur > 0 ? Math.min(1, (maxDur - dur) / maxDur) : 1;
// Score: emptiness × mp_cost — an empty expensive buff is best // Score: emptiness × mp_cost — an empty expensive buff is best
const score = emptiness * mpCost; const score = emptiness * mpCost;

View file

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

View file

@ -1,7 +1,7 @@
// ==UserScript== // ==UserScript==
// @name HV Unified // @name HV Unified
// @namespace hvunified // @namespace hvunified
// @version 0.13.16 // @version 0.13.17
// @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/*

View file

@ -240,7 +240,9 @@ function findBestChannelingTarget() {
const sp = findSpell(b.spell); const sp = findSpell(b.spell);
const mpCost = sp ? sp.mp : 0; const mpCost = sp ? sp.mp : 0;
// Fill level: how empty is this buff relative to its max observed duration // Fill level: how empty is this buff relative to its max observed duration
const maxDur = STATE.maxBuffDur[b.icon] || (dur + 10); // fallback if unknown let maxDur = STATE.maxBuffDur[b.icon] || 0;
// If max matches current (first observation, no learning yet), assume min 30 turns
if (maxDur <= dur) maxDur = Math.max(dur + 1, 30);
const emptiness = maxDur > 0 ? Math.min(1, (maxDur - dur) / maxDur) : 1; const emptiness = maxDur > 0 ? Math.min(1, (maxDur - dur) / maxDur) : 1;
// Score: emptiness × mp_cost — an empty expensive buff is best // Score: emptiness × mp_cost — an empty expensive buff is best
const score = emptiness * mpCost; const score = emptiness * mpCost;