Commit graph

45 commits

Author SHA1 Message Date
GaboGG
0a41f9d7c5 v0.14.4 - Sort buff loop by cost (cheapest-first when no channeling)
- Regular buff loop in all 3 tiers now collects candidates then sorts
  by MP cost ascending (cheapest first) when no channeling, or
  descending (most expensive first) when channeling is active
- Previously just iterated BUFF_PRIORITY declaration order (Haste →
  Spark → Heartseeker → Regen → Absorb → Shadow Veil) which wasn't
  cost-ordered
- _reason now shows 'buff' (cheapest first) or 'chBuff' (most expensive)
2026-07-27 14:45:00 -04:00
GaboGG
48e5103655 v0.14.3 - Add reason tags to castInitialBuffs (init/initCh)
- castInitialBuffs now tags actions with _reason='init' (cheapest-first
  sort, no channeling) or _reason='initCh' (most-expensive-first,
  channeling already charged)
- Makes the log clearly show which buff path fired
2026-07-27 14:43:40 -04:00
GaboGG
205f1cd631 v0.14.2 - Double-check cooldown via img opacity:0.5 in hasReadySpell
- Some quickbar/deprecated elements retain onclick even when on cooldown
  if the cooldown indicator is only visual (img opacity:0.5) not structural
- Now also checks img.btqi / img style.opacity !== '0.5'
- This catches cases where the parent div has onclick but the inner image
  is visually dimmed (game cooldown)
2026-07-27 14:08:48 -04:00
GaboGG
7ae6e4855f v0.14.1 - Fix cooldown checks: spells on cooldown no longer selected
- Added hasReadySpell() that checks DOM for onclick attribute (off cooldown)
- Buff loop (all tiers) now uses hasReadySpell instead of hasSpell
- Cure/Full-Cure now checks cooldown before casting
- Channeling target selection checks cooldown
- castInitialBuffs checks cooldown
- Imperil/Weaken debuffs check cooldown
- hasSpell() kept for knowledge checks (do we know this spell at all)
- Prevents silent failures where on-cooldown spells are selected but
  can't actually be cast (btsd[onclick] fails to find them)
2026-07-27 14:05:25 -04:00
GaboGG
c9ad92e28d v0.14.0 - Major strategy overhaul from agy research report
BUG FIXES (P0):
- Spark of Life SP safety: consume spirit items when SP < 55% BEFORE
  buffs, Cure, or any other action (both Adept and Veteran)
- Spirit Stance safety auto-disable: auto-toggle OFF if SP drops below 60%
- useItem() fallback: auto_ slot IDs now lookup by onmouseover attribute
  (on-cooldown items without DOM id were silently failing)
- setTimeout targeting race: removed 50ms delay in castTargetSpell/useSkill
  (was causing targeting failures with fast Q-repeat)
- maxBuffDur pre-seeded with L150+ baseline values (haste=80, heartseeker=120,
  sparklife=50, etc.) so fill-level scoring works from first turn

STRATEGY (P1):
- Channeling waste eliminated for 2H physical builds: no more Fiery Blast
  fallback — let channeling expire on a basic attack (Domino Strike +
  Penetrated Armor do more damage)
- Weapon skill thresholds lowered from 5+ to 3+ enemies (IWBTH survivability)
- Expanded debuffs: Imperil on any 2+ mobs, Weaken on high-threat targets
  (not just bosses anymore)
- Domino Strike optimal targeting: findOptimalDominoTarget() picks center
  monsters to max splash coverage (up to 2 targets each side)

NEW FEATURES:
- Domino Strike splash positioning algorithm added to all tiers
- Full _reason tags on every action return for decision tracing
- agy research report saved at hermes_report.md (554 lines)
2026-07-27 14:01:05 -04:00
GaboGG
467e844aae v0.13.34 - Explicit action names in log + _reason tags per decision
- Log now shows spell names/skill names/item names explicitly:
  '[HV] ▶ spell → Haste (self)' instead of '▶ spell → monster 0'
  '[HV] ▶ spell → Fiery Blast → monster 1 (ch fallback)'
  '[HV] ▶ item → Spirit Draught (self) (sp55)'
- Added _reason tag to every action return: 'hp60', 'sp55', 'buff',
  'ch score 81', 'threat', 'oc', 'boss', 'stun', 'mana', 'gem',
  'mystic', 'fallback', 'dmg'
- Now you can see exactly WHY each action was chosen on every turn
2026-07-27 13:35:45 -04:00
GaboGG
949aab915f v0.13.33 - Persist maxBuffDur to localStorage (no more warm-up)
- 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
2026-07-27 13:26:51 -04:00
GaboGG
b8e2e945ec v0.13.32 - Fix channeling waste when maxBuffDur hasn't learned true cap
- 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
2026-07-27 13:22:15 -04:00
GaboGG
533fad252a v0.13.31 - Enhanced battle logging for deep diagnostics
- 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
2026-07-27 13:13:59 -04:00
GaboGG
b5a5c3df54 v0.13.30 - Fix spirit item detection, add threat-based targeting
- 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
2026-07-27 13:07:39 -04:00
GaboGG
b6d78067ce v0.13.29 - Aggressive SP management for Spark of Life survival
- 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
2026-07-27 11:08:16 -04:00
GaboGG
ad2ff4b482 v0.13.27 - Remove auto Spirit Stance toggle from all tiers
- Removed toggle_spirit calls from Novice, Adept, and Veteran
  strategy functions (was auto-toggling stance at OC 60+)
- Spirit Stance is now fully manual (press spirit button yourself)
- Spirit Gem auto-consumption remains (fills SP when dropped)
- Kept the config guards (CFG.autoSpirit) for future opt-in
2026-07-26 15:26:51 -04:00
GaboGG
86968f531c v0.13.24 - Fix castInitialBuffs sorting (was most-expensive-first, not cheapest-first)
- 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.
2026-07-26 15:16:07 -04:00
GaboGG
0aa268b3f9 v0.13.23 - Fix affordable MP check using actual current MP from DOM
- 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
2026-07-26 15:09:38 -04:00
GaboGG
31799a7261 v0.13.22 - Cache base MP costs at battle start (dynamic, not hardcoded)
- 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
2026-07-26 15:08:23 -04:00
GaboGG
80fc77600a v0.13.21 - Dynamic base cost read + affordable MP check for channeling
- 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
2026-07-26 15:05:20 -04:00
GaboGG
e20fe0495c v0.13.20 - Add SPELL_COSTS fallback for Mana Conservation-reduced MP costs
- 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
2026-07-26 15:01:49 -04:00
GaboGG
49c7ce9cab v0.13.18 - Add channeling score debug logging
- 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
2026-07-26 14:12:48 -04:00
GaboGG
e74c97f87a 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)
2026-07-26 14:08:50 -04:00
GaboGG
25ea8b4543 v0.13.15 - Fix stale Mystic Gem state, add debug logging to Q press
- 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 ...
2026-07-26 14:01:48 -04:00
GaboGG
23f00450be v0.13.14 - Fix observer crash, raise channeling threshold to 30
- 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
2026-07-26 13:53:01 -04:00
GaboGG
c0c626c0bf v0.13.12 - Fill-level scoring for channeling consumption
- Score = emptiness_ratio × mp_cost
- emptiness_ratio = (max_dur - current) / max_dur
- max_dur is tracked dynamically per buff in STATE.maxBuffDur
  (learned from the highest duration seen per icon)
- Freshly cast buffs score ~0 regardless of MP cost
- A 100-MP buff at 1/90 turns scores 98.9
- A 40-MP buff at 25/50 turns scores 20.0
- A 140-MP buff at 71/80 turns scores 15.75
- Removed the Heartseeker >50 skip (no longer needed - fill level handles it)
2026-07-26 13:44:17 -04:00
GaboGG
37b502aa65 v0.13.11 - Score threshold for channeling buff consumption
- Reverted the >10 turns cutoff (wrong approach)
- Instead, added a minimum score threshold of 5 to the best candidate
- At score >= 5: a 50-MP spell with <9 turns, a 140-MP spell with <27 turns
- Below score 5: falls through to cheap damage spell fallback
- This prevents recasting fresh buffs while still considering all spells
2026-07-26 13:39:32 -04:00
GaboGG
e13b4c5206 v0.13.10 - Only consume channeling on buffs that need refresh (<10 turns)
- findBestChannelingTarget: skip buffs with >10 remaining turns
- When all buffs are fresh, falls through to cheap damage spell
  instead of wasting channeling on a nearly-full duration buff
- This prevents recasting Spark of Life at 15 turns just because
  its score happens to be highest among all fresh buffs
2026-07-26 13:38:31 -04:00
GaboGG
c8fc09263a v0.13.8 - Lower MP thresholds and fix console error
- Reduced buff loop MP threshold from 20% to 10% in all three tiers
  (prevents skipping buffs when MP is moderately low)
- Also fixed the 'battleLog is not defined' ReferenceError from the
  vitals observer (removed in previous commit but was still referenced)
2026-07-26 13:29:54 -04:00
GaboGG
bbf7c95fb9 v0.13.7 - Channeling prioritized before buff priority loop
- Moved findBestChannelingTarget() BEFORE the BUFF_PRIORITY loop
- When Q is pressed and channeling is active, the script now picks the most
  expensive buff for the channeling charge instead of wasting it on the
  first buff in priority order (Haste at 32 MP instead of Heartseeker at 175)
- Reverted the vitals auto-execute (Q-key workflow, not auto-battle)
- All three tiers: channeling check fires after castInitialBuffs but
  before the regular shouldBuff loop
2026-07-26 13:27:24 -04:00
GaboGG
ee9bba90fc v0.13.5 - Fix channeling waste when all buffs are fresh
- findBestChannelingTarget() fallback: expanded damage spell list to
  include T2 spells (Inferno, Blizzard, etc.) and a last-resort loop
  that fires ANY offensive spell the player knows
- Prevents channeling from expiring when all buffs have high remaining
  durations and the player has no T1 elemental spells
2026-07-26 13:18:56 -04:00
GaboGG
5f61a5c4fc v0.13.4 - Round 1 buff-up: cheapest first, channeling on most expensive
- Added castInitialBuffs() that fires when 60%+ buffs are down (first round)
  - Without channeling: casts cheapest buff first (more casts = more procs)
  - With channeling: casts most expensive remaining buff (best value for free cast)
- Added isFirstRoundBuffs() helper: checks if most buffs are missing
- Injected into all three tiers (Novice, Adept, Veteran) before the
  regular BUFF_PRIORITY loop
- Added STATE.battleRound counter for future round tracking
2026-07-26 13:13:48 -04:00
GaboGG
2107527c72 v0.13.0 - Heartseeker buff, channeling efficiency, 100% buff uptime
- Added Heartseeker to BUFF_PRIORITY (+25% phys dmg, +10% crit self-buff)
- Channeling consumption: mp_cost/(dur+1) scoring, no arbitrary cutoff
- KickstartChanneling: prefers highest-MP-cost buffs for better proc chance
- Raised minTurns on all buffs for 100% uptime (Haste/Protect/Regen/Absorb/Shadow 2->4, Spark 1->3)
- Updated knowledge-base spellUnlocks and milestones
- Removed <10 turn hard cutoff from channeling target selection
2026-07-26 11:25:40 -04:00
GaboGG
3a041f41da Fix boss detection: remove SP bar check, only use gold border
At Lv100+ / Hell difficulty, all normal monsters also have SP bars
(the red nbarred.png). This made the script think every monster was
a boss, triggering debuffs (Imperil/Weaken) on every fight and
draining MP.

Changes:
- isRareMonster() no longer checks for nbarred.png
- Only gold border (BD7400/E6CCA3) indicates a real boss
- Debuffs now only fire on gold-border bosses, not on ≤2 monsters
2026-07-23 17:53:47 -04:00
GaboGG
3777de77fa Spirit gems: P-slot gem used immediately, draughts/potions at threshold
Spirit item logic split by source:
- P-slot gem (10007): used immediately on every turn, free to refill
  with crystals, keeps SP permanently high for Spark + Stance
- Spirit Draughts/Potions in item slots: only used when SP < threshold (30%)
  to conserve consumable credits
2026-07-22 07:13:55 -04:00
GaboGG
a90dd47ec0 Spirit gems: use whenever available, no SP threshold
Spirit gems now fire whenever one is slotted regardless of SP level.
This keeps SP constantly topped up for:
- Spark of Life (costs 50% SP to trigger)
- Manual Spirit Stance (costs 1 SP + 10% OC per round)
Removed the old SP < 30% threshold check.
2026-07-22 07:11:40 -04:00
GaboGG
cdd0fe5984 Spirit Stance: manual only, auto-disable at 65% SP (Spark needs 50%)
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)
2026-07-22 06:50:51 -04:00
GaboGG
0bbdb66b14 Fix Spark of Life icon key: sparklife.png, not fallenshield
The active buff icon is /y/e/sparklife.png -> stored as 'sparklife'.
The 'fallenshield.png' check is for detecting when it has been consumed
(triggered), not for the active buff. This was causing the script to
think Spark of Life was never active, recasting it every turn.
2026-07-22 06:18:39 -04:00
GaboGG
b69eabd1eb Fix: Spark of Life icon key, mana items after buffs
1. Spark of Life icon is 'fallenshield.png' not 'spark_of_life' -
   fixed BUFF_PRIORITY key so buff detection works correctly
2. Mana/spirit items moved to AFTER buffs in all 3 tiers -
   was drinking mana potions on turn 2 before buffs were cast
3. Priority order: Health items -> Cure -> Buffs -> Mana items ->
   Skills -> Spirit Stance -> Damage spells -> Channeling -> Attack
2026-07-22 06:13:54 -04:00
GaboGG
d9fc1e547c Fix channeling: consume charge on useful spell, don't let it wear off
Channeling works: proc on mana-costing spell -> next spell costs 1 MP
and is 50% stronger/longer. The charge lasts 5 ticks (15 with Mystic
Gem) then wears off and is wasted.

Previously the script would basic attack with channeling charged,
wasting the bonus. Now:
- If channeling is active and nothing more urgent, still cast a buff
  (refresh anything under 10 turns) or a cheap damage spell
- Never basic attack while channeling is charged
- Novice/Adept/Veteran all have this protection
2026-07-21 14:45:23 -04:00
GaboGG
7093409580 Fix channeling: wiki-correct formula, Regen/Absorb as best 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.
2026-07-21 14:35:54 -04:00
GaboGG
0d57fead61 Shadow Veil fix: use correct icon key 'shadowveil', minTurns=3
Shadow Veil buff icon is stored as 'shadowveil' (from img src filename,
no underscore). Priority list now uses the correct key. minTurns=3
prevents recasting while still active. Also moved to last priority
position — only cast when MP is plentiful.
2026-07-21 14:07:17 -04:00
GaboGG
0ee31b7266 Benchmark v2 with real Lv90 stats, Shadow Veil removed, full-conservative defaults
Shadow Veil removed from BUFF_PRIORITY - was draining MP aggressively.
Player stats updated to match real character (effective values with gear).

Benchmark results (Lv90, Nightmare, 5 runs each):
1. full-conservative: 146 rounds, 4485 dmg/rd — WINNER
2. conservative-items: 143 rounds, 4468 dmg/rd
3. spirit-40: 136 rounds, 4457 dmg/rd

New defaults (full-conservative):
- cureHP: 0.45, cureItemHP: 0.50 (save items use cure)
- manaGemMP: 0.45, manaPotionMP: 0.25
- spiritPotionSP: 0.30, spiritStanceOC: 80
- skillOC: 75
2026-07-21 14:00:44 -04:00
GaboGG
e8c3f465d2 Spirit Stance: 40% SP minimum to toggle on (from 20%)
At 20% SP (~10 SP = 10 rounds of sustain), the player would toggle on
during a Grindfest round, consume SP quickly, toggle off when SP < 3%,
then 3 turns later the cooldown expires and OC is back to 60+ -> toggle
on again. Raising the threshold to 40% means ~20 rounds of sustained
Spirit Stance, making the engagement meaningful and rarely cycling.
2026-07-20 20:19:58 -04:00
GaboGG
a415b932d3 Spirit Stance: require 20% SP to toggle on, prevent flip-flop
Spirit Stance costs 1 SP + 10% OC per round (wiki). Previously toggled
on at 10% SP which meant ~5 rounds of sustain, often running out
immediately. Now requires 20% SP (~10 rounds sustain) to toggle on.
3-turn cooldown still prevents rapid flip-flopping between states.
2026-07-20 20:18:02 -04:00
GaboGG
e415a7660f Fix spirit stance flip-flop: 3-turn cooldown on toggle
Spirit stance was toggling on/off every turn because conditions (OC >= 60,
SP > 10%) would be met immediately after toggling off. Added a 3-turn
cooldown via STATE._spiritCooldown that blocks re-toggling until it
expires. Prevents wasted turns on spirit stance spam.
2026-07-20 20:00:19 -04:00
GaboGG
eb5f99dbfb Fix: skills before spirit stance, Rending Blow priority reorder
Priority order now:
1. Items
2. Cure
3. Buffs
4. Debuffs
5. Weapon skills (BEFORE spirit stance - fixes Rending Blow never firing)
   - Rending Blow first (AoE clear)
   - Shatter Strike second (needs Penetrated Armor)
   - Great Cleave last (single target, bosses only)
6. Spirit Stance (only if OC didn't get spent on skills)
7. Damage spells
8. Channeling kickstart
9. Basic attack
2026-07-20 19:58:27 -04:00
GaboGG
39ad28007c Sim benchmark: full-conservative wins, configurable skill OC
Benchmark results (1000 rounds, Lv64 Nightmare, infinite items):
1. full-conservative: 225.6 rounds, 3141 dmg/rd — BEST
2. conservative-items: 223.6 rounds, 3128 dmg/rd
3. attack-spells: 213.0 rounds, 2974 dmg/rd

Changes:
- Grindfest scaling: monsters +1 per 100 rounds, damage +0.4%/round
- skillOC config option (default: 70) for weapon skill OC threshold
- Spirit gem threshold bumped to 45%
- Simulator uses real monster count/damage scaling from wiki
- Benchmark results saved to simulator/results/benchmark.json
2026-07-20 19:53:03 -04:00
GaboGG
402db6bb2f Initial commit: HV Unified v0.11.0 structure
- 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)
2026-07-20 19:30:26 -04:00