diff --git a/docs/circular-deps.txt b/docs/circular-deps.txt index c9471e1..df90de0 100644 --- a/docs/circular-deps.txt +++ b/docs/circular-deps.txt @@ -1,5 +1,5 @@ # Circular Dependencies -Generated: 2026-06-08T21:50:12.078Z +Generated: 2026-06-08T23:26:23.477Z Found: 2 circular chain(s) — these MUST be fixed before modifying involved files. 1. 1) stores/golem-combat-actions.ts > stores/golem-combat-helpers.ts diff --git a/docs/dependency-graph.json b/docs/dependency-graph.json index 59e2aca..02cbace 100644 --- a/docs/dependency-graph.json +++ b/docs/dependency-graph.json @@ -1,6 +1,6 @@ { "_meta": { - "generated": "2026-06-08T21:50:10.004Z", + "generated": "2026-06-08T23:26:19.136Z", "description": "Import dependency graph for src/lib/game. Keys are files, values are arrays of files they import.", "usage": "To find what a file affects, search for its path in the VALUES. To find what a file depends on, look at its KEY entry." }, @@ -720,6 +720,7 @@ "stores/golem-combat-actions.ts": [ "constants.ts", "data/golems/index.ts", + "data/golems/types.ts", "data/golems/utils.ts", "stores/golem-combat-helpers.ts", "types.ts" diff --git a/docs/specs/attunements/enchanter/systems/enchanting-spec.md b/docs/specs/attunements/enchanter/systems/enchanting-spec.md index 8c28f6c..997cc71 100644 --- a/docs/specs/attunements/enchanter/systems/enchanting-spec.md +++ b/docs/specs/attunements/enchanter/systems/enchanting-spec.md @@ -281,6 +281,7 @@ Same formula as Prepare stage (§4.5). | **Catalyst** | basicCatalyst | 40 | | | fireCatalyst | 55 | | | voidCatalyst | 75 | +| | metalSpellFocus | 50 | | **Sword** | ironBlade | 30 | | | steelBlade | 40 | | | crystalBlade | 55 | @@ -643,7 +644,7 @@ capacity). It affects: | `src/lib/game/crafting-prep.ts` | Prepare stage logic, disenchant recovery | | `src/lib/game/crafting-apply.ts` | Apply stage logic, free enchant, Pure Essence | | `src/lib/game/crafting-utils.ts` | Shared utilities, capacity cost, cancellation refund | -| `src/lib/game/crafting-attunements.ts` | Attunement-crafting integration, enchanting XP | +| `src/lib/game/data/attunements.ts` | Attunement-crafting integration, enchanting XP | | `src/lib/game/data/enchantments/` | All enchantment effect definitions (7 categories) | | `src/lib/game/crafting-actions/design-actions.ts` | Design stage store actions | | `src/lib/game/crafting-actions/preparation-actions.ts` | Prepare stage store actions | diff --git a/src/lib/game/crafting-actions/design-actions.ts b/src/lib/game/crafting-actions/design-actions.ts index e008c3d..45c6de7 100644 --- a/src/lib/game/crafting-actions/design-actions.ts +++ b/src/lib/game/crafting-actions/design-actions.ts @@ -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; diff --git a/src/lib/game/crafting-actions/disenchant-actions.ts b/src/lib/game/crafting-actions/disenchant-actions.ts index c779638..f0fc437 100644 --- a/src/lib/game/crafting-actions/disenchant-actions.ts +++ b/src/lib/game/crafting-actions/disenchant-actions.ts @@ -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.`], diff --git a/src/lib/game/data/enchantments/spell-effects/basic-spells.ts b/src/lib/game/data/enchantments/spell-effects/basic-spells.ts index c315a58..4d5fd25 100644 --- a/src/lib/game/data/enchantments/spell-effects/basic-spells.ts +++ b/src/lib/game/data/enchantments/spell-effects/basic-spells.ts @@ -159,4 +159,14 @@ export const BASIC_SPELL_EFFECTS: Record = { 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' } + }, }; diff --git a/src/lib/game/data/enchantments/spell-effects/frost-spells.ts b/src/lib/game/data/enchantments/spell-effects/frost-spells.ts index fcd787f..471531b 100644 --- a/src/lib/game/data/enchantments/spell-effects/frost-spells.ts +++ b/src/lib/game/data/enchantments/spell-effects/frost-spells.ts @@ -15,16 +15,6 @@ export const FROST_SPELL_EFFECTS: Record = { 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', diff --git a/src/lib/game/effects/discipline-effects.ts b/src/lib/game/effects/discipline-effects.ts index 4a328f9..24d43d9 100644 --- a/src/lib/game/effects/discipline-effects.ts +++ b/src/lib/game/effects/discipline-effects.ts @@ -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); +} diff --git a/src/lib/game/stores/craftingStore.ts b/src/lib/game/stores/craftingStore.ts index 82ef3d0..f281cad 100644 --- a/src/lib/game/stores/craftingStore.ts +++ b/src/lib/game/stores/craftingStore.ts @@ -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()( // 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()( 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; diff --git a/src/lib/game/stores/pipelines/enchanting-tick.ts b/src/lib/game/stores/pipelines/enchanting-tick.ts index 51348e6..8237623 100644 --- a/src/lib/game/stores/pipelines/enchanting-tick.ts +++ b/src/lib/game/stores/pipelines/enchanting-tick.ts @@ -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!');