- maxBuffDur now saved to localStorage['hvunified_maxBuffDur'] on every
new high observation and loaded on STATE init
- Survives page reloads: Heartseeker's 180-turn max stays learned
- Removed the unreliable-max fallback hack — no longer needed
- First Q press of a fresh session has accurate fill-level scores
for all buffs that were observed in previous sessions
- When maxBuffDur gap (max - current) is < 10, the cap is unreliable
(first observation mid-battle, not at full duration)
- In that case, skip the fill-level formula entirely
- Only suggest a buff for channeling if it actually needs refresh
(dur <= minTurns from BUFF_PRIORITY)
- Once maxBuffDur learns the true cap (after a fresh cast), the
normal fill-level scoring takes over correctly
- Q-key log now shows: HP, current MP, MP%, SP%, OC, monster count,
buff durations, and per-monster SP bar levels (spBars: 0:120 1:95 ...)
- High-threat detection logs a red '⚠ HIGH THREAT' warning
- Emergency skill usage at low OC logs an orange '⚠ emergency' warning
- Now you can paste me the console log and I'll see exact threat levels,
why skills did/didn't fire, and what the state was before each action
- Fix: items on cooldown (no id attr) were invisible to parser
Spirit Draught/Potion on cooldown couldn't be auto-consumed
- Fix: spiritPotionSP raised from 30% to 60%
- New: parse monster SP bar widths into STATE.monsterSp[]
- New: findDangerousMonster() — targets highest SP bar monster
- New: hasHighThreat() — true if 2+ monsters >85% SP (about to special)
- Weapon skills now fire on high-threat even without full OC
- Basic attacks prefer dangerous monsters in all tiers
- Raised spiritPotionSP threshold from 30% to 60%
- Added Spirit item check at step 2b in Adept and Veteran tiers:
if SP < 55%, consume Spirit Draught/Potion immediately
- Spark of Life costs 50% max SP when triggered. If SP is too low,
Spark fails and you die. Keeping SP above 55% ensures Spark always
has enough fuel to save you from a killing blow on IWBTH
- Changed Veteran suggestion from PFUDOR (20x) to IWBTH (15x)
- PFUDOR is now only suggested at Master tier (L300+)
- Since you're already on IWBTH, the banner won't show anymore
(current == suggested, so no nag)
- 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)
- Added _lastQTime debounce (300ms) to the Q key handler
- Holding Q now fires at most ~3 actions per second instead
of ~20, preventing duplicate actions from stale state reads
- The previous issue: 6 identical Haste casts, then 8 identical
Spark casts, then 12 identical Heartseeker casts — all from
state not yet updated by the game
- castInitialBuffs was sorting by mpCost descending (b.mpCost - a.mpCost)
- So it picked Spark of Life (70) before Haste (41), defeating the
whole purpose of casting cheapest-first to proc channeling
- Changed to ascending sort (a.mpCost - b.mpCost): Haste first, then
Spark, etc.
- Was using vrhd (HP element) instead of vrm (current MP)
- Now reads #vrm textContent for current MP value
- Simply compares mpCost > curMp to skip unaffordable spells
- This prevents the infinite Heartseeker loop when you
don't have enough MP even though the buff is a good target
- Removed SPELL_COSTS hardcoded table
- Base MP costs are now read from the magic pane's onmouseover
tooltip at battle init and cached in STATE._baseMpCosts
- Cache is populated during parseBattleState() and persists
for the entire battle, avoiding channeling's 1-MP display
- Cache resets on new battle, so level-ups or stat changes
are picked up when starting a new arena
- Removed hardcoded SPELL_COSTS table (was brittle)
- Now reads base MP cost directly from the magic pane's
onmouseover tooltip (always shows true base cost, unaffected
by channeling's 1-MP display or Mana Conservation)
- Added affordable check: skip spells where base cost exceeds
current MP, even though channeling would make it cost 1
- The game's native MP check (shown by "You do not have enough")
checks against base cost, not channeling-reduced cost
- Some spells show mp=1 in the DOM due to high Mana Conservation
proficiency, making fill-level scoring produce near-zero values
- Now: if mpCost < 5, look up the base cost from SPELL_COSTS table
- This ensures Heartseeker (141 MP) scores correctly even when the
DOM shows it costing 1 MP
- Magic pane query used '.btsd[onclick]' which excludes
on-cooldown spells (opacity:0.5, no onclick attribute)
- Heartseeker was invisible to hasSpell() during its cooldown
period, which is exactly when channeling procs from it
- Changed to '.btsd' with onmouseover fallback
- Logs the top candidate from findBestChannelingTarget() on every Q press
- Shows: spell, score, dur, maxDur, mpCost, emptiness%
- Next time channeling doesn't fire on a buff, the console will show
the actual computed scores so we can see what's happening
- 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)
- The regex only matched 'X turns remaining' text format
- Buffs like channeling and freshly cast spells use the raw third
parameter of set_infopane_effect('Name','desc',X) without 'turns rem'
- These were defaulting to 50 turns, making all fresh buffs appear
as having 50 turns when they really had 0-5
- Added fallback to parse the last numeric argument of the function call
- Mystic Gem usage now tracked with STATE._mysticUsed flag
Prevents re-triggering the gem check after it's been consumed
(stale STATE.itemsKnown was causing infinite 'item → p' loops)
- Flag resets on new battle initialization
- Added buff durations + channeling status to Q-key console output
Shows: ch=true/false mp=X% oc=X buffs: heartseeker=47 haste=24 ...
- Removed call to undefined finalizeBattleLog() that was spamming
ReferenceErrors and potentially corrupting the MutationObserver
- Raised channeling score threshold from 5 to 30: now only picks a
buff for channeling if it's at least 30% empty on a 100-MP spell
or 60% empty on a 50-MP spell
- Spark at 30/60 turns (50% empty, score 44) now falls to cheap
damage fallback instead of wasting channeling
- Channeling detection was ONLY from battle log text entries
- When log entries scroll off (new round starts), the 'gains the effect'
message is lost and STATE.channeling stays false permanently
- Added effects pane check: if channeling.png is visible, force channeling=true
- This fixes the root cause of channeling being ignored when the log
has scrolled past the proc message
- Also bumped version properly this time
- config.js: 0.13.0 -> 0.13.8
- header.user.js: 0.12.0 -> 0.13.8 (was never bumped past 0.12.0)
- Now the @version tag in the userscript header matches the internal VERSION
Spark of Life consumes 50% of your SP bar when triggered. If you're in
Spirit Stance with low SP, Spark fails to proc. Changes:
- No auto-activation of Spirit Stance (toggle manually with S key)
- Auto-disable if SP drops below 65% to reserve SP for Spark trigger
- Veteran still has optional auto-activation via CFG.autoSpirit but
requires SP > 65% (practically never triggers)
Channeling wiki fix:
- Procs randomly on any mana-costing spell
- Proc chance = spell_cost / (base_mana * 1.2)
- Higher cost = higher chance (Regen/Absorb at 17 MP = ~6%)
- Cannot re-proc while already channeling
- Duration: 5 ticks (15 with Mystic Gem)
- Effect: next spell costs 1 MP, 50% stronger/longer
Kickstart now only refreshes expiring buffs (missing or < 5 turns)
with decent-cost spells (Regen/Absorb). No more cheap attack spells
at 4 MP = 1.4% proc chance — that was a waste of MP.
Defaults reverted to full-conservative values since the 'mp-focused'
win was based on wrong channeling assumptions.
- 23 source files in src/ (build via scripts/build.sh)
- Forum-sourced player knowledge in references/
- DESIGN.md with architecture and corrections
- References to existing scripts (Monsterbation, jpx, HV Utils)