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)
This commit is contained in:
parent
c9ad92e28d
commit
7ae6e4855f
5 changed files with 66 additions and 45 deletions
|
|
@ -1,7 +1,7 @@
|
|||
// ==UserScript==
|
||||
// @name HV Unified
|
||||
// @namespace hvunified
|
||||
// @version 0.14.0
|
||||
// @version 0.14.1
|
||||
// @description HV Unified — battle automation, guidance, and UI enhancements for HentaiVerse
|
||||
// @author GaboGG + Hermes
|
||||
// @match *://*.hentaiverse.org/*
|
||||
|
|
@ -17,7 +17,7 @@
|
|||
// CONFIG — default settings
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
|
||||
const VERSION = '0.14.0';
|
||||
const VERSION = '0.14.1';
|
||||
|
||||
const CFG = {
|
||||
// — Battle automation
|
||||
|
|
@ -733,6 +733,13 @@ function hasSpell(name) {
|
|||
return STATE.spellsKnown.some(s => s.n === name);
|
||||
}
|
||||
|
||||
// Check if a spell is not on cooldown (has onclick in the DOM)
|
||||
function hasReadySpell(name) {
|
||||
const el = $$('.btsd, .btqs').find(e =>
|
||||
(e.getAttribute('onmouseover') || '').includes(`set_infopane_spell('${name}'`));
|
||||
return el ? el.hasAttribute('onclick') : false;
|
||||
}
|
||||
|
||||
function findSpell(name) {
|
||||
return STATE.spellsKnown.find(s => s.n === name);
|
||||
}
|
||||
|
|
@ -939,7 +946,7 @@ function castInitialBuffs() {
|
|||
// Collect all missing/expiring buffs with their MP cost
|
||||
const candidates = [];
|
||||
for (const b of BUFF_PRIORITY) {
|
||||
if (!hasSpell(b.spell)) continue;
|
||||
if (!hasReadySpell(b.spell)) continue;
|
||||
const dur = buffDuration(b.icon);
|
||||
if (dur === 0 || dur < 3) {
|
||||
if (b.icon === 'regen' && STATE.hp >= CFG.cureRegenHP) continue;
|
||||
|
|
@ -1009,7 +1016,7 @@ function findBestChannelingTarget() {
|
|||
|
||||
const candidates = [];
|
||||
for (const b of BUFF_PRIORITY) {
|
||||
if (!hasSpell(b.spell)) continue;
|
||||
if (!hasReadySpell(b.spell)) continue;
|
||||
const dur = buffDuration(b.icon);
|
||||
if (b.icon === 'regen' && STATE.hp >= CFG.cureRegenHP) continue;
|
||||
const sp = findSpell(b.spell);
|
||||
|
|
@ -1126,7 +1133,7 @@ function strategyNovice() {
|
|||
|
||||
// 2. Cure
|
||||
if (STATE.hp < CFG.cureHP) {
|
||||
const c = hasSpell('Full-Cure') ? 'Full-Cure' : hasSpell('Cure') ? 'Cure' : null;
|
||||
const c = hasReadySpell('Full-Cure') ? 'Full-Cure' : hasReadySpell('Cure') ? 'Cure' : null;
|
||||
if (c) return { type: 'spell', name: c, selfTarget: true };
|
||||
}
|
||||
|
||||
|
|
@ -1142,7 +1149,7 @@ function strategyNovice() {
|
|||
if (ch) return ch;
|
||||
}
|
||||
for (const b of BUFF_PRIORITY) {
|
||||
if (shouldBuff(b.icon, b.minTurns) && STATE.mp > 0.10 && hasSpell(b.spell)) {
|
||||
if (shouldBuff(b.icon, b.minTurns) && STATE.mp > 0.10 && hasReadySpell(b.spell)) {
|
||||
if (b.icon === 'regen' && STATE.hp >= CFG.cureRegenHP) continue;
|
||||
return { type: 'spell', name: b.spell, selfTarget: true };
|
||||
}
|
||||
|
|
@ -1255,7 +1262,7 @@ function strategyAdept() {
|
|||
|
||||
// 2. Cure
|
||||
if (STATE.hp < CFG.cureHP) {
|
||||
const c = hasSpell('Full-Cure') ? 'Full-Cure' : hasSpell('Cure') ? 'Cure' : null;
|
||||
const c = hasReadySpell('Full-Cure') ? 'Full-Cure' : hasReadySpell('Cure') ? 'Cure' : null;
|
||||
if (c) return { type: 'spell', name: c, selfTarget: true };
|
||||
}
|
||||
|
||||
|
|
@ -1283,7 +1290,7 @@ function strategyAdept() {
|
|||
if (ch) return ch;
|
||||
}
|
||||
for (const b of BUFF_PRIORITY) {
|
||||
if (shouldBuff(b.icon, b.minTurns) && STATE.mp > 0.10 && hasSpell(b.spell)) {
|
||||
if (shouldBuff(b.icon, b.minTurns) && STATE.mp > 0.10 && hasReadySpell(b.spell)) {
|
||||
if (b.icon === 'regen' && STATE.hp >= CFG.cureRegenHP) continue;
|
||||
return { type: 'spell', name: b.spell, selfTarget: true };
|
||||
}
|
||||
|
|
@ -1311,12 +1318,12 @@ function strategyAdept() {
|
|||
|
||||
// 4. Debuffs — Imperil on 2+ mobs or bosses; Weaken on high-threat
|
||||
if (CFG.autoDebuff && STATE.mp > 0.20) {
|
||||
if (hasSpell('Imperil') && (anyRareMonster() || STATE.monsters.length >= 2)) {
|
||||
if (hasReadySpell('Imperil') && (anyRareMonster() || STATE.monsters.length >= 2)) {
|
||||
const b = findStrongestMonster();
|
||||
if (b >= 0 && !checkMonsterDebuff(b, 'imperil'))
|
||||
return { type: 'spell', name: 'Imperil', target: b, _reason: 'imperil' };
|
||||
}
|
||||
if (hasSpell('Weaken') && (anyRareMonster() || hasHighThreat(80) || STATE.monsters.length >= 3)) {
|
||||
if (hasReadySpell('Weaken') && (anyRareMonster() || hasHighThreat(80) || STATE.monsters.length >= 3)) {
|
||||
const b = findDangerousMonster();
|
||||
if (b >= 0 && !checkMonsterDebuff(b, 'weaken'))
|
||||
return { type: 'spell', name: 'Weaken', target: b, _reason: 'weaken' };
|
||||
|
|
@ -1407,7 +1414,7 @@ function strategyVeteran() {
|
|||
|
||||
// 2. Cure
|
||||
if (STATE.hp < CFG.cureHP) {
|
||||
const c = hasSpell('Full-Cure') ? 'Full-Cure' : hasSpell('Cure') ? 'Cure' : null;
|
||||
const c = hasReadySpell('Full-Cure') ? 'Full-Cure' : hasReadySpell('Cure') ? 'Cure' : null;
|
||||
if (c) return { type: 'spell', name: c, selfTarget: true, _reason: `hp${(STATE.hp*100).toFixed(0)}` };
|
||||
}
|
||||
|
||||
|
|
@ -1434,14 +1441,14 @@ function strategyVeteran() {
|
|||
if (ch) return ch;
|
||||
}
|
||||
for (const b of BUFF_PRIORITY) {
|
||||
if (shouldBuff(b.icon, b.minTurns) && STATE.mp > 0.10 && hasSpell(b.spell)) {
|
||||
if (shouldBuff(b.icon, b.minTurns) && STATE.mp > 0.10 && hasReadySpell(b.spell)) {
|
||||
if (b.icon === 'regen' && STATE.hp >= CFG.cureRegenHP) continue;
|
||||
return { type: 'spell', name: b.spell, selfTarget: true, _reason: 'buff' };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Mana/spirit items — AFTER buffs (Veteran)
|
||||
// 4. Mana/spirit items — AFTER buffs (Novice)
|
||||
if (STATE.mp < CFG.manaGemMP) {
|
||||
const gem = hasUsableGem('mana');
|
||||
if (gem) return { type: 'item', id: gem.id, selfTarget: true };
|
||||
|
|
@ -1466,13 +1473,13 @@ function strategyVeteran() {
|
|||
if (CFG.autoDebuff && STATE.mp > 0.20) {
|
||||
// Imperil: reduce physical mitigation by 25-50% (stacks with Penetrated Armor)
|
||||
// Use on any tanky enemy when facing 2+ monsters or a rare
|
||||
if (hasSpell('Imperil') && (anyRareMonster() || STATE.monsters.length >= 2)) {
|
||||
if (hasReadySpell('Imperil') && (anyRareMonster() || STATE.monsters.length >= 2)) {
|
||||
const b = findStrongestMonster();
|
||||
if (b >= 0 && !checkMonsterDebuff(b, 'imperil'))
|
||||
return { type: 'spell', name: 'Imperil', target: b, _reason: 'imperil' };
|
||||
}
|
||||
// Weaken: reduce incoming damage by 50% — use on high-threat or 3+ mobs
|
||||
if (hasSpell('Weaken') && (anyRareMonster() || hasHighThreat(80) || STATE.monsters.length >= 3)) {
|
||||
if (hasReadySpell('Weaken') && (anyRareMonster() || hasHighThreat(80) || STATE.monsters.length >= 3)) {
|
||||
const b = findDangerousMonster();
|
||||
if (b >= 0 && !checkMonsterDebuff(b, 'weaken'))
|
||||
return { type: 'spell', name: 'Weaken', target: b, _reason: 'weaken' };
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// ==UserScript==
|
||||
// @name HV Unified
|
||||
// @namespace hvunified
|
||||
// @version 0.14.0
|
||||
// @version 0.14.1
|
||||
// @description HV Unified — battle automation, guidance, and UI enhancements for HentaiVerse
|
||||
// @author GaboGG + Hermes
|
||||
// @match *://*.hentaiverse.org/*
|
||||
|
|
@ -17,7 +17,7 @@
|
|||
// CONFIG — default settings
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
|
||||
const VERSION = '0.14.0';
|
||||
const VERSION = '0.14.1';
|
||||
|
||||
const CFG = {
|
||||
// — Battle automation
|
||||
|
|
@ -733,6 +733,13 @@ function hasSpell(name) {
|
|||
return STATE.spellsKnown.some(s => s.n === name);
|
||||
}
|
||||
|
||||
// Check if a spell is not on cooldown (has onclick in the DOM)
|
||||
function hasReadySpell(name) {
|
||||
const el = $$('.btsd, .btqs').find(e =>
|
||||
(e.getAttribute('onmouseover') || '').includes(`set_infopane_spell('${name}'`));
|
||||
return el ? el.hasAttribute('onclick') : false;
|
||||
}
|
||||
|
||||
function findSpell(name) {
|
||||
return STATE.spellsKnown.find(s => s.n === name);
|
||||
}
|
||||
|
|
@ -939,7 +946,7 @@ function castInitialBuffs() {
|
|||
// Collect all missing/expiring buffs with their MP cost
|
||||
const candidates = [];
|
||||
for (const b of BUFF_PRIORITY) {
|
||||
if (!hasSpell(b.spell)) continue;
|
||||
if (!hasReadySpell(b.spell)) continue;
|
||||
const dur = buffDuration(b.icon);
|
||||
if (dur === 0 || dur < 3) {
|
||||
if (b.icon === 'regen' && STATE.hp >= CFG.cureRegenHP) continue;
|
||||
|
|
@ -1009,7 +1016,7 @@ function findBestChannelingTarget() {
|
|||
|
||||
const candidates = [];
|
||||
for (const b of BUFF_PRIORITY) {
|
||||
if (!hasSpell(b.spell)) continue;
|
||||
if (!hasReadySpell(b.spell)) continue;
|
||||
const dur = buffDuration(b.icon);
|
||||
if (b.icon === 'regen' && STATE.hp >= CFG.cureRegenHP) continue;
|
||||
const sp = findSpell(b.spell);
|
||||
|
|
@ -1126,7 +1133,7 @@ function strategyNovice() {
|
|||
|
||||
// 2. Cure
|
||||
if (STATE.hp < CFG.cureHP) {
|
||||
const c = hasSpell('Full-Cure') ? 'Full-Cure' : hasSpell('Cure') ? 'Cure' : null;
|
||||
const c = hasReadySpell('Full-Cure') ? 'Full-Cure' : hasReadySpell('Cure') ? 'Cure' : null;
|
||||
if (c) return { type: 'spell', name: c, selfTarget: true };
|
||||
}
|
||||
|
||||
|
|
@ -1142,7 +1149,7 @@ function strategyNovice() {
|
|||
if (ch) return ch;
|
||||
}
|
||||
for (const b of BUFF_PRIORITY) {
|
||||
if (shouldBuff(b.icon, b.minTurns) && STATE.mp > 0.10 && hasSpell(b.spell)) {
|
||||
if (shouldBuff(b.icon, b.minTurns) && STATE.mp > 0.10 && hasReadySpell(b.spell)) {
|
||||
if (b.icon === 'regen' && STATE.hp >= CFG.cureRegenHP) continue;
|
||||
return { type: 'spell', name: b.spell, selfTarget: true };
|
||||
}
|
||||
|
|
@ -1255,7 +1262,7 @@ function strategyAdept() {
|
|||
|
||||
// 2. Cure
|
||||
if (STATE.hp < CFG.cureHP) {
|
||||
const c = hasSpell('Full-Cure') ? 'Full-Cure' : hasSpell('Cure') ? 'Cure' : null;
|
||||
const c = hasReadySpell('Full-Cure') ? 'Full-Cure' : hasReadySpell('Cure') ? 'Cure' : null;
|
||||
if (c) return { type: 'spell', name: c, selfTarget: true };
|
||||
}
|
||||
|
||||
|
|
@ -1283,7 +1290,7 @@ function strategyAdept() {
|
|||
if (ch) return ch;
|
||||
}
|
||||
for (const b of BUFF_PRIORITY) {
|
||||
if (shouldBuff(b.icon, b.minTurns) && STATE.mp > 0.10 && hasSpell(b.spell)) {
|
||||
if (shouldBuff(b.icon, b.minTurns) && STATE.mp > 0.10 && hasReadySpell(b.spell)) {
|
||||
if (b.icon === 'regen' && STATE.hp >= CFG.cureRegenHP) continue;
|
||||
return { type: 'spell', name: b.spell, selfTarget: true };
|
||||
}
|
||||
|
|
@ -1311,12 +1318,12 @@ function strategyAdept() {
|
|||
|
||||
// 4. Debuffs — Imperil on 2+ mobs or bosses; Weaken on high-threat
|
||||
if (CFG.autoDebuff && STATE.mp > 0.20) {
|
||||
if (hasSpell('Imperil') && (anyRareMonster() || STATE.monsters.length >= 2)) {
|
||||
if (hasReadySpell('Imperil') && (anyRareMonster() || STATE.monsters.length >= 2)) {
|
||||
const b = findStrongestMonster();
|
||||
if (b >= 0 && !checkMonsterDebuff(b, 'imperil'))
|
||||
return { type: 'spell', name: 'Imperil', target: b, _reason: 'imperil' };
|
||||
}
|
||||
if (hasSpell('Weaken') && (anyRareMonster() || hasHighThreat(80) || STATE.monsters.length >= 3)) {
|
||||
if (hasReadySpell('Weaken') && (anyRareMonster() || hasHighThreat(80) || STATE.monsters.length >= 3)) {
|
||||
const b = findDangerousMonster();
|
||||
if (b >= 0 && !checkMonsterDebuff(b, 'weaken'))
|
||||
return { type: 'spell', name: 'Weaken', target: b, _reason: 'weaken' };
|
||||
|
|
@ -1407,7 +1414,7 @@ function strategyVeteran() {
|
|||
|
||||
// 2. Cure
|
||||
if (STATE.hp < CFG.cureHP) {
|
||||
const c = hasSpell('Full-Cure') ? 'Full-Cure' : hasSpell('Cure') ? 'Cure' : null;
|
||||
const c = hasReadySpell('Full-Cure') ? 'Full-Cure' : hasReadySpell('Cure') ? 'Cure' : null;
|
||||
if (c) return { type: 'spell', name: c, selfTarget: true, _reason: `hp${(STATE.hp*100).toFixed(0)}` };
|
||||
}
|
||||
|
||||
|
|
@ -1434,14 +1441,14 @@ function strategyVeteran() {
|
|||
if (ch) return ch;
|
||||
}
|
||||
for (const b of BUFF_PRIORITY) {
|
||||
if (shouldBuff(b.icon, b.minTurns) && STATE.mp > 0.10 && hasSpell(b.spell)) {
|
||||
if (shouldBuff(b.icon, b.minTurns) && STATE.mp > 0.10 && hasReadySpell(b.spell)) {
|
||||
if (b.icon === 'regen' && STATE.hp >= CFG.cureRegenHP) continue;
|
||||
return { type: 'spell', name: b.spell, selfTarget: true, _reason: 'buff' };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Mana/spirit items — AFTER buffs (Veteran)
|
||||
// 4. Mana/spirit items — AFTER buffs (Novice)
|
||||
if (STATE.mp < CFG.manaGemMP) {
|
||||
const gem = hasUsableGem('mana');
|
||||
if (gem) return { type: 'item', id: gem.id, selfTarget: true };
|
||||
|
|
@ -1466,13 +1473,13 @@ function strategyVeteran() {
|
|||
if (CFG.autoDebuff && STATE.mp > 0.20) {
|
||||
// Imperil: reduce physical mitigation by 25-50% (stacks with Penetrated Armor)
|
||||
// Use on any tanky enemy when facing 2+ monsters or a rare
|
||||
if (hasSpell('Imperil') && (anyRareMonster() || STATE.monsters.length >= 2)) {
|
||||
if (hasReadySpell('Imperil') && (anyRareMonster() || STATE.monsters.length >= 2)) {
|
||||
const b = findStrongestMonster();
|
||||
if (b >= 0 && !checkMonsterDebuff(b, 'imperil'))
|
||||
return { type: 'spell', name: 'Imperil', target: b, _reason: 'imperil' };
|
||||
}
|
||||
// Weaken: reduce incoming damage by 50% — use on high-threat or 3+ mobs
|
||||
if (hasSpell('Weaken') && (anyRareMonster() || hasHighThreat(80) || STATE.monsters.length >= 3)) {
|
||||
if (hasReadySpell('Weaken') && (anyRareMonster() || hasHighThreat(80) || STATE.monsters.length >= 3)) {
|
||||
const b = findDangerousMonster();
|
||||
if (b >= 0 && !checkMonsterDebuff(b, 'weaken'))
|
||||
return { type: 'spell', name: 'Weaken', target: b, _reason: 'weaken' };
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
// CONFIG — default settings
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
|
||||
const VERSION = '0.14.0';
|
||||
const VERSION = '0.14.1';
|
||||
|
||||
const CFG = {
|
||||
// — Battle automation
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// ==UserScript==
|
||||
// @name HV Unified
|
||||
// @namespace hvunified
|
||||
// @version 0.14.0
|
||||
// @version 0.14.1
|
||||
// @description HV Unified — battle automation, guidance, and UI enhancements for HentaiVerse
|
||||
// @author GaboGG + Hermes
|
||||
// @match *://*.hentaiverse.org/*
|
||||
|
|
|
|||
|
|
@ -8,6 +8,13 @@ function hasSpell(name) {
|
|||
return STATE.spellsKnown.some(s => s.n === name);
|
||||
}
|
||||
|
||||
// Check if a spell is not on cooldown (has onclick in the DOM)
|
||||
function hasReadySpell(name) {
|
||||
const el = $$('.btsd, .btqs').find(e =>
|
||||
(e.getAttribute('onmouseover') || '').includes(`set_infopane_spell('${name}'`));
|
||||
return el ? el.hasAttribute('onclick') : false;
|
||||
}
|
||||
|
||||
function findSpell(name) {
|
||||
return STATE.spellsKnown.find(s => s.n === name);
|
||||
}
|
||||
|
|
@ -214,7 +221,7 @@ function castInitialBuffs() {
|
|||
// Collect all missing/expiring buffs with their MP cost
|
||||
const candidates = [];
|
||||
for (const b of BUFF_PRIORITY) {
|
||||
if (!hasSpell(b.spell)) continue;
|
||||
if (!hasReadySpell(b.spell)) continue;
|
||||
const dur = buffDuration(b.icon);
|
||||
if (dur === 0 || dur < 3) {
|
||||
if (b.icon === 'regen' && STATE.hp >= CFG.cureRegenHP) continue;
|
||||
|
|
@ -284,7 +291,7 @@ function findBestChannelingTarget() {
|
|||
|
||||
const candidates = [];
|
||||
for (const b of BUFF_PRIORITY) {
|
||||
if (!hasSpell(b.spell)) continue;
|
||||
if (!hasReadySpell(b.spell)) continue;
|
||||
const dur = buffDuration(b.icon);
|
||||
if (b.icon === 'regen' && STATE.hp >= CFG.cureRegenHP) continue;
|
||||
const sp = findSpell(b.spell);
|
||||
|
|
@ -401,7 +408,7 @@ function strategyNovice() {
|
|||
|
||||
// 2. Cure
|
||||
if (STATE.hp < CFG.cureHP) {
|
||||
const c = hasSpell('Full-Cure') ? 'Full-Cure' : hasSpell('Cure') ? 'Cure' : null;
|
||||
const c = hasReadySpell('Full-Cure') ? 'Full-Cure' : hasReadySpell('Cure') ? 'Cure' : null;
|
||||
if (c) return { type: 'spell', name: c, selfTarget: true };
|
||||
}
|
||||
|
||||
|
|
@ -417,7 +424,7 @@ function strategyNovice() {
|
|||
if (ch) return ch;
|
||||
}
|
||||
for (const b of BUFF_PRIORITY) {
|
||||
if (shouldBuff(b.icon, b.minTurns) && STATE.mp > 0.10 && hasSpell(b.spell)) {
|
||||
if (shouldBuff(b.icon, b.minTurns) && STATE.mp > 0.10 && hasReadySpell(b.spell)) {
|
||||
if (b.icon === 'regen' && STATE.hp >= CFG.cureRegenHP) continue;
|
||||
return { type: 'spell', name: b.spell, selfTarget: true };
|
||||
}
|
||||
|
|
@ -530,7 +537,7 @@ function strategyAdept() {
|
|||
|
||||
// 2. Cure
|
||||
if (STATE.hp < CFG.cureHP) {
|
||||
const c = hasSpell('Full-Cure') ? 'Full-Cure' : hasSpell('Cure') ? 'Cure' : null;
|
||||
const c = hasReadySpell('Full-Cure') ? 'Full-Cure' : hasReadySpell('Cure') ? 'Cure' : null;
|
||||
if (c) return { type: 'spell', name: c, selfTarget: true };
|
||||
}
|
||||
|
||||
|
|
@ -558,7 +565,7 @@ function strategyAdept() {
|
|||
if (ch) return ch;
|
||||
}
|
||||
for (const b of BUFF_PRIORITY) {
|
||||
if (shouldBuff(b.icon, b.minTurns) && STATE.mp > 0.10 && hasSpell(b.spell)) {
|
||||
if (shouldBuff(b.icon, b.minTurns) && STATE.mp > 0.10 && hasReadySpell(b.spell)) {
|
||||
if (b.icon === 'regen' && STATE.hp >= CFG.cureRegenHP) continue;
|
||||
return { type: 'spell', name: b.spell, selfTarget: true };
|
||||
}
|
||||
|
|
@ -586,12 +593,12 @@ function strategyAdept() {
|
|||
|
||||
// 4. Debuffs — Imperil on 2+ mobs or bosses; Weaken on high-threat
|
||||
if (CFG.autoDebuff && STATE.mp > 0.20) {
|
||||
if (hasSpell('Imperil') && (anyRareMonster() || STATE.monsters.length >= 2)) {
|
||||
if (hasReadySpell('Imperil') && (anyRareMonster() || STATE.monsters.length >= 2)) {
|
||||
const b = findStrongestMonster();
|
||||
if (b >= 0 && !checkMonsterDebuff(b, 'imperil'))
|
||||
return { type: 'spell', name: 'Imperil', target: b, _reason: 'imperil' };
|
||||
}
|
||||
if (hasSpell('Weaken') && (anyRareMonster() || hasHighThreat(80) || STATE.monsters.length >= 3)) {
|
||||
if (hasReadySpell('Weaken') && (anyRareMonster() || hasHighThreat(80) || STATE.monsters.length >= 3)) {
|
||||
const b = findDangerousMonster();
|
||||
if (b >= 0 && !checkMonsterDebuff(b, 'weaken'))
|
||||
return { type: 'spell', name: 'Weaken', target: b, _reason: 'weaken' };
|
||||
|
|
@ -682,7 +689,7 @@ function strategyVeteran() {
|
|||
|
||||
// 2. Cure
|
||||
if (STATE.hp < CFG.cureHP) {
|
||||
const c = hasSpell('Full-Cure') ? 'Full-Cure' : hasSpell('Cure') ? 'Cure' : null;
|
||||
const c = hasReadySpell('Full-Cure') ? 'Full-Cure' : hasReadySpell('Cure') ? 'Cure' : null;
|
||||
if (c) return { type: 'spell', name: c, selfTarget: true, _reason: `hp${(STATE.hp*100).toFixed(0)}` };
|
||||
}
|
||||
|
||||
|
|
@ -709,14 +716,14 @@ function strategyVeteran() {
|
|||
if (ch) return ch;
|
||||
}
|
||||
for (const b of BUFF_PRIORITY) {
|
||||
if (shouldBuff(b.icon, b.minTurns) && STATE.mp > 0.10 && hasSpell(b.spell)) {
|
||||
if (shouldBuff(b.icon, b.minTurns) && STATE.mp > 0.10 && hasReadySpell(b.spell)) {
|
||||
if (b.icon === 'regen' && STATE.hp >= CFG.cureRegenHP) continue;
|
||||
return { type: 'spell', name: b.spell, selfTarget: true, _reason: 'buff' };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Mana/spirit items — AFTER buffs (Veteran)
|
||||
// 4. Mana/spirit items — AFTER buffs (Novice)
|
||||
if (STATE.mp < CFG.manaGemMP) {
|
||||
const gem = hasUsableGem('mana');
|
||||
if (gem) return { type: 'item', id: gem.id, selfTarget: true };
|
||||
|
|
@ -741,13 +748,13 @@ function strategyVeteran() {
|
|||
if (CFG.autoDebuff && STATE.mp > 0.20) {
|
||||
// Imperil: reduce physical mitigation by 25-50% (stacks with Penetrated Armor)
|
||||
// Use on any tanky enemy when facing 2+ monsters or a rare
|
||||
if (hasSpell('Imperil') && (anyRareMonster() || STATE.monsters.length >= 2)) {
|
||||
if (hasReadySpell('Imperil') && (anyRareMonster() || STATE.monsters.length >= 2)) {
|
||||
const b = findStrongestMonster();
|
||||
if (b >= 0 && !checkMonsterDebuff(b, 'imperil'))
|
||||
return { type: 'spell', name: 'Imperil', target: b, _reason: 'imperil' };
|
||||
}
|
||||
// Weaken: reduce incoming damage by 50% — use on high-threat or 3+ mobs
|
||||
if (hasSpell('Weaken') && (anyRareMonster() || hasHighThreat(80) || STATE.monsters.length >= 3)) {
|
||||
if (hasReadySpell('Weaken') && (anyRareMonster() || hasHighThreat(80) || STATE.monsters.length >= 3)) {
|
||||
const b = findDangerousMonster();
|
||||
if (b >= 0 && !checkMonsterDebuff(b, 'weaken'))
|
||||
return { type: 'spell', name: 'Weaken', target: b, _reason: 'weaken' };
|
||||
|
|
|
|||
Loading…
Reference in a new issue