fix: resolve enchanting spec vs code discrepancies (issue #324)
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m19s
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:
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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!');
|
||||
|
||||
Reference in New Issue
Block a user