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:
GaboGG 2026-07-22 06:13:54 -04:00
parent d9fc1e547c
commit b69eabd1eb
4 changed files with 321 additions and 315 deletions

View file

@ -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')) {

View file

@ -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')) {

View file

@ -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"
}

View file

@ -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')) {