fix: resolve enchanting spec vs code discrepancies (issue #324)
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m19s

- D2: Move spell_iceShard to basic-spells.ts at cost 75 (canonical), remove duplicate from frost-spells.ts
- D7: disenchantEquipment now adds 'Ready for Enchantment' tag and resets rarity to 'common'
- D8: disenchantEquipment now credits recovered mana to raw mana pool
- D14: Wire enchantPower stat from discipline effects into efficiencyBonus via new getEnchantingEfficiencyBonus() helper
- D3: Fix spec file reference from crafting-attunements.ts to data/attunements.ts
- D1/D6: Add missing metalSpellFocus to spec capacity table
This commit is contained in:
2026-06-09 09:33:30 +02:00
parent b0e553c290
commit e45c206321
10 changed files with 46 additions and 16 deletions
@@ -5,6 +5,7 @@ import type { EnchantmentDesign, DesignEffect } from '../types';
import * as CraftingUtils from '../crafting-utils';
import * as CraftingDesign from '../crafting-design';
import { computeEffects } from '../effects/upgrade-effects';
import { getEnchantingEfficiencyBonus } from '../effects/discipline-effects';
import { hasSpecial, SPECIAL_EFFECTS } from '../effects/special-effects';
export interface DesignActionsParams {
@@ -32,7 +33,8 @@ export function startDesigningEnchantment(
const equipType = CraftingUtils.getEquipmentType(equipmentTypeId);
if (!equipType) return false;
const totalCapacityCost = CraftingDesign.calculateDesignCapacityCost(effects, 0);
const efficiencyBonus = getEnchantingEfficiencyBonus();
const totalCapacityCost = CraftingDesign.calculateDesignCapacityCost(effects, efficiencyBonus);
if (totalCapacityCost > equipType.baseCapacity) {
return false;
@@ -1,6 +1,7 @@
// ─── Disenchanting Actions ─────────────────────────────────────────────────
import type { CraftingState } from '../stores/craftingStore.types';
import { useManaStore } from '../stores/manaStore';
export function disenchantEquipment(
instanceId: string,
@@ -19,6 +20,11 @@ export function disenchantEquipment(
totalRecovered += Math.floor(ench.actualCost * recoveryRate);
}
// Credit recovered mana to raw mana pool
if (totalRecovered > 0) {
useManaStore.setState((s) => ({ rawMana: s.rawMana + totalRecovered }));
}
set((s) => ({
equipmentInstances: {
...s.equipmentInstances,
@@ -26,6 +32,8 @@ export function disenchantEquipment(
...instance,
enchantments: [],
usedCapacity: 0,
rarity: 'common',
tags: [...(instance.tags || []), 'Ready for Enchantment'],
},
},
log: [`✨ Disenchanted ${instance.name}, recovered ${totalRecovered} mana.`],
@@ -159,4 +159,14 @@ export const BASIC_SPELL_EFFECTS: Record<string, EnchantmentEffectDef> = {
allowedEquipmentCategories: ALL_CASTER,
effect: { type: 'spell', spellId: 'darkPulse' }
},
spell_iceShard: {
id: 'spell_iceShard',
name: 'Ice Shard',
description: 'Grants the ability to cast Ice Shard (14 water/ice damage)',
category: 'spell',
baseCapacityCost: 75,
maxStacks: 1,
allowedEquipmentCategories: ALL_CASTER,
effect: { type: 'spell', spellId: 'iceShard' }
},
};
@@ -15,16 +15,6 @@ export const FROST_SPELL_EFFECTS: Record<string, EnchantmentEffectDef> = {
allowedEquipmentCategories: ALL_CASTER,
effect: { type: 'spell', spellId: 'frostBite' }
},
spell_iceShard: {
id: 'spell_iceShard',
name: 'Ice Shard',
description: 'Grants the ability to cast Ice Shard (18 frost damage, freeze)',
category: 'spell',
baseCapacityCost: 95,
maxStacks: 1,
allowedEquipmentCategories: ALL_CASTER,
effect: { type: 'spell', spellId: 'iceShard' }
},
spell_frostNova: {
id: 'spell_frostNova',
name: 'Frost Nova',
@@ -153,3 +153,18 @@ export function computeDisciplineEffects(_state?: DisciplineStoreState): Discipl
return { bonuses, multipliers, specials, meditationCapBonus, conversions };
}
/**
* Compute the enchanting efficiency bonus from discipline effects.
* The enchantPower stat from active disciplines is converted to an efficiency bonus
* that reduces enchantment capacity costs.
*
* Formula: efficiencyBonus = enchantPower / 100 (capped at 0.9)
*
* Example: enchantPower = 15 → efficiencyBonus = 0.15 (15% cost reduction)
*/
export function getEnchantingEfficiencyBonus(): number {
const effects = computeDisciplineEffects();
const enchantPower = effects.bonuses.enchantPower || 0;
return Math.min(0.9, enchantPower / 100);
}
+3 -1
View File
@@ -9,6 +9,7 @@ import { useManaStore } from './manaStore';
import { useCombatStore } from './combatStore';
import { useUIStore } from './uiStore';
import { getEnchantingEfficiencyBonus } from '../effects/discipline-effects';
import * as ApplicationActions from '../crafting-actions/application-actions';
import * as PreparationActions from '../crafting-actions/preparation-actions';
import { equipItem as equipItemAction, unequipItem as unequipItemAction } from '../crafting-actions/equipment-actions';
@@ -36,6 +37,7 @@ export const useCraftingStore = create<CraftingStore>()(
// Enchantment design actions
startDesigningEnchantment: (name, equipmentTypeId, effects) => {
const state = get(); // crafting state
const efficiencyBonus = getEnchantingEfficiencyBonus();
const validation = CraftingDesign.validateDesignEffects(effects, equipmentTypeId, 0, state.unlockedEffects);
if (!validation.valid) return false;
@@ -43,7 +45,7 @@ export const useCraftingStore = create<CraftingStore>()(
const equipType = CraftingUtils.getEquipmentType(equipmentTypeId);
if (!equipType) return false;
const totalCapacityCost = CraftingDesign.calculateDesignCapacityCost(effects, 0);
const totalCapacityCost = CraftingDesign.calculateDesignCapacityCost(effects, efficiencyBonus);
if (totalCapacityCost > equipType.baseCapacity) return false;
@@ -8,6 +8,7 @@ import { calculateDesignProgress, createCompletedDesignFromProgress } from '../.
import { calculatePreparationTick, completePreparation } from '../../crafting-prep';
import { calculateApplicationTick, applyEnchantments, updateEnchanterAttunement } from '../../crafting-apply';
import { useCraftingStore } from '../craftingStore';
import { getEnchantingEfficiencyBonus } from '../../effects/discipline-effects';
import type { TickContext, TickWrites } from '../tick-pipeline';
interface EnchantingTickParams {
@@ -51,7 +52,7 @@ export function processEnchantingTicks(
effects: activeProgress.effects,
required: activeProgress.required,
},
0,
getEnchantingEfficiencyBonus(),
);
useCraftingStore.getState().saveDesign(completedDesign);
addLog('Design "' + completedDesign.name + '" completed!');