Fix: Spark of Life icon key, mana items after buffs
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
This commit is contained in:
parent
d9fc1e547c
commit
b69eabd1eb
4 changed files with 321 additions and 315 deletions
|
|
@ -679,10 +679,9 @@ function shouldBuff(name, minTurns) {
|
|||
const BUFF_PRIORITY = [
|
||||
{ icon: 'haste', spell: 'Haste', minTurns: 2 },
|
||||
{ icon: 'protection', spell: 'Protection', minTurns: 2 },
|
||||
{ icon: 'spark_of_life', spell: 'Spark of Life', minTurns: 1 },
|
||||
{ icon: 'fallenshield', spell: 'Spark of Life', minTurns: 1 }, // icon: fallenshield.png
|
||||
{ icon: 'regen', spell: 'Regen', minTurns: 2 },
|
||||
{ icon: 'absorb', spell: 'Absorb', minTurns: 2 },
|
||||
// Shadow Veil: icon key = lowercased spell name without spaces
|
||||
{ icon: 'shadowveil', spell: 'Shadow Veil', minTurns: 3 },
|
||||
];
|
||||
|
||||
|
|
@ -844,23 +843,13 @@ function strategyNovice() {
|
|||
if (gem && !STATE.channeling)
|
||||
return { type: 'item', id: gem.id, selfTarget: true };
|
||||
|
||||
// 1. Gems proactively — don't hoard
|
||||
// 1. Health items — safe to use early
|
||||
if (STATE.hp < CFG.cureItemHP) {
|
||||
const gem = hasUsableGem('heal');
|
||||
if (gem) return { type: 'item', id: gem.id, selfTarget: true };
|
||||
}
|
||||
// Mana gem — use when MP < 50%
|
||||
if (STATE.mp < CFG.manaGemMP) {
|
||||
const gem = hasUsableGem('mana');
|
||||
if (gem) return { type: 'item', id: gem.id, selfTarget: true };
|
||||
}
|
||||
// Spirit gem — use when SP is low (keeps Spirit Stance available)
|
||||
if (STATE.sp < CFG.spiritPotionSP) {
|
||||
const gem = hasUsableGem('spirit');
|
||||
if (gem) return { type: 'item', id: gem.id, selfTarget: true };
|
||||
}
|
||||
|
||||
// 2. Cure only if items aren't available and HP is critical
|
||||
// 2. Cure
|
||||
if (STATE.hp < CFG.cureHP) {
|
||||
const c = hasSpell('Full-Cure') ? 'Full-Cure' : hasSpell('Cure') ? 'Cure' : null;
|
||||
if (c) return { type: 'spell', name: c, selfTarget: true };
|
||||
|
|
@ -869,12 +858,22 @@ function strategyNovice() {
|
|||
// 3. Buffs — cast if missing or about to expire
|
||||
for (const b of BUFF_PRIORITY) {
|
||||
if (shouldBuff(b.icon, b.minTurns) && STATE.mp > 0.2 && hasSpell(b.spell)) {
|
||||
// Regen is hp-dependent: only cast if HP is below threshold
|
||||
if (b.icon === 'regen' && STATE.hp >= CFG.cureRegenHP) continue;
|
||||
return { type: 'spell', name: b.spell, selfTarget: true };
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Mana/spirit items — AFTER buffs, so we don't waste a potion
|
||||
// before casting spells
|
||||
if (STATE.mp < CFG.manaGemMP) {
|
||||
const gem = hasUsableGem('mana');
|
||||
if (gem) return { type: 'item', id: gem.id, selfTarget: true };
|
||||
}
|
||||
if (STATE.sp < CFG.spiritPotionSP) {
|
||||
const gem = hasUsableGem('spirit');
|
||||
if (gem) return { type: 'item', id: gem.id, selfTarget: true };
|
||||
}
|
||||
|
||||
// 5. Spirit Stance at OC 60 — but only if we have SP to sustain it
|
||||
if (STATE.oc >= 60 && !STATE.spiritStance && STATE.sp > 0.10)
|
||||
return { type: 'toggle_spirit' };
|
||||
|
|
@ -954,20 +953,11 @@ function strategyAdept() {
|
|||
if (gem && !STATE.channeling)
|
||||
return { type: 'item', id: gem.id, selfTarget: true };
|
||||
|
||||
// 1. Gems proactively — don't hoard
|
||||
// 1. Health items — safe to use early
|
||||
if (STATE.hp < CFG.cureItemHP) {
|
||||
const gem = hasUsableGem('heal');
|
||||
if (gem) return { type: 'item', id: gem.id, selfTarget: true };
|
||||
}
|
||||
if (STATE.mp < CFG.manaGemMP) {
|
||||
const gem = hasUsableGem('mana');
|
||||
if (gem) return { type: 'item', id: gem.id, selfTarget: true };
|
||||
}
|
||||
// Spirit gem — use when SP is low
|
||||
if (STATE.sp < CFG.spiritPotionSP) {
|
||||
const gem = hasUsableGem('spirit');
|
||||
if (gem) return { type: 'item', id: gem.id, selfTarget: true };
|
||||
}
|
||||
|
||||
// 2. Cure
|
||||
if (STATE.hp < CFG.cureHP) {
|
||||
|
|
@ -975,7 +965,7 @@ function strategyAdept() {
|
|||
if (c) return { type: 'spell', name: c, selfTarget: true };
|
||||
}
|
||||
|
||||
// 3. Buffs — prioritize by remaining duration (lowest = most urgent)
|
||||
// 3. Buffs
|
||||
for (const b of BUFF_PRIORITY) {
|
||||
if (shouldBuff(b.icon, b.minTurns) && STATE.mp > 0.2 && hasSpell(b.spell)) {
|
||||
if (b.icon === 'regen' && STATE.hp >= CFG.cureRegenHP) continue;
|
||||
|
|
@ -983,6 +973,16 @@ function strategyAdept() {
|
|||
}
|
||||
}
|
||||
|
||||
// 4. Mana/spirit items — AFTER buffs
|
||||
if (STATE.mp < CFG.manaGemMP) {
|
||||
const gem = hasUsableGem('mana');
|
||||
if (gem) return { type: 'item', id: gem.id, selfTarget: true };
|
||||
}
|
||||
if (STATE.sp < CFG.spiritPotionSP) {
|
||||
const gem = hasUsableGem('spirit');
|
||||
if (gem) return { type: 'item', id: gem.id, selfTarget: true };
|
||||
}
|
||||
|
||||
// 4. Debuffs — only on important fights (rare/boss/ultimate monsters)
|
||||
// In random mob fights, just attack through them
|
||||
if (anyRareMonster() || STATE.monsters.length <= 2) {
|
||||
|
|
@ -1083,19 +1083,11 @@ function strategyVeteran() {
|
|||
if (gem && !STATE.channeling)
|
||||
return { type: 'item', id: gem.id, selfTarget: true };
|
||||
|
||||
// 1. Gems proactively — don't hoard, use at reasonable thresholds
|
||||
// 1. Health items — safe to use early
|
||||
if (STATE.hp < 0.60) {
|
||||
const gem = hasUsableGem('heal');
|
||||
if (gem) return { type: 'item', id: gem.id, selfTarget: true };
|
||||
}
|
||||
if (STATE.mp < CFG.manaGemMP) {
|
||||
const gem = hasUsableGem('mana');
|
||||
if (gem) return { type: 'item', id: gem.id, selfTarget: true };
|
||||
}
|
||||
if (STATE.sp < CFG.spiritPotionSP) {
|
||||
const gem = hasUsableGem('spirit');
|
||||
if (gem) return { type: 'item', id: gem.id, selfTarget: true };
|
||||
}
|
||||
|
||||
// 2. Cure
|
||||
if (STATE.hp < CFG.cureHP) {
|
||||
|
|
@ -1103,7 +1095,7 @@ function strategyVeteran() {
|
|||
if (c) return { type: 'spell', name: c, selfTarget: true };
|
||||
}
|
||||
|
||||
// 3. Buffs — prioritize by remaining duration
|
||||
// 3. Buffs
|
||||
if (CFG.autoBuff) {
|
||||
for (const b of BUFF_PRIORITY) {
|
||||
if (shouldBuff(b.icon, b.minTurns) && STATE.mp > 0.2 && hasSpell(b.spell)) {
|
||||
|
|
@ -1113,6 +1105,16 @@ function strategyVeteran() {
|
|||
}
|
||||
}
|
||||
|
||||
// 4. Mana/spirit items — AFTER buffs
|
||||
if (STATE.mp < CFG.manaGemMP) {
|
||||
const gem = hasUsableGem('mana');
|
||||
if (gem) return { type: 'item', id: gem.id, selfTarget: true };
|
||||
}
|
||||
if (STATE.sp < CFG.spiritPotionSP) {
|
||||
const gem = hasUsableGem('spirit');
|
||||
if (gem) return { type: 'item', id: gem.id, selfTarget: true };
|
||||
}
|
||||
|
||||
// 4. Debuffs — only on important fights (rare/boss)
|
||||
if (CFG.autoDebuff && STATE.mp > 0.2 && (anyRareMonster() || STATE.monsters.length <= 2)) {
|
||||
if (hasSpell('Imperil')) {
|
||||
|
|
|
|||
|
|
@ -679,10 +679,9 @@ function shouldBuff(name, minTurns) {
|
|||
const BUFF_PRIORITY = [
|
||||
{ icon: 'haste', spell: 'Haste', minTurns: 2 },
|
||||
{ icon: 'protection', spell: 'Protection', minTurns: 2 },
|
||||
{ icon: 'spark_of_life', spell: 'Spark of Life', minTurns: 1 },
|
||||
{ icon: 'fallenshield', spell: 'Spark of Life', minTurns: 1 }, // icon: fallenshield.png
|
||||
{ icon: 'regen', spell: 'Regen', minTurns: 2 },
|
||||
{ icon: 'absorb', spell: 'Absorb', minTurns: 2 },
|
||||
// Shadow Veil: icon key = lowercased spell name without spaces
|
||||
{ icon: 'shadowveil', spell: 'Shadow Veil', minTurns: 3 },
|
||||
];
|
||||
|
||||
|
|
@ -844,23 +843,13 @@ function strategyNovice() {
|
|||
if (gem && !STATE.channeling)
|
||||
return { type: 'item', id: gem.id, selfTarget: true };
|
||||
|
||||
// 1. Gems proactively — don't hoard
|
||||
// 1. Health items — safe to use early
|
||||
if (STATE.hp < CFG.cureItemHP) {
|
||||
const gem = hasUsableGem('heal');
|
||||
if (gem) return { type: 'item', id: gem.id, selfTarget: true };
|
||||
}
|
||||
// Mana gem — use when MP < 50%
|
||||
if (STATE.mp < CFG.manaGemMP) {
|
||||
const gem = hasUsableGem('mana');
|
||||
if (gem) return { type: 'item', id: gem.id, selfTarget: true };
|
||||
}
|
||||
// Spirit gem — use when SP is low (keeps Spirit Stance available)
|
||||
if (STATE.sp < CFG.spiritPotionSP) {
|
||||
const gem = hasUsableGem('spirit');
|
||||
if (gem) return { type: 'item', id: gem.id, selfTarget: true };
|
||||
}
|
||||
|
||||
// 2. Cure only if items aren't available and HP is critical
|
||||
// 2. Cure
|
||||
if (STATE.hp < CFG.cureHP) {
|
||||
const c = hasSpell('Full-Cure') ? 'Full-Cure' : hasSpell('Cure') ? 'Cure' : null;
|
||||
if (c) return { type: 'spell', name: c, selfTarget: true };
|
||||
|
|
@ -869,12 +858,22 @@ function strategyNovice() {
|
|||
// 3. Buffs — cast if missing or about to expire
|
||||
for (const b of BUFF_PRIORITY) {
|
||||
if (shouldBuff(b.icon, b.minTurns) && STATE.mp > 0.2 && hasSpell(b.spell)) {
|
||||
// Regen is hp-dependent: only cast if HP is below threshold
|
||||
if (b.icon === 'regen' && STATE.hp >= CFG.cureRegenHP) continue;
|
||||
return { type: 'spell', name: b.spell, selfTarget: true };
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Mana/spirit items — AFTER buffs, so we don't waste a potion
|
||||
// before casting spells
|
||||
if (STATE.mp < CFG.manaGemMP) {
|
||||
const gem = hasUsableGem('mana');
|
||||
if (gem) return { type: 'item', id: gem.id, selfTarget: true };
|
||||
}
|
||||
if (STATE.sp < CFG.spiritPotionSP) {
|
||||
const gem = hasUsableGem('spirit');
|
||||
if (gem) return { type: 'item', id: gem.id, selfTarget: true };
|
||||
}
|
||||
|
||||
// 5. Spirit Stance at OC 60 — but only if we have SP to sustain it
|
||||
if (STATE.oc >= 60 && !STATE.spiritStance && STATE.sp > 0.10)
|
||||
return { type: 'toggle_spirit' };
|
||||
|
|
@ -954,20 +953,11 @@ function strategyAdept() {
|
|||
if (gem && !STATE.channeling)
|
||||
return { type: 'item', id: gem.id, selfTarget: true };
|
||||
|
||||
// 1. Gems proactively — don't hoard
|
||||
// 1. Health items — safe to use early
|
||||
if (STATE.hp < CFG.cureItemHP) {
|
||||
const gem = hasUsableGem('heal');
|
||||
if (gem) return { type: 'item', id: gem.id, selfTarget: true };
|
||||
}
|
||||
if (STATE.mp < CFG.manaGemMP) {
|
||||
const gem = hasUsableGem('mana');
|
||||
if (gem) return { type: 'item', id: gem.id, selfTarget: true };
|
||||
}
|
||||
// Spirit gem — use when SP is low
|
||||
if (STATE.sp < CFG.spiritPotionSP) {
|
||||
const gem = hasUsableGem('spirit');
|
||||
if (gem) return { type: 'item', id: gem.id, selfTarget: true };
|
||||
}
|
||||
|
||||
// 2. Cure
|
||||
if (STATE.hp < CFG.cureHP) {
|
||||
|
|
@ -975,7 +965,7 @@ function strategyAdept() {
|
|||
if (c) return { type: 'spell', name: c, selfTarget: true };
|
||||
}
|
||||
|
||||
// 3. Buffs — prioritize by remaining duration (lowest = most urgent)
|
||||
// 3. Buffs
|
||||
for (const b of BUFF_PRIORITY) {
|
||||
if (shouldBuff(b.icon, b.minTurns) && STATE.mp > 0.2 && hasSpell(b.spell)) {
|
||||
if (b.icon === 'regen' && STATE.hp >= CFG.cureRegenHP) continue;
|
||||
|
|
@ -983,6 +973,16 @@ function strategyAdept() {
|
|||
}
|
||||
}
|
||||
|
||||
// 4. Mana/spirit items — AFTER buffs
|
||||
if (STATE.mp < CFG.manaGemMP) {
|
||||
const gem = hasUsableGem('mana');
|
||||
if (gem) return { type: 'item', id: gem.id, selfTarget: true };
|
||||
}
|
||||
if (STATE.sp < CFG.spiritPotionSP) {
|
||||
const gem = hasUsableGem('spirit');
|
||||
if (gem) return { type: 'item', id: gem.id, selfTarget: true };
|
||||
}
|
||||
|
||||
// 4. Debuffs — only on important fights (rare/boss/ultimate monsters)
|
||||
// In random mob fights, just attack through them
|
||||
if (anyRareMonster() || STATE.monsters.length <= 2) {
|
||||
|
|
@ -1083,19 +1083,11 @@ function strategyVeteran() {
|
|||
if (gem && !STATE.channeling)
|
||||
return { type: 'item', id: gem.id, selfTarget: true };
|
||||
|
||||
// 1. Gems proactively — don't hoard, use at reasonable thresholds
|
||||
// 1. Health items — safe to use early
|
||||
if (STATE.hp < 0.60) {
|
||||
const gem = hasUsableGem('heal');
|
||||
if (gem) return { type: 'item', id: gem.id, selfTarget: true };
|
||||
}
|
||||
if (STATE.mp < CFG.manaGemMP) {
|
||||
const gem = hasUsableGem('mana');
|
||||
if (gem) return { type: 'item', id: gem.id, selfTarget: true };
|
||||
}
|
||||
if (STATE.sp < CFG.spiritPotionSP) {
|
||||
const gem = hasUsableGem('spirit');
|
||||
if (gem) return { type: 'item', id: gem.id, selfTarget: true };
|
||||
}
|
||||
|
||||
// 2. Cure
|
||||
if (STATE.hp < CFG.cureHP) {
|
||||
|
|
@ -1103,7 +1095,7 @@ function strategyVeteran() {
|
|||
if (c) return { type: 'spell', name: c, selfTarget: true };
|
||||
}
|
||||
|
||||
// 3. Buffs — prioritize by remaining duration
|
||||
// 3. Buffs
|
||||
if (CFG.autoBuff) {
|
||||
for (const b of BUFF_PRIORITY) {
|
||||
if (shouldBuff(b.icon, b.minTurns) && STATE.mp > 0.2 && hasSpell(b.spell)) {
|
||||
|
|
@ -1113,6 +1105,16 @@ function strategyVeteran() {
|
|||
}
|
||||
}
|
||||
|
||||
// 4. Mana/spirit items — AFTER buffs
|
||||
if (STATE.mp < CFG.manaGemMP) {
|
||||
const gem = hasUsableGem('mana');
|
||||
if (gem) return { type: 'item', id: gem.id, selfTarget: true };
|
||||
}
|
||||
if (STATE.sp < CFG.spiritPotionSP) {
|
||||
const gem = hasUsableGem('spirit');
|
||||
if (gem) return { type: 'item', id: gem.id, selfTarget: true };
|
||||
}
|
||||
|
||||
// 4. Debuffs — only on important fights (rare/boss)
|
||||
if (CFG.autoDebuff && STATE.mp > 0.2 && (anyRareMonster() || STATE.monsters.length <= 2)) {
|
||||
if (hasSpell('Imperil')) {
|
||||
|
|
|
|||
|
|
@ -27,241 +27,241 @@
|
|||
{
|
||||
"name": "chan-mp-focused",
|
||||
"desc": "Channeling + aggressive mana gem use",
|
||||
"avgRounds": "149.0",
|
||||
"avgKills": "835.2",
|
||||
"avgRounds": "151.8",
|
||||
"avgKills": "846.8",
|
||||
"avgDmgRatio": "2.9",
|
||||
"avgDmgPerRound": 4304,
|
||||
"avgExp": 4488560,
|
||||
"avgCredits": 58783,
|
||||
"maxRounds": 162,
|
||||
"avgDmgPerRound": 4310,
|
||||
"avgExp": 4682000,
|
||||
"avgCredits": 62540,
|
||||
"maxRounds": 171,
|
||||
"survivalRate": "0",
|
||||
"deaths": 5,
|
||||
"score": 83520
|
||||
},
|
||||
{
|
||||
"name": "chan-no-buff-refresh",
|
||||
"desc": "Only cast missing buffs, never refresh early",
|
||||
"avgRounds": "143.0",
|
||||
"avgKills": "794.4",
|
||||
"avgDmgRatio": "3.1",
|
||||
"avgDmgPerRound": 4252,
|
||||
"avgExp": 4141360,
|
||||
"avgCredits": 54902,
|
||||
"maxRounds": 157,
|
||||
"survivalRate": "0",
|
||||
"deaths": 5,
|
||||
"score": 79440
|
||||
},
|
||||
{
|
||||
"name": "chan-aggressive-buff",
|
||||
"desc": "Channeling via buffs at 20+ turns, keep all buffs fresh",
|
||||
"avgRounds": "142.4",
|
||||
"avgKills": "790.2",
|
||||
"avgDmgRatio": "3.2",
|
||||
"avgDmgPerRound": 4440,
|
||||
"avgExp": 4093520,
|
||||
"avgCredits": 51455,
|
||||
"maxRounds": 149,
|
||||
"survivalRate": "0",
|
||||
"deaths": 5,
|
||||
"score": 79020
|
||||
},
|
||||
{
|
||||
"name": "chan-buff-only",
|
||||
"desc": "Channeling via buff refresh only, no attack spells",
|
||||
"avgRounds": "140.6",
|
||||
"avgKills": "777.0",
|
||||
"avgDmgRatio": "3.3",
|
||||
"avgDmgPerRound": 4500,
|
||||
"avgExp": 4052960,
|
||||
"avgCredits": 54172,
|
||||
"maxRounds": 166,
|
||||
"survivalRate": "0",
|
||||
"deaths": 5,
|
||||
"score": 77700
|
||||
},
|
||||
{
|
||||
"name": "conservative-items",
|
||||
"desc": "Save items, use Cure more",
|
||||
"avgRounds": "139.8",
|
||||
"avgKills": "773.0",
|
||||
"avgDmgRatio": "3.3",
|
||||
"avgDmgPerRound": 4480,
|
||||
"avgExp": 3997360,
|
||||
"avgCredits": 53493,
|
||||
"maxRounds": 163,
|
||||
"survivalRate": "0",
|
||||
"deaths": 5,
|
||||
"score": 77300
|
||||
},
|
||||
{
|
||||
"name": "full-conservative",
|
||||
"desc": "Late spirit, save items, no attack spells",
|
||||
"avgRounds": "136.8",
|
||||
"avgKills": "759.0",
|
||||
"avgDmgRatio": "3.3",
|
||||
"avgDmgPerRound": 4413,
|
||||
"avgExp": 3777680,
|
||||
"avgCredits": 47629,
|
||||
"maxRounds": 148,
|
||||
"survivalRate": "0",
|
||||
"deaths": 5,
|
||||
"score": 75900
|
||||
},
|
||||
{
|
||||
"name": "chan-cheap-spell",
|
||||
"desc": "Channeling via cheapest attack spell (4 MP)",
|
||||
"avgRounds": "137.4",
|
||||
"avgKills": "757.8",
|
||||
"avgDmgRatio": "3.1",
|
||||
"avgDmgPerRound": 4250,
|
||||
"avgExp": 3854800,
|
||||
"avgCredits": 52097,
|
||||
"maxRounds": 162,
|
||||
"survivalRate": "0",
|
||||
"deaths": 5,
|
||||
"score": 75780
|
||||
},
|
||||
{
|
||||
"name": "chan-spirit-60",
|
||||
"desc": "Channeling + spirit stance at 60",
|
||||
"avgRounds": "136.8",
|
||||
"avgKills": "755.4",
|
||||
"avgDmgRatio": "3.3",
|
||||
"avgDmgPerRound": 4216,
|
||||
"avgExp": 3785840,
|
||||
"avgCredits": 51913,
|
||||
"maxRounds": 153,
|
||||
"survivalRate": "0",
|
||||
"deaths": 5,
|
||||
"score": 75540
|
||||
"score": 84680
|
||||
},
|
||||
{
|
||||
"name": "chan-always-regen",
|
||||
"desc": "Channeling: always cast Regen first if not active",
|
||||
"avgRounds": "134.2",
|
||||
"avgKills": "739.8",
|
||||
"avgDmgRatio": "3.4",
|
||||
"avgDmgPerRound": 4441,
|
||||
"avgExp": 3694400,
|
||||
"avgCredits": 51225,
|
||||
"maxRounds": 158,
|
||||
"survivalRate": "0",
|
||||
"deaths": 5,
|
||||
"score": 73980
|
||||
},
|
||||
{
|
||||
"name": "baseline",
|
||||
"desc": "Current v0.11.0 defaults",
|
||||
"avgRounds": "130.0",
|
||||
"avgKills": "712.8",
|
||||
"avgDmgRatio": "3.5",
|
||||
"avgDmgPerRound": 4371,
|
||||
"avgExp": 3440240,
|
||||
"avgCredits": 46973,
|
||||
"avgRounds": "143.2",
|
||||
"avgKills": "796.6",
|
||||
"avgDmgRatio": "3.3",
|
||||
"avgDmgPerRound": 4478,
|
||||
"avgExp": 4137360,
|
||||
"avgCredits": 53088,
|
||||
"maxRounds": 152,
|
||||
"survivalRate": "0",
|
||||
"deaths": 5,
|
||||
"score": 71280
|
||||
"score": 79660
|
||||
},
|
||||
{
|
||||
"name": "spirit-80",
|
||||
"desc": "Spirit Stance at OC 80 (late, skills first)",
|
||||
"avgRounds": "127.8",
|
||||
"avgKills": "698.6",
|
||||
"avgDmgRatio": "3.8",
|
||||
"avgDmgPerRound": 4351,
|
||||
"avgExp": 3331760,
|
||||
"avgCredits": 44061,
|
||||
"maxRounds": 149,
|
||||
"name": "conservative-items",
|
||||
"desc": "Save items, use Cure more",
|
||||
"avgRounds": "141.8",
|
||||
"avgKills": "787.6",
|
||||
"avgDmgRatio": "3.2",
|
||||
"avgDmgPerRound": 4457,
|
||||
"avgExp": 4090640,
|
||||
"avgCredits": 55015,
|
||||
"maxRounds": 162,
|
||||
"survivalRate": "0",
|
||||
"deaths": 5,
|
||||
"score": 69860
|
||||
"score": 78760
|
||||
},
|
||||
{
|
||||
"name": "chan-buff-only",
|
||||
"desc": "Channeling via buff refresh only, no attack spells",
|
||||
"avgRounds": "141.6",
|
||||
"avgKills": "786.4",
|
||||
"avgDmgRatio": "3.3",
|
||||
"avgDmgPerRound": 4471,
|
||||
"avgExp": 4058400,
|
||||
"avgCredits": 55880,
|
||||
"maxRounds": 154,
|
||||
"survivalRate": "0",
|
||||
"deaths": 5,
|
||||
"score": 78640
|
||||
},
|
||||
{
|
||||
"name": "chan-spirit-60",
|
||||
"desc": "Channeling + spirit stance at 60",
|
||||
"avgRounds": "141.0",
|
||||
"avgKills": "783.8",
|
||||
"avgDmgRatio": "3.1",
|
||||
"avgDmgPerRound": 4147,
|
||||
"avgExp": 4022400,
|
||||
"avgCredits": 53634,
|
||||
"maxRounds": 159,
|
||||
"survivalRate": "0",
|
||||
"deaths": 5,
|
||||
"score": 78380
|
||||
},
|
||||
{
|
||||
"name": "chan-aggressive-buff",
|
||||
"desc": "Channeling via buffs at 20+ turns, keep all buffs fresh",
|
||||
"avgRounds": "140.8",
|
||||
"avgKills": "781.0",
|
||||
"avgDmgRatio": "3.4",
|
||||
"avgDmgPerRound": 4446,
|
||||
"avgExp": 4092800,
|
||||
"avgCredits": 55232,
|
||||
"maxRounds": 173,
|
||||
"survivalRate": "0",
|
||||
"deaths": 5,
|
||||
"score": 78100
|
||||
},
|
||||
{
|
||||
"name": "chan-cheap-spell",
|
||||
"desc": "Channeling via cheapest attack spell (4 MP)",
|
||||
"avgRounds": "140.6",
|
||||
"avgKills": "778.2",
|
||||
"avgDmgRatio": "3.0",
|
||||
"avgDmgPerRound": 4246,
|
||||
"avgExp": 4041760,
|
||||
"avgCredits": 53287,
|
||||
"maxRounds": 161,
|
||||
"survivalRate": "0",
|
||||
"deaths": 5,
|
||||
"score": 77820
|
||||
},
|
||||
{
|
||||
"name": "full-conservative",
|
||||
"desc": "Late spirit, save items, no attack spells",
|
||||
"avgRounds": "140.0",
|
||||
"avgKills": "776.0",
|
||||
"avgDmgRatio": "3.3",
|
||||
"avgDmgPerRound": 4379,
|
||||
"avgExp": 3977920,
|
||||
"avgCredits": 52946,
|
||||
"maxRounds": 155,
|
||||
"survivalRate": "0",
|
||||
"deaths": 5,
|
||||
"score": 77600
|
||||
},
|
||||
{
|
||||
"name": "chan-no-buff-refresh",
|
||||
"desc": "Only cast missing buffs, never refresh early",
|
||||
"avgRounds": "136.4",
|
||||
"avgKills": "754.0",
|
||||
"avgDmgRatio": "3.3",
|
||||
"avgDmgPerRound": 4239,
|
||||
"avgExp": 3764560,
|
||||
"avgCredits": 49857,
|
||||
"maxRounds": 146,
|
||||
"survivalRate": "0",
|
||||
"deaths": 5,
|
||||
"score": 75400
|
||||
},
|
||||
{
|
||||
"name": "attack-spells",
|
||||
"desc": "Use cheap attack spells for channeling",
|
||||
"avgRounds": "130.2",
|
||||
"avgKills": "718.0",
|
||||
"avgDmgRatio": "3.4",
|
||||
"avgDmgPerRound": 4199,
|
||||
"avgExp": 3463600,
|
||||
"avgCredits": 44691,
|
||||
"maxRounds": 150,
|
||||
"survivalRate": "0",
|
||||
"deaths": 5,
|
||||
"score": 71800
|
||||
},
|
||||
{
|
||||
"name": "spirit-80",
|
||||
"desc": "Spirit Stance at OC 80 (late, skills first)",
|
||||
"avgRounds": "126.8",
|
||||
"avgKills": "692.2",
|
||||
"avgDmgRatio": "3.5",
|
||||
"avgDmgPerRound": 4209,
|
||||
"avgExp": 3274880,
|
||||
"avgCredits": 42980,
|
||||
"maxRounds": 152,
|
||||
"survivalRate": "0",
|
||||
"deaths": 5,
|
||||
"score": 69220
|
||||
},
|
||||
{
|
||||
"name": "skills-at-60",
|
||||
"desc": "Skills fire at OC 60 instead of 80",
|
||||
"avgRounds": "125.6",
|
||||
"avgKills": "685.0",
|
||||
"avgKills": "691.8",
|
||||
"avgDmgRatio": "3.6",
|
||||
"avgDmgPerRound": 4469,
|
||||
"avgExp": 3200400,
|
||||
"avgCredits": 41273,
|
||||
"maxRounds": 137,
|
||||
"avgDmgPerRound": 4309,
|
||||
"avgExp": 3283360,
|
||||
"avgCredits": 42557,
|
||||
"maxRounds": 147,
|
||||
"survivalRate": "0",
|
||||
"deaths": 5,
|
||||
"score": 68500
|
||||
},
|
||||
{
|
||||
"name": "spirit-40",
|
||||
"desc": "Spirit Stance at OC 40 (early)",
|
||||
"avgRounds": "117.8",
|
||||
"avgKills": "637.2",
|
||||
"avgDmgRatio": "4.3",
|
||||
"avgDmgPerRound": 4401,
|
||||
"avgExp": 2806160,
|
||||
"avgCredits": 36809,
|
||||
"maxRounds": 122,
|
||||
"survivalRate": "0",
|
||||
"deaths": 5,
|
||||
"score": 63720
|
||||
},
|
||||
{
|
||||
"name": "full-aggressive",
|
||||
"desc": "Early spirit + aggressive items + attack spells",
|
||||
"avgRounds": "117.2",
|
||||
"avgKills": "632.6",
|
||||
"avgDmgRatio": "4.0",
|
||||
"avgDmgPerRound": 4226,
|
||||
"avgExp": 2801680,
|
||||
"avgCredits": 38420,
|
||||
"maxRounds": 141,
|
||||
"survivalRate": "0",
|
||||
"deaths": 5,
|
||||
"score": 63260
|
||||
},
|
||||
{
|
||||
"name": "aggressive-items",
|
||||
"desc": "Use items very proactively",
|
||||
"avgRounds": "116.2",
|
||||
"avgKills": "629.0",
|
||||
"avgDmgRatio": "4.0",
|
||||
"avgDmgPerRound": 4302,
|
||||
"avgExp": 2730480,
|
||||
"avgCredits": 36126,
|
||||
"maxRounds": 125,
|
||||
"survivalRate": "0",
|
||||
"deaths": 5,
|
||||
"score": 62900
|
||||
"score": 69180
|
||||
},
|
||||
{
|
||||
"name": "balanced",
|
||||
"desc": "Mid spirit, moderate items, attack spells for chan",
|
||||
"avgRounds": "115.2",
|
||||
"avgKills": "623.6",
|
||||
"avgDmgRatio": "4.0",
|
||||
"avgDmgPerRound": 4097,
|
||||
"avgExp": 2690320,
|
||||
"avgCredits": 35489,
|
||||
"maxRounds": 126,
|
||||
"avgRounds": "126.4",
|
||||
"avgKills": "689.2",
|
||||
"avgDmgRatio": "3.4",
|
||||
"avgDmgPerRound": 4106,
|
||||
"avgExp": 3240560,
|
||||
"avgCredits": 42024,
|
||||
"maxRounds": 138,
|
||||
"survivalRate": "0",
|
||||
"deaths": 5,
|
||||
"score": 62360
|
||||
"score": 68920
|
||||
},
|
||||
{
|
||||
"name": "skills-at-60",
|
||||
"desc": "Skills fire at OC 60 instead of 80",
|
||||
"avgRounds": "126.4",
|
||||
"avgKills": "687.4",
|
||||
"avgDmgRatio": "3.7",
|
||||
"avgDmgPerRound": 4300,
|
||||
"avgExp": 3273680,
|
||||
"avgCredits": 42465,
|
||||
"maxRounds": 147,
|
||||
"survivalRate": "0",
|
||||
"deaths": 5,
|
||||
"score": 68740
|
||||
},
|
||||
{
|
||||
"name": "spirit-40",
|
||||
"desc": "Spirit Stance at OC 40 (early)",
|
||||
"avgRounds": "124.2",
|
||||
"avgKills": "675.4",
|
||||
"avgDmgRatio": "3.9",
|
||||
"avgDmgPerRound": 4354,
|
||||
"avgExp": 3141920,
|
||||
"avgCredits": 42525,
|
||||
"maxRounds": 147,
|
||||
"survivalRate": "0",
|
||||
"deaths": 5,
|
||||
"score": 67540
|
||||
},
|
||||
{
|
||||
"name": "baseline",
|
||||
"desc": "Current v0.11.0 defaults",
|
||||
"avgRounds": "121.8",
|
||||
"avgKills": "666.0",
|
||||
"avgDmgRatio": "3.8",
|
||||
"avgDmgPerRound": 4336,
|
||||
"avgExp": 3015760,
|
||||
"avgCredits": 40701,
|
||||
"maxRounds": 136,
|
||||
"survivalRate": "0",
|
||||
"deaths": 5,
|
||||
"score": 66600
|
||||
},
|
||||
{
|
||||
"name": "aggressive-items",
|
||||
"desc": "Use items very proactively",
|
||||
"avgRounds": "117.4",
|
||||
"avgKills": "631.4",
|
||||
"avgDmgRatio": "4.1",
|
||||
"avgDmgPerRound": 4289,
|
||||
"avgExp": 2803200,
|
||||
"avgCredits": 38028,
|
||||
"maxRounds": 136,
|
||||
"survivalRate": "0",
|
||||
"deaths": 5,
|
||||
"score": 63140
|
||||
},
|
||||
{
|
||||
"name": "full-aggressive",
|
||||
"desc": "Early spirit + aggressive items + attack spells",
|
||||
"avgRounds": "103.6",
|
||||
"avgKills": "550.6",
|
||||
"avgDmgRatio": "4.3",
|
||||
"avgDmgPerRound": 4083,
|
||||
"avgExp": 2205200,
|
||||
"avgCredits": 31050,
|
||||
"maxRounds": 129,
|
||||
"survivalRate": "0",
|
||||
"deaths": 5,
|
||||
"score": 55060
|
||||
}
|
||||
],
|
||||
"timestamp": "2026-07-21T18:12:28.425Z"
|
||||
"timestamp": "2026-07-21T18:46:13.922Z"
|
||||
}
|
||||
|
|
@ -34,10 +34,9 @@ function shouldBuff(name, minTurns) {
|
|||
const BUFF_PRIORITY = [
|
||||
{ icon: 'haste', spell: 'Haste', minTurns: 2 },
|
||||
{ icon: 'protection', spell: 'Protection', minTurns: 2 },
|
||||
{ icon: 'spark_of_life', spell: 'Spark of Life', minTurns: 1 },
|
||||
{ icon: 'fallenshield', spell: 'Spark of Life', minTurns: 1 }, // icon: fallenshield.png
|
||||
{ icon: 'regen', spell: 'Regen', minTurns: 2 },
|
||||
{ icon: 'absorb', spell: 'Absorb', minTurns: 2 },
|
||||
// Shadow Veil: icon key = lowercased spell name without spaces
|
||||
{ icon: 'shadowveil', spell: 'Shadow Veil', minTurns: 3 },
|
||||
];
|
||||
|
||||
|
|
@ -199,23 +198,13 @@ function strategyNovice() {
|
|||
if (gem && !STATE.channeling)
|
||||
return { type: 'item', id: gem.id, selfTarget: true };
|
||||
|
||||
// 1. Gems proactively — don't hoard
|
||||
// 1. Health items — safe to use early
|
||||
if (STATE.hp < CFG.cureItemHP) {
|
||||
const gem = hasUsableGem('heal');
|
||||
if (gem) return { type: 'item', id: gem.id, selfTarget: true };
|
||||
}
|
||||
// Mana gem — use when MP < 50%
|
||||
if (STATE.mp < CFG.manaGemMP) {
|
||||
const gem = hasUsableGem('mana');
|
||||
if (gem) return { type: 'item', id: gem.id, selfTarget: true };
|
||||
}
|
||||
// Spirit gem — use when SP is low (keeps Spirit Stance available)
|
||||
if (STATE.sp < CFG.spiritPotionSP) {
|
||||
const gem = hasUsableGem('spirit');
|
||||
if (gem) return { type: 'item', id: gem.id, selfTarget: true };
|
||||
}
|
||||
|
||||
// 2. Cure only if items aren't available and HP is critical
|
||||
// 2. Cure
|
||||
if (STATE.hp < CFG.cureHP) {
|
||||
const c = hasSpell('Full-Cure') ? 'Full-Cure' : hasSpell('Cure') ? 'Cure' : null;
|
||||
if (c) return { type: 'spell', name: c, selfTarget: true };
|
||||
|
|
@ -224,12 +213,22 @@ function strategyNovice() {
|
|||
// 3. Buffs — cast if missing or about to expire
|
||||
for (const b of BUFF_PRIORITY) {
|
||||
if (shouldBuff(b.icon, b.minTurns) && STATE.mp > 0.2 && hasSpell(b.spell)) {
|
||||
// Regen is hp-dependent: only cast if HP is below threshold
|
||||
if (b.icon === 'regen' && STATE.hp >= CFG.cureRegenHP) continue;
|
||||
return { type: 'spell', name: b.spell, selfTarget: true };
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Mana/spirit items — AFTER buffs, so we don't waste a potion
|
||||
// before casting spells
|
||||
if (STATE.mp < CFG.manaGemMP) {
|
||||
const gem = hasUsableGem('mana');
|
||||
if (gem) return { type: 'item', id: gem.id, selfTarget: true };
|
||||
}
|
||||
if (STATE.sp < CFG.spiritPotionSP) {
|
||||
const gem = hasUsableGem('spirit');
|
||||
if (gem) return { type: 'item', id: gem.id, selfTarget: true };
|
||||
}
|
||||
|
||||
// 5. Spirit Stance at OC 60 — but only if we have SP to sustain it
|
||||
if (STATE.oc >= 60 && !STATE.spiritStance && STATE.sp > 0.10)
|
||||
return { type: 'toggle_spirit' };
|
||||
|
|
@ -309,20 +308,11 @@ function strategyAdept() {
|
|||
if (gem && !STATE.channeling)
|
||||
return { type: 'item', id: gem.id, selfTarget: true };
|
||||
|
||||
// 1. Gems proactively — don't hoard
|
||||
// 1. Health items — safe to use early
|
||||
if (STATE.hp < CFG.cureItemHP) {
|
||||
const gem = hasUsableGem('heal');
|
||||
if (gem) return { type: 'item', id: gem.id, selfTarget: true };
|
||||
}
|
||||
if (STATE.mp < CFG.manaGemMP) {
|
||||
const gem = hasUsableGem('mana');
|
||||
if (gem) return { type: 'item', id: gem.id, selfTarget: true };
|
||||
}
|
||||
// Spirit gem — use when SP is low
|
||||
if (STATE.sp < CFG.spiritPotionSP) {
|
||||
const gem = hasUsableGem('spirit');
|
||||
if (gem) return { type: 'item', id: gem.id, selfTarget: true };
|
||||
}
|
||||
|
||||
// 2. Cure
|
||||
if (STATE.hp < CFG.cureHP) {
|
||||
|
|
@ -330,7 +320,7 @@ function strategyAdept() {
|
|||
if (c) return { type: 'spell', name: c, selfTarget: true };
|
||||
}
|
||||
|
||||
// 3. Buffs — prioritize by remaining duration (lowest = most urgent)
|
||||
// 3. Buffs
|
||||
for (const b of BUFF_PRIORITY) {
|
||||
if (shouldBuff(b.icon, b.minTurns) && STATE.mp > 0.2 && hasSpell(b.spell)) {
|
||||
if (b.icon === 'regen' && STATE.hp >= CFG.cureRegenHP) continue;
|
||||
|
|
@ -338,6 +328,16 @@ function strategyAdept() {
|
|||
}
|
||||
}
|
||||
|
||||
// 4. Mana/spirit items — AFTER buffs
|
||||
if (STATE.mp < CFG.manaGemMP) {
|
||||
const gem = hasUsableGem('mana');
|
||||
if (gem) return { type: 'item', id: gem.id, selfTarget: true };
|
||||
}
|
||||
if (STATE.sp < CFG.spiritPotionSP) {
|
||||
const gem = hasUsableGem('spirit');
|
||||
if (gem) return { type: 'item', id: gem.id, selfTarget: true };
|
||||
}
|
||||
|
||||
// 4. Debuffs — only on important fights (rare/boss/ultimate monsters)
|
||||
// In random mob fights, just attack through them
|
||||
if (anyRareMonster() || STATE.monsters.length <= 2) {
|
||||
|
|
@ -438,19 +438,11 @@ function strategyVeteran() {
|
|||
if (gem && !STATE.channeling)
|
||||
return { type: 'item', id: gem.id, selfTarget: true };
|
||||
|
||||
// 1. Gems proactively — don't hoard, use at reasonable thresholds
|
||||
// 1. Health items — safe to use early
|
||||
if (STATE.hp < 0.60) {
|
||||
const gem = hasUsableGem('heal');
|
||||
if (gem) return { type: 'item', id: gem.id, selfTarget: true };
|
||||
}
|
||||
if (STATE.mp < CFG.manaGemMP) {
|
||||
const gem = hasUsableGem('mana');
|
||||
if (gem) return { type: 'item', id: gem.id, selfTarget: true };
|
||||
}
|
||||
if (STATE.sp < CFG.spiritPotionSP) {
|
||||
const gem = hasUsableGem('spirit');
|
||||
if (gem) return { type: 'item', id: gem.id, selfTarget: true };
|
||||
}
|
||||
|
||||
// 2. Cure
|
||||
if (STATE.hp < CFG.cureHP) {
|
||||
|
|
@ -458,7 +450,7 @@ function strategyVeteran() {
|
|||
if (c) return { type: 'spell', name: c, selfTarget: true };
|
||||
}
|
||||
|
||||
// 3. Buffs — prioritize by remaining duration
|
||||
// 3. Buffs
|
||||
if (CFG.autoBuff) {
|
||||
for (const b of BUFF_PRIORITY) {
|
||||
if (shouldBuff(b.icon, b.minTurns) && STATE.mp > 0.2 && hasSpell(b.spell)) {
|
||||
|
|
@ -468,6 +460,16 @@ function strategyVeteran() {
|
|||
}
|
||||
}
|
||||
|
||||
// 4. Mana/spirit items — AFTER buffs
|
||||
if (STATE.mp < CFG.manaGemMP) {
|
||||
const gem = hasUsableGem('mana');
|
||||
if (gem) return { type: 'item', id: gem.id, selfTarget: true };
|
||||
}
|
||||
if (STATE.sp < CFG.spiritPotionSP) {
|
||||
const gem = hasUsableGem('spirit');
|
||||
if (gem) return { type: 'item', id: gem.id, selfTarget: true };
|
||||
}
|
||||
|
||||
// 4. Debuffs — only on important fights (rare/boss)
|
||||
if (CFG.autoDebuff && STATE.mp > 0.2 && (anyRareMonster() || STATE.monsters.length <= 2)) {
|
||||
if (hasSpell('Imperil')) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue