- 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)
- 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
- 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
- The vitals observer check 'battleLog && !battleLog.summary' referenced
an undefined variable, throwing on every observer tick
- Simplified to just check for pane_completion existence
- This crash may have been interfering with state parsing on the observer
- 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
- 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)
- 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
- Added action execution to the vitals MutationObserver so the script
fires on every round tick, not only when hovering a monster
- This fixes buffs expiring and channeling being wasted when the user
isn't actively hovering a monster during auto-battle
- Removed unused STATE.battleRound
- 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
- 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
- New settings-page.js: adds a toolbar above the auto-salvage table
on Character Settings (ss=se) with bulk preset buttons
- Preset 1: Sell <=Fair (value 2) / Salvage <=Average (value 3)
- Preset 2: Sell <=Crude (value 1) / Salvage <=Fair (value 2)
- Disable All: reset both to No Auto-Sell/Salvage
- Shows current state summary (Sell X | Salvage Y)
- Also fixes init.js and build.sh to include new file
- Scrap the old floating panel approach (wrong DOM selectors)
- Now injects a toolbar directly into the equipment list on the Armory page
- Parses equipment from native #equiplist table rows using correct
DOM selectors (hover_equip, select_equip, eqids[] checkboxes)
- Detects current screen (organize/sell/salvage/modify) to show relevant
bulk-select buttons per tab
- Sell tab: select Crude, select <=Fair
- Salvage tab: select salvage-grade, select <=Average
- Organize tab: select equipped (for pinning)
- Plus: Select All, Invert, Clear on every screen
- Grades by quality: Keep (Magnificent+), Keep (Superior),
Salvage (Average-Fair), Sell (Crude)
- Resets properly across page navigations (single-use id guard)
- Removed enhanceEquipShop() — broken bulk sell/salvage from equip shop page
- Rewrote enhanceArmory() with actionable equipment management panel:
- Parses all equipment from the Armory (ss=am) page
- Grades each piece by quality: Keep (Magnificent+), Keep (Superior),
Salvage (Average-Fair), Sell (Crude)
- Bulk Sell / Bulk Salvage buttons that click native game buttons
- Shows slot separators, quality colors, status icons (equipped/locked/stored)
- Sorting: equipped/pinned first, then by quality, then by level
- Summary counts for each action category
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
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
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.
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)
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.
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
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
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.
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.
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.
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.
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.
- 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)