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:
parent
f060bd31a8
commit
7093409580
4 changed files with 72 additions and 93 deletions
|
|
@ -29,16 +29,16 @@ const CFG = {
|
||||||
autoCure: true,
|
autoCure: true,
|
||||||
autoSpirit: false,
|
autoSpirit: false,
|
||||||
|
|
||||||
// — Thresholds (benchmark-optimized)
|
// — Thresholds (simulation-backed)
|
||||||
cureHP: 0.45, // Cast Cure below this HP
|
cureHP: 0.45, // Cast Cure below this HP
|
||||||
cureItemHP: 0.50, // Use health items at this HP
|
cureItemHP: 0.50, // Use health items at this HP
|
||||||
cureRegenHP: 0.65, // Cast Regen below this HP
|
cureRegenHP: 0.65, // Cast Regen below this HP
|
||||||
manaGemMP: 0.70, // Use mana gems below this MP (aggressive, stay rich)
|
manaGemMP: 0.45, // Use mana gems below this MP
|
||||||
manaPotionMP: 0.50, // Use mana potions at this MP
|
manaPotionMP: 0.25, // Use mana potions at this MP
|
||||||
spiritPotionSP: 0.30, // Use spirit gems/potions below this SP
|
spiritPotionSP: 0.30, // Use spirit gems/potions below this SP
|
||||||
spiritStanceOC: 80, // Activate Spirit Stance late (save SP for skills)
|
spiritStanceOC: 80, // Activate Spirit Stance late (save SP for skills)
|
||||||
skillOC: 75, // Use weapon skills at this OC
|
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
|
// — Out of battle
|
||||||
autoSell: true,
|
autoSell: true,
|
||||||
|
|
@ -778,40 +778,33 @@ function getBestDamageSpell() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Channeling maintenance ──
|
// ── Channeling maintenance ──
|
||||||
|
//
|
||||||
// Cheapest mana-costing spells to trigger channeling when it wears off
|
// Channeling: procs randomly when casting any mana-costing spell.
|
||||||
const CHANNEL_TRIGGERS = ['Fiery Blast', 'Freeze', 'Shockblast', 'Gale'];
|
// 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() {
|
function kickstartChanneling() {
|
||||||
if (STATE.channeling) return null;
|
if (STATE.channeling) return null;
|
||||||
|
|
||||||
// Priority 1: refresh any buff that's about to expire (< 15 turns)
|
// Priority: cast or refresh a buff with a good MP cost for proc chance
|
||||||
// This is more useful than wasting MP on attack spells
|
// 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) {
|
for (const b of BUFF_PRIORITY) {
|
||||||
const dur = buffDuration(b.icon);
|
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;
|
if (b.icon === 'regen' && STATE.hp >= CFG.cureRegenHP) continue;
|
||||||
return { type: 'spell', name: b.spell, selfTarget: true };
|
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;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,16 +29,16 @@ const CFG = {
|
||||||
autoCure: true,
|
autoCure: true,
|
||||||
autoSpirit: false,
|
autoSpirit: false,
|
||||||
|
|
||||||
// — Thresholds (benchmark-optimized)
|
// — Thresholds (simulation-backed)
|
||||||
cureHP: 0.45, // Cast Cure below this HP
|
cureHP: 0.45, // Cast Cure below this HP
|
||||||
cureItemHP: 0.50, // Use health items at this HP
|
cureItemHP: 0.50, // Use health items at this HP
|
||||||
cureRegenHP: 0.65, // Cast Regen below this HP
|
cureRegenHP: 0.65, // Cast Regen below this HP
|
||||||
manaGemMP: 0.70, // Use mana gems below this MP (aggressive, stay rich)
|
manaGemMP: 0.45, // Use mana gems below this MP
|
||||||
manaPotionMP: 0.50, // Use mana potions at this MP
|
manaPotionMP: 0.25, // Use mana potions at this MP
|
||||||
spiritPotionSP: 0.30, // Use spirit gems/potions below this SP
|
spiritPotionSP: 0.30, // Use spirit gems/potions below this SP
|
||||||
spiritStanceOC: 80, // Activate Spirit Stance late (save SP for skills)
|
spiritStanceOC: 80, // Activate Spirit Stance late (save SP for skills)
|
||||||
skillOC: 75, // Use weapon skills at this OC
|
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
|
// — Out of battle
|
||||||
autoSell: true,
|
autoSell: true,
|
||||||
|
|
@ -778,40 +778,33 @@ function getBestDamageSpell() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Channeling maintenance ──
|
// ── Channeling maintenance ──
|
||||||
|
//
|
||||||
// Cheapest mana-costing spells to trigger channeling when it wears off
|
// Channeling: procs randomly when casting any mana-costing spell.
|
||||||
const CHANNEL_TRIGGERS = ['Fiery Blast', 'Freeze', 'Shockblast', 'Gale'];
|
// 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() {
|
function kickstartChanneling() {
|
||||||
if (STATE.channeling) return null;
|
if (STATE.channeling) return null;
|
||||||
|
|
||||||
// Priority 1: refresh any buff that's about to expire (< 15 turns)
|
// Priority: cast or refresh a buff with a good MP cost for proc chance
|
||||||
// This is more useful than wasting MP on attack spells
|
// 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) {
|
for (const b of BUFF_PRIORITY) {
|
||||||
const dur = buffDuration(b.icon);
|
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;
|
if (b.icon === 'regen' && STATE.hp >= CFG.cureRegenHP) continue;
|
||||||
return { type: 'spell', name: b.spell, selfTarget: true };
|
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;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,16 +14,16 @@ const CFG = {
|
||||||
autoCure: true,
|
autoCure: true,
|
||||||
autoSpirit: false,
|
autoSpirit: false,
|
||||||
|
|
||||||
// — Thresholds (benchmark-optimized)
|
// — Thresholds (simulation-backed)
|
||||||
cureHP: 0.45, // Cast Cure below this HP
|
cureHP: 0.45, // Cast Cure below this HP
|
||||||
cureItemHP: 0.50, // Use health items at this HP
|
cureItemHP: 0.50, // Use health items at this HP
|
||||||
cureRegenHP: 0.65, // Cast Regen below this HP
|
cureRegenHP: 0.65, // Cast Regen below this HP
|
||||||
manaGemMP: 0.70, // Use mana gems below this MP (aggressive, stay rich)
|
manaGemMP: 0.45, // Use mana gems below this MP
|
||||||
manaPotionMP: 0.50, // Use mana potions at this MP
|
manaPotionMP: 0.25, // Use mana potions at this MP
|
||||||
spiritPotionSP: 0.30, // Use spirit gems/potions below this SP
|
spiritPotionSP: 0.30, // Use spirit gems/potions below this SP
|
||||||
spiritStanceOC: 80, // Activate Spirit Stance late (save SP for skills)
|
spiritStanceOC: 80, // Activate Spirit Stance late (save SP for skills)
|
||||||
skillOC: 75, // Use weapon skills at this OC
|
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
|
// — Out of battle
|
||||||
autoSell: true,
|
autoSell: true,
|
||||||
|
|
|
||||||
|
|
@ -133,40 +133,33 @@ function getBestDamageSpell() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Channeling maintenance ──
|
// ── Channeling maintenance ──
|
||||||
|
//
|
||||||
// Cheapest mana-costing spells to trigger channeling when it wears off
|
// Channeling: procs randomly when casting any mana-costing spell.
|
||||||
const CHANNEL_TRIGGERS = ['Fiery Blast', 'Freeze', 'Shockblast', 'Gale'];
|
// 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() {
|
function kickstartChanneling() {
|
||||||
if (STATE.channeling) return null;
|
if (STATE.channeling) return null;
|
||||||
|
|
||||||
// Priority 1: refresh any buff that's about to expire (< 15 turns)
|
// Priority: cast or refresh a buff with a good MP cost for proc chance
|
||||||
// This is more useful than wasting MP on attack spells
|
// 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) {
|
for (const b of BUFF_PRIORITY) {
|
||||||
const dur = buffDuration(b.icon);
|
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;
|
if (b.icon === 'regen' && STATE.hp >= CFG.cureRegenHP) continue;
|
||||||
return { type: 'spell', name: b.spell, selfTarget: true };
|
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;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue