diff --git a/src/components/game/tabs/CraftingTab.tsx b/src/components/game/tabs/CraftingTab.tsx index fff40dc..ff5642a 100755 --- a/src/components/game/tabs/CraftingTab.tsx +++ b/src/components/game/tabs/CraftingTab.tsx @@ -17,7 +17,7 @@ import { ENCHANTMENT_EFFECTS, type EnchantmentEffectDef, calculateEffectCapacity import { CRAFTING_RECIPES, canCraftRecipe } from '@/lib/game/data/crafting-recipes'; import { LOOT_DROPS, RARITY_COLORS } from '@/lib/game/data/loot-drops'; import type { EquipmentInstance, EnchantmentDesign, DesignEffect, AppliedEnchantment, LootInventory, EquipmentCraftingProgress } from '@/lib/game/types'; -import { fmt } from '@/lib/game/store'; +import { fmt, type GameStore } from '@/lib/game/store'; // Slot display names const SLOT_NAMES: Record = { @@ -31,76 +31,38 @@ const SLOT_NAMES: Record = { accessory2: 'Accessory 2', }; -interface CraftingTabProps { - // Equipment state - equippedInstances: Record; - equipmentInstances: Record; - enchantmentDesigns: EnchantmentDesign[]; - - // Progress states - designProgress: { designId: string; progress: number; required: number } | null; - preparationProgress: { equipmentInstanceId: string; progress: number; required: number; manaCostPaid: number } | null; - applicationProgress: { equipmentInstanceId: string; designId: string; progress: number; required: number; manaPerHour: number; paused: boolean; manaSpent: number } | null; - equipmentCraftingProgress: EquipmentCraftingProgress | null; - - // Player state - rawMana: number; - skills: Record; - currentAction: string; - unlockedEffects: string[]; // Effect IDs that have been researched - - // Loot inventory - lootInventory: LootInventory; - - // Actions - startDesigningEnchantment: (name: string, equipmentTypeId: string, effects: DesignEffect[]) => boolean; - cancelDesign: () => void; - saveDesign: (design: EnchantmentDesign) => void; - deleteDesign: (designId: string) => void; - startPreparing: (equipmentInstanceId: string) => boolean; - cancelPreparation: () => void; - startApplying: (equipmentInstanceId: string, designId: string) => boolean; - pauseApplication: () => void; - resumeApplication: () => void; - cancelApplication: () => void; - disenchantEquipment: (instanceId: string) => void; - getAvailableCapacity: (instanceId: string) => number; - - // Equipment crafting actions - startCraftingEquipment: (blueprintId: string) => boolean; - cancelEquipmentCrafting: () => void; - deleteMaterial: (materialId: string, amount: number) => void; +export interface CraftingTabProps { + store: GameStore; } -export function CraftingTab({ - equippedInstances, - equipmentInstances, - enchantmentDesigns, - designProgress, - preparationProgress, - applicationProgress, - equipmentCraftingProgress, - rawMana, - skills, - currentAction, - unlockedEffects, - lootInventory, - startDesigningEnchantment, - cancelDesign, - saveDesign, - deleteDesign, - startPreparing, - cancelPreparation, - startApplying, - pauseApplication, - resumeApplication, - cancelApplication, - disenchantEquipment, - getAvailableCapacity, - startCraftingEquipment, - cancelEquipmentCrafting, - deleteMaterial, -}: CraftingTabProps) { +export function CraftingTab({ store }: CraftingTabProps) { + const equippedInstances = store.equippedInstances; + const equipmentInstances = store.equipmentInstances; + const enchantmentDesigns = store.enchantmentDesigns; + const designProgress = store.designProgress; + const preparationProgress = store.preparationProgress; + const applicationProgress = store.applicationProgress; + const equipmentCraftingProgress = store.equipmentCraftingProgress; + const rawMana = store.rawMana; + const skills = store.skills; + const currentAction = store.currentAction; + const unlockedEffects = store.unlockedEffects; + const lootInventory = store.lootInventory; + const startDesigningEnchantment = store.startDesigningEnchantment; + const cancelDesign = store.cancelDesign; + const saveDesign = store.saveDesign; + const deleteDesign = store.deleteDesign; + const startPreparing = store.startPreparing; + const cancelPreparation = store.cancelPreparation; + const startApplying = store.startApplying; + const pauseApplication = store.pauseApplication; + const resumeApplication = store.resumeApplication; + const cancelApplication = store.cancelApplication; + const disenchantEquipment = store.disenchantEquipment; + const getAvailableCapacity = store.getAvailableCapacity; + const startCraftingEquipment = store.startCraftingEquipment; + const cancelEquipmentCrafting = store.cancelEquipmentCrafting; + const deleteMaterial = store.deleteMaterial; const [craftingStage, setCraftingStage] = useState<'design' | 'prepare' | 'apply' | 'craft'>('craft'); const [selectedEquipmentType, setSelectedEquipmentType] = useState(null); const [selectedEquipmentInstance, setSelectedEquipmentInstance] = useState(null); diff --git a/src/lib/game/crafting-slice.ts b/src/lib/game/crafting-slice.ts index 4cb2a22..ac95d1e 100755 --- a/src/lib/game/crafting-slice.ts +++ b/src/lib/game/crafting-slice.ts @@ -1,11 +1,12 @@ // ─── Crafting Store Slice ───────────────────────────────────────────────────────── // Handles equipment and enchantment system: design, prepare, apply stages -import type { GameState, EquipmentInstance, AppliedEnchantment, EnchantmentDesign, DesignEffect, EquipmentSlot, EquipmentCraftingProgress, LootInventory } from './types'; +import type { GameState, EquipmentInstance, AppliedEnchantment, EnchantmentDesign, DesignEffect, EquipmentSlot, EquipmentCraftingProgress, LootInventory, AttunementState } from './types'; import { EQUIPMENT_TYPES, type EquipmentCategory } from './data/equipment'; import { ENCHANTMENT_EFFECTS, calculateEffectCapacityCost } from './data/enchantment-effects'; import { CRAFTING_RECIPES, canCraftRecipe, type CraftingRecipe } from './data/crafting-recipes'; import { SPELLS_DEF } from './constants'; +import { calculateEnchantingXP, getAttunementXPForLevel, MAX_ATTUNEMENT_LEVEL } from './data/attunements'; // ─── Helper Functions ───────────────────────────────────────────────────────── @@ -687,11 +688,42 @@ export function processCraftingTick( actualCost: eff.capacityCost, })); + // Calculate and grant attunement XP to enchanter + const xpGained = calculateEnchantingXP(design.totalCapacityUsed); + let newAttunements = state.attunements; + + if (state.attunements.enchanter?.active && xpGained > 0) { + const enchanterState = state.attunements.enchanter; + let newXP = enchanterState.experience + xpGained; + let newLevel = enchanterState.level; + + // Check for level ups + while (newLevel < MAX_ATTUNEMENT_LEVEL) { + const xpNeeded = getAttunementXPForLevel(newLevel + 1); + if (newXP >= xpNeeded) { + newXP -= xpNeeded; + newLevel++; + } else { + break; + } + } + + newAttunements = { + ...state.attunements, + enchanter: { + ...enchanterState, + level: newLevel, + experience: newXP, + }, + }; + } + updates = { ...updates, rawMana: rawMana - manaCost, applicationProgress: null, currentAction: 'meditate', + attunements: newAttunements, equipmentInstances: { ...state.equipmentInstances, [app.equipmentInstanceId]: { @@ -700,7 +732,7 @@ export function processCraftingTick( usedCapacity: instance.usedCapacity + design.totalCapacityUsed, }, }, - log: [`✨ Enchantment "${design.name}" applied to ${instance.name}!`, ...log], + log: [`✨ Enchantment "${design.name}" applied to ${instance.name}! (+${xpGained} Enchanter XP)`, ...log], }; } } else { diff --git a/src/lib/game/data/attunements.ts b/src/lib/game/data/attunements.ts index ce38e20..18bbed9 100644 --- a/src/lib/game/data/attunements.ts +++ b/src/lib/game/data/attunements.ts @@ -111,10 +111,22 @@ export function getAttunementConversionRate(attunementId: string, level: number) // XP required for attunement level export function getAttunementXPForLevel(level: number): number { - // Level 2: 100 XP, Level 3: 300 XP, Level 4: 900 XP, etc. - // Exponential: 100 * (3 ^ (level - 2)) + // New scaling: + // Level 2: 1000 XP + // Level 3: 2500 XP + // Level 4: 5000 XP + // Level 5: 10000 XP + // etc. (each level requires 2x the previous, starting from 1000) if (level <= 1) return 0; - return Math.floor(100 * Math.pow(3, level - 2)); + if (level === 2) return 1000; + // For level 3+: 1000 * 2.5^(level-2), but rounded nicely + return Math.floor(1000 * Math.pow(2, level - 2) * (level >= 3 ? 1.25 : 1)); +} + +// Calculate XP gained from enchanting based on capacity used +export function calculateEnchantingXP(capacityUsed: number): number { + // 1 XP per 10 capacity used, floored, minimum 1 + return Math.max(1, Math.floor(capacityUsed / 10)); } // Max attunement level diff --git a/src/lib/game/store.ts b/src/lib/game/store.ts index 0c37af6..e7caea9 100755 --- a/src/lib/game/store.ts +++ b/src/lib/game/store.ts @@ -499,6 +499,7 @@ function makeInitial(overrides: Partial = {}): GameState { designProgress: null, preparationProgress: null, applicationProgress: null, + equipmentCraftingProgress: null, unlockedEffects: [...BASE_UNLOCKED_EFFECTS], // Start with mana bolt only equipmentSpellStates: [], @@ -1875,6 +1876,8 @@ export const useGameStore = create()( designProgress: state.designProgress, preparationProgress: state.preparationProgress, applicationProgress: state.applicationProgress, + equipmentCraftingProgress: state.equipmentCraftingProgress, + unlockedEffects: state.unlockedEffects, // Loot inventory lootInventory: state.lootInventory, }), diff --git a/src/lib/game/types.ts b/src/lib/game/types.ts index 6aae0f4..8b4e6fd 100755 --- a/src/lib/game/types.ts +++ b/src/lib/game/types.ts @@ -232,6 +232,15 @@ export interface ApplicationProgress { manaSpent: number; // Total mana spent so far } +// Equipment crafting progress (from blueprints) +export interface EquipmentCraftingProgress { + blueprintId: string; + equipmentTypeId: string; + progress: number; // Hours spent crafting + required: number; // Total hours needed + manaSpent: number; // Total mana spent so far +} + // Equipment spell state (for multi-spell casting) export interface EquipmentSpellState { spellId: string; @@ -362,6 +371,7 @@ export interface GameState { designProgress: DesignProgress | null; preparationProgress: PreparationProgress | null; applicationProgress: ApplicationProgress | null; + equipmentCraftingProgress: EquipmentCraftingProgress | null; // Unlocked enchantment effects for designing unlockedEffects: string[]; // Effect IDs that have been researched