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.
This commit is contained in:
GaboGG 2026-07-21 14:35:54 -04:00
parent f060bd31a8
commit 7093409580
4 changed files with 72 additions and 93 deletions

View file

@ -29,16 +29,16 @@ const CFG = {
autoCure: true,
autoSpirit: false,
// — Thresholds (benchmark-optimized)
// — Thresholds (simulation-backed)
cureHP: 0.45, // Cast Cure below this HP
cureItemHP: 0.50, // Use health items at this HP
cureRegenHP: 0.65, // Cast Regen below this HP
manaGemMP: 0.70, // Use mana gems below this MP (aggressive, stay rich)
manaPotionMP: 0.50, // Use mana potions at this MP
manaGemMP: 0.45, // Use mana gems below this MP
manaPotionMP: 0.25, // Use mana potions at this MP
spiritPotionSP: 0.30, // Use spirit gems/potions below this SP
spiritStanceOC: 80, // Activate Spirit Stance late (save SP for skills)
skillOC: 75, // Use weapon skills at this OC
useAttackSpells: true, // Use cheap spells for channeling kickstart
useAttackSpells: false, // Don't use attack spells (let channel proc naturally)
// — Out of battle
autoSell: true,
@ -778,40 +778,33 @@ function getBestDamageSpell() {
}
// ── Channeling maintenance ──
// Cheapest mana-costing spells to trigger channeling when it wears off
const CHANNEL_TRIGGERS = ['Fiery Blast', 'Freeze', 'Shockblast', 'Gale'];
//
// Channeling: procs randomly when casting any mana-costing spell.
// Proc chance = spell_cost / (base_mana * 1.2)
// Cannot proc from spells cast while already Channeling.
// Effect: next spell costs 1 MP and is 50% stronger (or 50% longer for buffs)
// Duration: 5 ticks (15 with Mystic Gem in P-slot)
//
// Best trigger spells: Regen/Absorb (17 MP = ~6% proc chance each cast)
// Cheaper spells have proportionally lower proc chance.
function kickstartChanneling() {
if (STATE.channeling) return null;
// Priority 1: refresh any buff that's about to expire (< 15 turns)
// This is more useful than wasting MP on attack spells
// Priority: cast or refresh a buff with a good MP cost for proc chance
// Regen has 17 MP = best cost/proc ratio among buffs we always want active
if (STATE.mp > 0.20) {
// Try buffs in priority order — Regen is best for sustain + channel proc
for (const b of BUFF_PRIORITY) {
const dur = buffDuration(b.icon);
if (dur > 0 && dur < 15 && STATE.mp > 0.15 && hasSpell(b.spell)) {
// Only refresh if expiring soon (< 5 turns) or missing
if ((dur === 0 || dur < 5) && STATE.mp > 0.15 && hasSpell(b.spell)) {
if (b.icon === 'regen' && STATE.hp >= CFG.cureRegenHP) continue;
return { type: 'spell', name: b.spell, selfTarget: true };
}
}
// Priority 2: cast a buff that's missing entirely
for (const b of BUFF_PRIORITY) {
if (buffDuration(b.icon) === 0 && STATE.mp > 0.15 && hasSpell(b.spell)) {
if (b.icon === 'regen' && STATE.hp >= CFG.cureRegenHP) continue;
return { type: 'spell', name: b.spell, selfTarget: true };
}
}
// Priority 3: cheapest attack spell to trigger channeling (only if attack spells enabled)
if (CFG.useAttackSpells) {
const spell = findDmgSpell(CHANNEL_TRIGGERS);
if (spell && STATE.mp > 0.10) {
const t = findWeakestMonster();
if (t >= 0 && STATE.monsters[t] && STATE.monsters[t].hasAttribute('onclick'))
return { type: 'spell', name: spell, target: t };
}
}
return null;
}

View file

@ -29,16 +29,16 @@ const CFG = {
autoCure: true,
autoSpirit: false,
// — Thresholds (benchmark-optimized)
// — Thresholds (simulation-backed)
cureHP: 0.45, // Cast Cure below this HP
cureItemHP: 0.50, // Use health items at this HP
cureRegenHP: 0.65, // Cast Regen below this HP
manaGemMP: 0.70, // Use mana gems below this MP (aggressive, stay rich)
manaPotionMP: 0.50, // Use mana potions at this MP
manaGemMP: 0.45, // Use mana gems below this MP
manaPotionMP: 0.25, // Use mana potions at this MP
spiritPotionSP: 0.30, // Use spirit gems/potions below this SP
spiritStanceOC: 80, // Activate Spirit Stance late (save SP for skills)
skillOC: 75, // Use weapon skills at this OC
useAttackSpells: true, // Use cheap spells for channeling kickstart
useAttackSpells: false, // Don't use attack spells (let channel proc naturally)
// — Out of battle
autoSell: true,
@ -778,40 +778,33 @@ function getBestDamageSpell() {
}
// ── Channeling maintenance ──
// Cheapest mana-costing spells to trigger channeling when it wears off
const CHANNEL_TRIGGERS = ['Fiery Blast', 'Freeze', 'Shockblast', 'Gale'];
//
// Channeling: procs randomly when casting any mana-costing spell.
// Proc chance = spell_cost / (base_mana * 1.2)
// Cannot proc from spells cast while already Channeling.
// Effect: next spell costs 1 MP and is 50% stronger (or 50% longer for buffs)
// Duration: 5 ticks (15 with Mystic Gem in P-slot)
//
// Best trigger spells: Regen/Absorb (17 MP = ~6% proc chance each cast)
// Cheaper spells have proportionally lower proc chance.
function kickstartChanneling() {
if (STATE.channeling) return null;
// Priority 1: refresh any buff that's about to expire (< 15 turns)
// This is more useful than wasting MP on attack spells
// Priority: cast or refresh a buff with a good MP cost for proc chance
// Regen has 17 MP = best cost/proc ratio among buffs we always want active
if (STATE.mp > 0.20) {
// Try buffs in priority order — Regen is best for sustain + channel proc
for (const b of BUFF_PRIORITY) {
const dur = buffDuration(b.icon);
if (dur > 0 && dur < 15 && STATE.mp > 0.15 && hasSpell(b.spell)) {
// Only refresh if expiring soon (< 5 turns) or missing
if ((dur === 0 || dur < 5) && STATE.mp > 0.15 && hasSpell(b.spell)) {
if (b.icon === 'regen' && STATE.hp >= CFG.cureRegenHP) continue;
return { type: 'spell', name: b.spell, selfTarget: true };
}
}
// Priority 2: cast a buff that's missing entirely
for (const b of BUFF_PRIORITY) {
if (buffDuration(b.icon) === 0 && STATE.mp > 0.15 && hasSpell(b.spell)) {
if (b.icon === 'regen' && STATE.hp >= CFG.cureRegenHP) continue;
return { type: 'spell', name: b.spell, selfTarget: true };
}
}
// Priority 3: cheapest attack spell to trigger channeling (only if attack spells enabled)
if (CFG.useAttackSpells) {
const spell = findDmgSpell(CHANNEL_TRIGGERS);
if (spell && STATE.mp > 0.10) {
const t = findWeakestMonster();
if (t >= 0 && STATE.monsters[t] && STATE.monsters[t].hasAttribute('onclick'))
return { type: 'spell', name: spell, target: t };
}
}
return null;
}

View file

@ -14,16 +14,16 @@ const CFG = {
autoCure: true,
autoSpirit: false,
// — Thresholds (benchmark-optimized)
// — Thresholds (simulation-backed)
cureHP: 0.45, // Cast Cure below this HP
cureItemHP: 0.50, // Use health items at this HP
cureRegenHP: 0.65, // Cast Regen below this HP
manaGemMP: 0.70, // Use mana gems below this MP (aggressive, stay rich)
manaPotionMP: 0.50, // Use mana potions at this MP
manaGemMP: 0.45, // Use mana gems below this MP
manaPotionMP: 0.25, // Use mana potions at this MP
spiritPotionSP: 0.30, // Use spirit gems/potions below this SP
spiritStanceOC: 80, // Activate Spirit Stance late (save SP for skills)
skillOC: 75, // Use weapon skills at this OC
useAttackSpells: true, // Use cheap spells for channeling kickstart
useAttackSpells: false, // Don't use attack spells (let channel proc naturally)
// — Out of battle
autoSell: true,

View file

@ -133,40 +133,33 @@ function getBestDamageSpell() {
}
// ── Channeling maintenance ──
// Cheapest mana-costing spells to trigger channeling when it wears off
const CHANNEL_TRIGGERS = ['Fiery Blast', 'Freeze', 'Shockblast', 'Gale'];
//
// Channeling: procs randomly when casting any mana-costing spell.
// Proc chance = spell_cost / (base_mana * 1.2)
// Cannot proc from spells cast while already Channeling.
// Effect: next spell costs 1 MP and is 50% stronger (or 50% longer for buffs)
// Duration: 5 ticks (15 with Mystic Gem in P-slot)
//
// Best trigger spells: Regen/Absorb (17 MP = ~6% proc chance each cast)
// Cheaper spells have proportionally lower proc chance.
function kickstartChanneling() {
if (STATE.channeling) return null;
// Priority 1: refresh any buff that's about to expire (< 15 turns)
// This is more useful than wasting MP on attack spells
// Priority: cast or refresh a buff with a good MP cost for proc chance
// Regen has 17 MP = best cost/proc ratio among buffs we always want active
if (STATE.mp > 0.20) {
// Try buffs in priority order — Regen is best for sustain + channel proc
for (const b of BUFF_PRIORITY) {
const dur = buffDuration(b.icon);
if (dur > 0 && dur < 15 && STATE.mp > 0.15 && hasSpell(b.spell)) {
// Only refresh if expiring soon (< 5 turns) or missing
if ((dur === 0 || dur < 5) && STATE.mp > 0.15 && hasSpell(b.spell)) {
if (b.icon === 'regen' && STATE.hp >= CFG.cureRegenHP) continue;
return { type: 'spell', name: b.spell, selfTarget: true };
}
}
// Priority 2: cast a buff that's missing entirely
for (const b of BUFF_PRIORITY) {
if (buffDuration(b.icon) === 0 && STATE.mp > 0.15 && hasSpell(b.spell)) {
if (b.icon === 'regen' && STATE.hp >= CFG.cureRegenHP) continue;
return { type: 'spell', name: b.spell, selfTarget: true };
}
}
// Priority 3: cheapest attack spell to trigger channeling (only if attack spells enabled)
if (CFG.useAttackSpells) {
const spell = findDmgSpell(CHANNEL_TRIGGERS);
if (spell && STATE.mp > 0.10) {
const t = findWeakestMonster();
if (t >= 0 && STATE.monsters[t] && STATE.monsters[t].hasAttribute('onclick'))
return { type: 'spell', name: spell, target: t };
}
}
return null;
}