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
This commit is contained in:
parent
7093409580
commit
d9fc1e547c
3 changed files with 189 additions and 12 deletions
|
|
@ -915,11 +915,31 @@ function strategyNovice() {
|
|||
}
|
||||
}
|
||||
|
||||
// 7. Channeling kickstart or basic attack — builds OC naturally
|
||||
// 7. Channeling kickstart or basic attack
|
||||
{
|
||||
const k = kickstartChanneling();
|
||||
if (k) return k;
|
||||
}
|
||||
|
||||
// If channeling is charged, consume it on a useful spell
|
||||
if (STATE.channeling && STATE.mp > 0.10) {
|
||||
for (const b of BUFF_PRIORITY) {
|
||||
if (hasSpell(b.spell)) {
|
||||
const dur = buffDuration(b.icon);
|
||||
if (dur === 0 || dur < 10) {
|
||||
if (b.icon === 'regen' && STATE.hp >= CFG.cureRegenHP) continue;
|
||||
return { type: 'spell', name: b.spell, selfTarget: true };
|
||||
}
|
||||
}
|
||||
}
|
||||
const cheap = findDmgSpell(['Fiery Blast', 'Freeze', 'Shockblast', 'Gale']);
|
||||
if (cheap) {
|
||||
const t = findWeakestMonster();
|
||||
if (t >= 0 && STATE.monsters[t] && STATE.monsters[t].hasAttribute('onclick'))
|
||||
return { type: 'spell', name: cheap, target: t };
|
||||
}
|
||||
}
|
||||
|
||||
const t = findWeakestMonster();
|
||||
if (t >= 0 && STATE.monsters[t] && STATE.monsters[t].hasAttribute('onclick'))
|
||||
return { type: 'attack', target: t };
|
||||
|
|
@ -978,9 +998,8 @@ function strategyAdept() {
|
|||
}
|
||||
}
|
||||
|
||||
// 5. Weapon skills — before spirit stance, so we don't burn OC on spirit
|
||||
// and miss the skill window. This is the highest-value use of OC.
|
||||
const skillOC_N = CFG.skillOC || 80;
|
||||
// 5. Weapon skills — before spirit stance
|
||||
const skillOC_N = CFG.skillOC || 75;
|
||||
if (STATE.oc >= skillOC_N) {
|
||||
// Rending Blow: AoE armor pen vs 5+ enemies (highest priority in Grindfest)
|
||||
if (STATE.monsters.length >= 5 && STATE.skillsKnown.includes('Rending Blow')) {
|
||||
|
|
@ -1030,6 +1049,26 @@ function strategyAdept() {
|
|||
const k = kickstartChanneling();
|
||||
if (k) return k;
|
||||
}
|
||||
|
||||
// Use channeling charge on a spell
|
||||
if (STATE.channeling && STATE.mp > 0.10) {
|
||||
for (const b of BUFF_PRIORITY) {
|
||||
if (hasSpell(b.spell)) {
|
||||
const dur = buffDuration(b.icon);
|
||||
if (dur === 0 || dur < 10) {
|
||||
if (b.icon === 'regen' && STATE.hp >= CFG.cureRegenHP) continue;
|
||||
return { type: 'spell', name: b.spell, selfTarget: true };
|
||||
}
|
||||
}
|
||||
}
|
||||
const cheap = findDmgSpell(['Fiery Blast', 'Freeze', 'Shockblast', 'Gale']);
|
||||
if (cheap) {
|
||||
const t = findWeakestMonster();
|
||||
if (t >= 0 && STATE.monsters[t] && STATE.monsters[t].hasAttribute('onclick'))
|
||||
return { type: 'spell', name: cheap, target: t };
|
||||
}
|
||||
}
|
||||
|
||||
const t = findWeakestMonster();
|
||||
if (t >= 0 && STATE.monsters[t] && STATE.monsters[t].hasAttribute('onclick'))
|
||||
return { type: 'attack', target: t };
|
||||
|
|
@ -1142,6 +1181,26 @@ function strategyVeteran() {
|
|||
const k = kickstartChanneling();
|
||||
if (k) return k;
|
||||
}
|
||||
|
||||
// Consume channeling on a spell
|
||||
if (STATE.channeling && STATE.mp > 0.10) {
|
||||
for (const b of BUFF_PRIORITY) {
|
||||
if (hasSpell(b.spell)) {
|
||||
const dur = buffDuration(b.icon);
|
||||
if (dur === 0 || dur < 10) {
|
||||
if (b.icon === 'regen' && STATE.hp >= CFG.cureRegenHP) continue;
|
||||
return { type: 'spell', name: b.spell, selfTarget: true };
|
||||
}
|
||||
}
|
||||
}
|
||||
const cheap = findDmgSpell(['Fiery Blast', 'Freeze', 'Shockblast', 'Gale']);
|
||||
if (cheap) {
|
||||
const t = findWeakestMonster();
|
||||
if (t >= 0 && STATE.monsters[t] && STATE.monsters[t].hasAttribute('onclick'))
|
||||
return { type: 'spell', name: cheap, target: t };
|
||||
}
|
||||
}
|
||||
|
||||
const t = findWeakestMonster();
|
||||
if (t >= 0 && STATE.monsters[t] && STATE.monsters[t].hasAttribute('onclick'))
|
||||
return { type: 'attack', target: t };
|
||||
|
|
|
|||
|
|
@ -915,11 +915,31 @@ function strategyNovice() {
|
|||
}
|
||||
}
|
||||
|
||||
// 7. Channeling kickstart or basic attack — builds OC naturally
|
||||
// 7. Channeling kickstart or basic attack
|
||||
{
|
||||
const k = kickstartChanneling();
|
||||
if (k) return k;
|
||||
}
|
||||
|
||||
// If channeling is charged, consume it on a useful spell
|
||||
if (STATE.channeling && STATE.mp > 0.10) {
|
||||
for (const b of BUFF_PRIORITY) {
|
||||
if (hasSpell(b.spell)) {
|
||||
const dur = buffDuration(b.icon);
|
||||
if (dur === 0 || dur < 10) {
|
||||
if (b.icon === 'regen' && STATE.hp >= CFG.cureRegenHP) continue;
|
||||
return { type: 'spell', name: b.spell, selfTarget: true };
|
||||
}
|
||||
}
|
||||
}
|
||||
const cheap = findDmgSpell(['Fiery Blast', 'Freeze', 'Shockblast', 'Gale']);
|
||||
if (cheap) {
|
||||
const t = findWeakestMonster();
|
||||
if (t >= 0 && STATE.monsters[t] && STATE.monsters[t].hasAttribute('onclick'))
|
||||
return { type: 'spell', name: cheap, target: t };
|
||||
}
|
||||
}
|
||||
|
||||
const t = findWeakestMonster();
|
||||
if (t >= 0 && STATE.monsters[t] && STATE.monsters[t].hasAttribute('onclick'))
|
||||
return { type: 'attack', target: t };
|
||||
|
|
@ -978,9 +998,8 @@ function strategyAdept() {
|
|||
}
|
||||
}
|
||||
|
||||
// 5. Weapon skills — before spirit stance, so we don't burn OC on spirit
|
||||
// and miss the skill window. This is the highest-value use of OC.
|
||||
const skillOC_N = CFG.skillOC || 80;
|
||||
// 5. Weapon skills — before spirit stance
|
||||
const skillOC_N = CFG.skillOC || 75;
|
||||
if (STATE.oc >= skillOC_N) {
|
||||
// Rending Blow: AoE armor pen vs 5+ enemies (highest priority in Grindfest)
|
||||
if (STATE.monsters.length >= 5 && STATE.skillsKnown.includes('Rending Blow')) {
|
||||
|
|
@ -1030,6 +1049,26 @@ function strategyAdept() {
|
|||
const k = kickstartChanneling();
|
||||
if (k) return k;
|
||||
}
|
||||
|
||||
// Use channeling charge on a spell
|
||||
if (STATE.channeling && STATE.mp > 0.10) {
|
||||
for (const b of BUFF_PRIORITY) {
|
||||
if (hasSpell(b.spell)) {
|
||||
const dur = buffDuration(b.icon);
|
||||
if (dur === 0 || dur < 10) {
|
||||
if (b.icon === 'regen' && STATE.hp >= CFG.cureRegenHP) continue;
|
||||
return { type: 'spell', name: b.spell, selfTarget: true };
|
||||
}
|
||||
}
|
||||
}
|
||||
const cheap = findDmgSpell(['Fiery Blast', 'Freeze', 'Shockblast', 'Gale']);
|
||||
if (cheap) {
|
||||
const t = findWeakestMonster();
|
||||
if (t >= 0 && STATE.monsters[t] && STATE.monsters[t].hasAttribute('onclick'))
|
||||
return { type: 'spell', name: cheap, target: t };
|
||||
}
|
||||
}
|
||||
|
||||
const t = findWeakestMonster();
|
||||
if (t >= 0 && STATE.monsters[t] && STATE.monsters[t].hasAttribute('onclick'))
|
||||
return { type: 'attack', target: t };
|
||||
|
|
@ -1142,6 +1181,26 @@ function strategyVeteran() {
|
|||
const k = kickstartChanneling();
|
||||
if (k) return k;
|
||||
}
|
||||
|
||||
// Consume channeling on a spell
|
||||
if (STATE.channeling && STATE.mp > 0.10) {
|
||||
for (const b of BUFF_PRIORITY) {
|
||||
if (hasSpell(b.spell)) {
|
||||
const dur = buffDuration(b.icon);
|
||||
if (dur === 0 || dur < 10) {
|
||||
if (b.icon === 'regen' && STATE.hp >= CFG.cureRegenHP) continue;
|
||||
return { type: 'spell', name: b.spell, selfTarget: true };
|
||||
}
|
||||
}
|
||||
}
|
||||
const cheap = findDmgSpell(['Fiery Blast', 'Freeze', 'Shockblast', 'Gale']);
|
||||
if (cheap) {
|
||||
const t = findWeakestMonster();
|
||||
if (t >= 0 && STATE.monsters[t] && STATE.monsters[t].hasAttribute('onclick'))
|
||||
return { type: 'spell', name: cheap, target: t };
|
||||
}
|
||||
}
|
||||
|
||||
const t = findWeakestMonster();
|
||||
if (t >= 0 && STATE.monsters[t] && STATE.monsters[t].hasAttribute('onclick'))
|
||||
return { type: 'attack', target: t };
|
||||
|
|
|
|||
|
|
@ -270,11 +270,31 @@ function strategyNovice() {
|
|||
}
|
||||
}
|
||||
|
||||
// 7. Channeling kickstart or basic attack — builds OC naturally
|
||||
// 7. Channeling kickstart or basic attack
|
||||
{
|
||||
const k = kickstartChanneling();
|
||||
if (k) return k;
|
||||
}
|
||||
|
||||
// If channeling is charged, consume it on a useful spell
|
||||
if (STATE.channeling && STATE.mp > 0.10) {
|
||||
for (const b of BUFF_PRIORITY) {
|
||||
if (hasSpell(b.spell)) {
|
||||
const dur = buffDuration(b.icon);
|
||||
if (dur === 0 || dur < 10) {
|
||||
if (b.icon === 'regen' && STATE.hp >= CFG.cureRegenHP) continue;
|
||||
return { type: 'spell', name: b.spell, selfTarget: true };
|
||||
}
|
||||
}
|
||||
}
|
||||
const cheap = findDmgSpell(['Fiery Blast', 'Freeze', 'Shockblast', 'Gale']);
|
||||
if (cheap) {
|
||||
const t = findWeakestMonster();
|
||||
if (t >= 0 && STATE.monsters[t] && STATE.monsters[t].hasAttribute('onclick'))
|
||||
return { type: 'spell', name: cheap, target: t };
|
||||
}
|
||||
}
|
||||
|
||||
const t = findWeakestMonster();
|
||||
if (t >= 0 && STATE.monsters[t] && STATE.monsters[t].hasAttribute('onclick'))
|
||||
return { type: 'attack', target: t };
|
||||
|
|
@ -333,9 +353,8 @@ function strategyAdept() {
|
|||
}
|
||||
}
|
||||
|
||||
// 5. Weapon skills — before spirit stance, so we don't burn OC on spirit
|
||||
// and miss the skill window. This is the highest-value use of OC.
|
||||
const skillOC_N = CFG.skillOC || 80;
|
||||
// 5. Weapon skills — before spirit stance
|
||||
const skillOC_N = CFG.skillOC || 75;
|
||||
if (STATE.oc >= skillOC_N) {
|
||||
// Rending Blow: AoE armor pen vs 5+ enemies (highest priority in Grindfest)
|
||||
if (STATE.monsters.length >= 5 && STATE.skillsKnown.includes('Rending Blow')) {
|
||||
|
|
@ -385,6 +404,26 @@ function strategyAdept() {
|
|||
const k = kickstartChanneling();
|
||||
if (k) return k;
|
||||
}
|
||||
|
||||
// Use channeling charge on a spell
|
||||
if (STATE.channeling && STATE.mp > 0.10) {
|
||||
for (const b of BUFF_PRIORITY) {
|
||||
if (hasSpell(b.spell)) {
|
||||
const dur = buffDuration(b.icon);
|
||||
if (dur === 0 || dur < 10) {
|
||||
if (b.icon === 'regen' && STATE.hp >= CFG.cureRegenHP) continue;
|
||||
return { type: 'spell', name: b.spell, selfTarget: true };
|
||||
}
|
||||
}
|
||||
}
|
||||
const cheap = findDmgSpell(['Fiery Blast', 'Freeze', 'Shockblast', 'Gale']);
|
||||
if (cheap) {
|
||||
const t = findWeakestMonster();
|
||||
if (t >= 0 && STATE.monsters[t] && STATE.monsters[t].hasAttribute('onclick'))
|
||||
return { type: 'spell', name: cheap, target: t };
|
||||
}
|
||||
}
|
||||
|
||||
const t = findWeakestMonster();
|
||||
if (t >= 0 && STATE.monsters[t] && STATE.monsters[t].hasAttribute('onclick'))
|
||||
return { type: 'attack', target: t };
|
||||
|
|
@ -497,6 +536,26 @@ function strategyVeteran() {
|
|||
const k = kickstartChanneling();
|
||||
if (k) return k;
|
||||
}
|
||||
|
||||
// Consume channeling on a spell
|
||||
if (STATE.channeling && STATE.mp > 0.10) {
|
||||
for (const b of BUFF_PRIORITY) {
|
||||
if (hasSpell(b.spell)) {
|
||||
const dur = buffDuration(b.icon);
|
||||
if (dur === 0 || dur < 10) {
|
||||
if (b.icon === 'regen' && STATE.hp >= CFG.cureRegenHP) continue;
|
||||
return { type: 'spell', name: b.spell, selfTarget: true };
|
||||
}
|
||||
}
|
||||
}
|
||||
const cheap = findDmgSpell(['Fiery Blast', 'Freeze', 'Shockblast', 'Gale']);
|
||||
if (cheap) {
|
||||
const t = findWeakestMonster();
|
||||
if (t >= 0 && STATE.monsters[t] && STATE.monsters[t].hasAttribute('onclick'))
|
||||
return { type: 'spell', name: cheap, target: t };
|
||||
}
|
||||
}
|
||||
|
||||
const t = findWeakestMonster();
|
||||
if (t >= 0 && STATE.monsters[t] && STATE.monsters[t].hasAttribute('onclick'))
|
||||
return { type: 'attack', target: t };
|
||||
|
|
|
|||
Loading…
Reference in a new issue