diff --git a/docs/circular-deps.txt b/docs/circular-deps.txt index c93e31e..1d6ed30 100644 --- a/docs/circular-deps.txt +++ b/docs/circular-deps.txt @@ -1,8 +1,8 @@ # Circular Dependencies -Generated: 2026-05-23T15:02:53.084Z +Generated: 2026-05-23T17:29:49.986Z Found: 4 circular chain(s) — these MUST be fixed before modifying involved files. -1. Processed 129 files (1.5s) (3 warnings) +1. Processed 132 files (1.4s) (3 warnings) 2. 1) stores/gameStore.ts > stores/gameActions.ts 3. 2) stores/gameStore.ts > stores/gameLoopActions.ts 4. 3) stores/gameStore.ts > stores/tick-pipeline.ts diff --git a/docs/dependency-graph.json b/docs/dependency-graph.json index f6aa1fe..844efb5 100644 --- a/docs/dependency-graph.json +++ b/docs/dependency-graph.json @@ -1,6 +1,6 @@ { "_meta": { - "generated": "2026-05-23T15:02:51.444Z", + "generated": "2026-05-23T17:29:48.378Z", "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." }, @@ -174,6 +174,15 @@ "data/disciplines/base.ts": [ "types/disciplines.ts" ], + "data/disciplines/enchanter-special.ts": [ + "types/disciplines.ts" + ], + "data/disciplines/enchanter-spells.ts": [ + "types/disciplines.ts" + ], + "data/disciplines/enchanter-utility.ts": [ + "types/disciplines.ts" + ], "data/disciplines/enchanter.ts": [ "types/disciplines.ts" ], @@ -182,6 +191,9 @@ ], "data/disciplines/index.ts": [ "data/disciplines/base.ts", + "data/disciplines/enchanter-special.ts", + "data/disciplines/enchanter-spells.ts", + "data/disciplines/enchanter-utility.ts", "data/disciplines/enchanter.ts", "data/disciplines/fabricator.ts", "data/disciplines/invoker.ts", @@ -454,6 +466,9 @@ ], "stores/discipline-slice.ts": [ "data/disciplines/base.ts", + "data/disciplines/enchanter-special.ts", + "data/disciplines/enchanter-spells.ts", + "data/disciplines/enchanter-utility.ts", "data/disciplines/enchanter.ts", "data/disciplines/fabricator.ts", "data/disciplines/invoker.ts", diff --git a/src/app/page.tsx b/src/app/page.tsx index 0d6144c..fedfe4f 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -91,7 +91,7 @@ function useGameDerivedStats() { attunements: {}, }, upgradeEffects, disciplineEffects); - const clickMana = computeClickMana({ skills: {} }, disciplineEffects); + const clickMana = computeClickMana({}, disciplineEffects); const meditationMultiplier = getMeditationBonus(meditateTicks, {}, upgradeEffects.meditationEfficiency); const incursionStrength = getIncursionStrength(day, hour); diff --git a/src/components/game/ActionButtons.tsx b/src/components/game/ActionButtons.tsx index 6154320..09e4cf0 100755 --- a/src/components/game/ActionButtons.tsx +++ b/src/components/game/ActionButtons.tsx @@ -5,7 +5,7 @@ import type { GameAction } from '@/lib/game/types'; interface ActionButtonsProps { currentAction: GameAction; - currentStudyTarget: { type: 'skill' | 'spell'; id: string; progress: number; required: number } | null; + currentStudyTarget?: { type: 'skill' | 'spell'; id: string; progress: number; required: number } | null; designProgress: { progress: number; required: number } | null; designProgress2: { progress: number; required: number } | null; preparationProgress: { progress: number; required: number } | null; diff --git a/src/components/game/GameToast.tsx b/src/components/game/GameToast.tsx index 773bf2c..0f42c69 100644 --- a/src/components/game/GameToast.tsx +++ b/src/components/game/GameToast.tsx @@ -124,16 +124,9 @@ export function useGameToast() { const toastTypeClass = `toast-type-${type}`; return toast({ - title, - description, + title: title as string, + description: description as string, className: toastTypeClass, - // Store the type for styling - ...{ toastType: type }, - } as { - title: ReactNode; - description?: ReactNode; - className?: string; - toastType?: ToastType; }); }; } diff --git a/src/components/game/crafting/EnchantmentDesigner/EffectSelector.tsx b/src/components/game/crafting/EnchantmentDesigner/EffectSelector.tsx index 9b5c49a..db0b6fb 100644 --- a/src/components/game/crafting/EnchantmentDesigner/EffectSelector.tsx +++ b/src/components/game/crafting/EnchantmentDesigner/EffectSelector.tsx @@ -78,7 +78,7 @@ export function EffectSelector({ {selected && ( removeEffect(effect.id)} > @@ -87,7 +87,7 @@ export function EffectSelector({ )} addEffect(effect.id)} disabled={!selected && selectedEffects.length >= 5} diff --git a/src/components/game/crafting/EnchantmentDesigner/EquipmentTypeSelector.tsx b/src/components/game/crafting/EnchantmentDesigner/EquipmentTypeSelector.tsx index e1504b3..97d5a1e 100644 --- a/src/components/game/crafting/EnchantmentDesigner/EquipmentTypeSelector.tsx +++ b/src/components/game/crafting/EnchantmentDesigner/EquipmentTypeSelector.tsx @@ -29,7 +29,7 @@ export function EquipmentTypeSelector({ />
{designProgress.progress.toFixed(1)}h / {designProgress.required.toFixed(1)}h - Cancel + Cancel
) : ( diff --git a/src/components/game/crafting/EnchantmentDesigner/types.ts b/src/components/game/crafting/EnchantmentDesigner/types.ts index 05fd616..08d00ef 100644 --- a/src/components/game/crafting/EnchantmentDesigner/types.ts +++ b/src/components/game/crafting/EnchantmentDesigner/types.ts @@ -1,4 +1,4 @@ -import type { EquipmentInstance, EnchantmentDesign, DesignEffect, EquipmentCraftingProgress, EquipmentCategory } from '@/lib/game/types'; +import type { EquipmentInstance, EnchantmentDesign, DesignEffect, DesignProgress, EquipmentCategory } from '@/lib/game/types'; export interface EnchantmentDesignerProps { selectedEquipmentType: string | null; @@ -15,7 +15,7 @@ export interface EquipmentTypeSelectorProps { ownedEquipmentTypes: Array<{ id: string; name: string; baseCapacity: number }>; selectedEquipmentType: string | null; setSelectedEquipmentType: (type: string | null) => void; - designProgress: EquipmentCraftingProgress | null; + designProgress: DesignProgress | null; cancelDesign: () => void; } @@ -24,10 +24,10 @@ export interface EffectSelectorProps { selectedEffects: DesignEffect[]; setSelectedEffects: (effects: DesignEffect[]) => void; availableEffects: Array<{ id: string; name: string; description: string; baseCapacityCost: number; maxStacks: number }>; - incompatibleEffects: Array<{ id: string; name: string; description: string }>; + incompatibleEffects: Array<{ id: string; name: string; description: string; allowedEquipmentCategories: EquipmentCategory[] }>; enchantingLevel: number; efficiencyBonus: number; - designProgress: EquipmentCraftingProgress | null; + designProgress: DesignProgress | null; addEffect: (effectId: string) => void; removeEffect: (effectId: string) => void; getIncompatibilityReason: (effect: { id: string; name: string; description: string; allowedEquipmentCategories: EquipmentCategory[] }) => string; diff --git a/src/components/game/crafting/EnchantmentPreparer.tsx b/src/components/game/crafting/EnchantmentPreparer.tsx index 0f05a57..19f9854 100644 --- a/src/components/game/crafting/EnchantmentPreparer.tsx +++ b/src/components/game/crafting/EnchantmentPreparer.tsx @@ -93,7 +93,7 @@ export function EnchantmentPreparer({ {preparationProgress.progress.toFixed(1)}h / {preparationProgress.required.toFixed(1)}h Mana paid: {fmt(preparationProgress.manaCostPaid)} - { + { cancelPreparation(); showToast('warning', 'Preparation Cancelled', 'Equipment preparation was cancelled.'); }}>Cancel diff --git a/src/components/game/tabs/SpireCombatPage/SpireCombatPage.tsx b/src/components/game/tabs/SpireCombatPage/SpireCombatPage.tsx index 620cc39..59907f4 100644 --- a/src/components/game/tabs/SpireCombatPage/SpireCombatPage.tsx +++ b/src/components/game/tabs/SpireCombatPage/SpireCombatPage.tsx @@ -16,7 +16,7 @@ import { SpireManaDisplay } from './SpireManaDisplay'; // ─── Derived Stats Hook ────────────────────────────────────────────────────── -function useSpireStats(prestigeUpgrades: Record, equippedInstances: unknown[], equipmentInstances: unknown[]) { +function useSpireStats(prestigeUpgrades: Record, equippedInstances: Record, equipmentInstances: Record) { const disciplineEffects = computeDisciplineEffects(); const upgradeEffects = getUnifiedEffects({ diff --git a/src/lib/game/__tests__/activity-log.test.ts b/src/lib/game/__tests__/activity-log.test.ts index d1a0cb6..e3c46c1 100644 --- a/src/lib/game/__tests__/activity-log.test.ts +++ b/src/lib/game/__tests__/activity-log.test.ts @@ -1,6 +1,6 @@ import { describe, it, expect } from 'vitest'; import { addActivityLogEntry } from '../utils/activity-log'; -import type { ActivityLogEntry } from '../types'; +import type { ActivityEventType, ActivityLogEntry } from '../types'; // ─── addActivityLogEntry ────────────────────────────────────────────────────── @@ -106,8 +106,8 @@ describe('addActivityLogEntry', () => { }); it('should handle various event types', () => { - const state = { activityLog: [] }; - const types = ['combat', 'crafting', 'prestige', 'discovery', 'achievement'] as const; + const state: { activityLog: ActivityLogEntry[] } = { activityLog: [] }; + const types: ActivityEventType[] = ['combat', 'damage_dealt', 'enemy_defeated', 'floor_cleared', 'spell_cast']; let current = state; for (const eventType of types) { const result = addActivityLogEntry(current, eventType, `Event: ${eventType}`); diff --git a/src/lib/game/__tests__/computed-stats.test.ts b/src/lib/game/__tests__/computed-stats.test.ts index d9afdf2..c51f75e 100755 --- a/src/lib/game/__tests__/computed-stats.test.ts +++ b/src/lib/game/__tests__/computed-stats.test.ts @@ -164,7 +164,7 @@ describe('deductSpellCost', () => { describe('computeMaxMana', () => { it('should return base 100 with no skills or upgrades', () => { const state = { - skills: {}, + skills: {} as Record, prestigeUpgrades: {}, skillUpgrades: {}, skillTiers: {}, @@ -176,7 +176,7 @@ describe('computeMaxMana', () => { it('should include manaWell prestige upgrade', () => { const state = { - skills: {}, + skills: {} as Record, prestigeUpgrades: { manaWell: 5 }, skillUpgrades: {}, skillTiers: {}, @@ -188,7 +188,7 @@ describe('computeMaxMana', () => { it('should apply multiplier from effects', () => { const state = { - skills: {}, + skills: {} as Record, prestigeUpgrades: {}, skillUpgrades: {}, skillTiers: {}, @@ -200,7 +200,7 @@ describe('computeMaxMana', () => { it('should apply bonus from effects', () => { const state = { - skills: {}, + skills: {} as Record, prestigeUpgrades: {}, skillUpgrades: {}, skillTiers: {}, @@ -214,14 +214,14 @@ describe('computeMaxMana', () => { describe('computeRegen', () => { it('should return base regen with no skills', () => { const state = { - skills: {}, - prestigeUpgrades: {}, - skillUpgrades: {}, - skillTiers: {}, - attunements: {}, + skills: {} as Record, + prestigeUpgrades: {} as Record, + skillUpgrades: {} as Record, + skillTiers: {} as Record, + attunements: {} as Record, }; const effects = { regenBonus: 0, regenMultiplier: 1, permanentRegenBonus: 0 } as any; - const result = computeRegen(state, effects); + const result = computeRegen(state as any, effects); // Base regen is 2 (this test provides effects, so no attunement bonus) expect(result).toBe(2); }); @@ -230,10 +230,10 @@ describe('computeRegen', () => { describe('computeClickMana', () => { it('should return base click mana with no skills', () => { const state = { - skills: {}, + skills: {} as Record, }; const discipline = { bonuses: {}, multipliers: {} }; - const result = computeClickMana(state, discipline); + const result = computeClickMana(state.skills, discipline); expect(result).toBeGreaterThanOrEqual(1); }); }); diff --git a/src/lib/game/__tests__/crafting-utils-equipment.test.ts b/src/lib/game/__tests__/crafting-utils-equipment.test.ts index 8f03408..c3d26ef 100644 --- a/src/lib/game/__tests__/crafting-utils-equipment.test.ts +++ b/src/lib/game/__tests__/crafting-utils-equipment.test.ts @@ -3,6 +3,7 @@ import { canEquipInSlot, isTwoHanded, } from '../crafting-utils'; +import type { EquipmentSlot } from '../types/equipmentSlot'; function makeInstance(overrides = {}): any { return { @@ -12,12 +13,15 @@ function makeInstance(overrides = {}): any { enchantments: [], totalCapacity: 100, usedCapacity: 0, + rarity: 'common', + quality: 100, + tags: [], ...overrides, }; } describe('canEquipInSlot', () => { - const baseSlot: Record = { + const baseSlot: Record = { head: null, body: null, hands: null, @@ -72,7 +76,7 @@ describe('canEquipInSlot', () => { }); it('should block two-handed weapon if mainHand is occupied', () => { - const slot = { ...baseSlot, mainHand: 'something' }; + const slot: Record = { ...baseSlot, mainHand: 'something' }; const instance = makeInstance({ instanceId: 'th_1', typeId: 'oakStaff' }); // Even if type is not two-handed, the slot check for mainHand+offHand applies const result = canEquipInSlot(instance, 'mainHand', slot, {}); diff --git a/src/lib/game/__tests__/crafting-utils-recipe.test.ts b/src/lib/game/__tests__/crafting-utils-recipe.test.ts index eb34ca4..cbee955 100644 --- a/src/lib/game/__tests__/crafting-utils-recipe.test.ts +++ b/src/lib/game/__tests__/crafting-utils-recipe.test.ts @@ -34,7 +34,7 @@ describe('checkRecipeMaterials', () => { }); it('should return canCraft false when materials are empty', () => { - const result = checkRecipeMaterials(makeRecipe(), {}); + const result = checkRecipeMaterials(makeRecipe(), {} as any); expect(result.canCraft).toBe(false); expect(result.missingMaterials).toEqual({ manaCrystalDust: 5, arcaneShard: 2 }); }); @@ -52,8 +52,8 @@ describe('checkRecipeMaterials', () => { }); it('should handle recipe with no materials', () => { - const emptyRecipe = makeRecipe({}); - const result = checkRecipeMaterials(emptyRecipe, {}); + const emptyRecipe = makeRecipe({} as any); + const result = checkRecipeMaterials(emptyRecipe, {} as any); expect(result.canCraft).toBe(true); expect(result.missingMaterials).toEqual({}); }); @@ -89,7 +89,7 @@ describe('deductRecipeMaterials', () => { }); it('should handle empty materials', () => { - const result = deductRecipeMaterials(makeRecipe(), {}); + const result = deductRecipeMaterials(makeRecipe(), {} as any); expect(result).toEqual({}); }); }); @@ -129,13 +129,13 @@ describe('refundCraftMaterials', () => { it('should floor fractional refunds', () => { // Recipe with manaCrystalDust: 7 // 50% of 7 = 3.5 → floor = 3 - const recipeWithOdd = makeRecipe({ manaCrystalDust: 7 }); + const recipeWithOdd = makeRecipe({ manaCrystalDust: 7 } as any); const result = refundCraftMaterials(recipeWithOdd, 0.5); expect(result.manaCrystalDust).toBe(3); }); it('should handle empty recipe materials', () => { - const emptyRecipe = makeRecipe({}); + const emptyRecipe = makeRecipe({} as any); const result = refundCraftMaterials(emptyRecipe); expect(result).toEqual({}); }); diff --git a/src/lib/game/__tests__/discipline-math.test.ts b/src/lib/game/__tests__/discipline-math.test.ts index 12376f1..efadaf7 100644 --- a/src/lib/game/__tests__/discipline-math.test.ts +++ b/src/lib/game/__tests__/discipline-math.test.ts @@ -8,6 +8,7 @@ import { getUnlockedPerks, calculateDisciplineStats, } from '../utils/discipline-math'; +import { DisciplinesAttunementType } from '../types/disciplines'; import type { DisciplineDefinition, DisciplineState } from '../types/disciplines'; // ─── Test Fixtures ──────────────────────────────────────────────────────────── @@ -15,7 +16,7 @@ import type { DisciplineDefinition, DisciplineState } from '../types/disciplines const rawMastery: DisciplineDefinition = { id: 'raw-mastery', name: 'Raw Mana Mastery', - attunement: 'base', + attunement: DisciplinesAttunementType.BASE, manaType: 'raw', baseCost: 5, description: 'Learn to harness raw mana more efficiently.', @@ -44,7 +45,7 @@ const rawMastery: DisciplineDefinition = { const elementalAttunement: DisciplineDefinition = { id: 'elemental-attunement', name: 'Elemental Attunement', - attunement: 'base', + attunement: DisciplinesAttunementType.BASE, manaType: 'fire', baseCost: 10, description: 'Begin focusing raw mana into fire.', @@ -66,7 +67,7 @@ const elementalAttunement: DisciplineDefinition = { const cappedPerkDiscipline: DisciplineDefinition = { id: 'capped-test', name: 'Capped Perk Test', - attunement: 'base', + attunement: DisciplinesAttunementType.BASE, manaType: 'raw', baseCost: 1, description: 'Test discipline with capped perk.', diff --git a/src/lib/game/__tests__/floor-utils.upgraded.test.ts b/src/lib/game/__tests__/floor-utils.upgraded.test.ts index ba90382..44ba940 100644 --- a/src/lib/game/__tests__/floor-utils.upgraded.test.ts +++ b/src/lib/game/__tests__/floor-utils.upgraded.test.ts @@ -128,7 +128,8 @@ describe('getFloorElement - Enhanced Edge Cases', () => { }); it('should handle negative floors', () => { - expect(getFloorElement(-10)).toBe('water'); // (-10-1) % 7 = -11 % 7 = 3, earth? Check actual formula + // ((-10-1) % 7 + 7) % 7 = (-11 % 7 + 7) % 7 = (-4 + 7) % 7 = 3 => earth + expect(getFloorElement(-10)).toBe('earth' as string); }); it('should return only valid element names', () => { @@ -141,7 +142,7 @@ describe('getFloorElement - Enhanced Edge Cases', () => { it('should maintain consistent cycling for sequential calls', () => { // Ensure the cycle is consistent across multiple calls - const elements = []; + const elements: string[] = []; for (let i = 1; i <= 21; i++) { elements.push(getFloorElement(i)); } diff --git a/src/lib/game/__tests__/room-utils-floor-state.test.ts b/src/lib/game/__tests__/room-utils-floor-state.test.ts index b021b0a..3e307f9 100644 --- a/src/lib/game/__tests__/room-utils-floor-state.test.ts +++ b/src/lib/game/__tests__/room-utils-floor-state.test.ts @@ -2,7 +2,8 @@ import { describe, it, expect } from 'vitest'; import { generateFloorState } from '../utils/room-utils'; import { PUZZLE_ROOMS, SWARM_CONFIG, SPEED_ROOM_CONFIG } from '../constants'; import { getGuardianForFloor } from '../data/guardian-encounters'; -import { getFloorMaxHP, getDodgeChance } from '../utils/floor-utils'; +import { getFloorMaxHP } from '../utils/floor-utils'; +import { getDodgeChance } from '../utils/room-utils'; // ─── generateFloorState ─────────────────────────────────────────────────────── @@ -72,8 +73,8 @@ describe('generateFloorState', () => { Math.random = () => 0.19; const state = generateFloorState(7); expect(state.roomType).toBe('puzzle'); - expect(state.puzzleAttunements.length).toBeGreaterThan(0); - expect(typeof state.puzzleAttunements[0]).toBe('string'); + expect(state.puzzleAttunements!.length).toBeGreaterThan(0); + expect(typeof state.puzzleAttunements![0]).toBe('string'); Math.random = originalRandom; }); diff --git a/src/lib/game/__tests__/store-actions-combat-prestige.test.ts b/src/lib/game/__tests__/store-actions-combat-prestige.test.ts index 579d289..0d83f06 100644 --- a/src/lib/game/__tests__/store-actions-combat-prestige.test.ts +++ b/src/lib/game/__tests__/store-actions-combat-prestige.test.ts @@ -14,7 +14,7 @@ function resetCombatStore() { currentAction: 'meditate', castProgress: 0, spireMode: false, - currentRoom: { roomType: 'combat', enemies: [], cleared: false }, + currentRoom: { roomType: 'combat', enemies: [] }, clearedFloors: {}, climbDirection: null, isDescending: false, @@ -222,32 +222,32 @@ describe('PrestigeStore', () => { describe('addMemory / removeMemory', () => { it('should add a memory when slots available', () => { - usePrestigeStore.getState().addMemory({ skillId: 'manaFlow', level: 3 }); + usePrestigeStore.getState().addMemory({ skillId: 'manaFlow', level: 3, tier: 1, upgrades: [] }); expect(usePrestigeStore.getState().memories.length).toBe(1); }); it('should not add duplicate memory', () => { - usePrestigeStore.getState().addMemory({ skillId: 'manaFlow', level: 3 }); - usePrestigeStore.getState().addMemory({ skillId: 'manaFlow', level: 5 }); + usePrestigeStore.getState().addMemory({ skillId: 'manaFlow', level: 3, tier: 1, upgrades: [] }); + usePrestigeStore.getState().addMemory({ skillId: 'manaFlow', level: 5, tier: 1, upgrades: [] }); expect(usePrestigeStore.getState().memories.length).toBe(1); }); it('should not exceed memory slots', () => { usePrestigeStore.setState({ memorySlots: 1 }); - usePrestigeStore.getState().addMemory({ skillId: 'manaFlow', level: 1 }); - usePrestigeStore.getState().addMemory({ skillId: 'manaSpring', level: 1 }); + usePrestigeStore.getState().addMemory({ skillId: 'manaFlow', level: 1, tier: 1, upgrades: [] }); + usePrestigeStore.getState().addMemory({ skillId: 'manaSpring', level: 1, tier: 1, upgrades: [] }); expect(usePrestigeStore.getState().memories.length).toBe(1); }); it('should remove memory by skillId', () => { - usePrestigeStore.getState().addMemory({ skillId: 'manaFlow', level: 3 }); + usePrestigeStore.getState().addMemory({ skillId: 'manaFlow', level: 3, tier: 1, upgrades: [] }); usePrestigeStore.getState().removeMemory('manaFlow'); expect(usePrestigeStore.getState().memories.length).toBe(0); }); it('should clear all memories', () => { - usePrestigeStore.getState().addMemory({ skillId: 'manaFlow', level: 1 }); - usePrestigeStore.getState().addMemory({ skillId: 'manaSpring', level: 1 }); + usePrestigeStore.getState().addMemory({ skillId: 'manaFlow', level: 1, tier: 1, upgrades: [] }); + usePrestigeStore.getState().addMemory({ skillId: 'manaSpring', level: 1, tier: 1, upgrades: [] }); usePrestigeStore.getState().clearMemories(); expect(usePrestigeStore.getState().memories.length).toBe(0); }); diff --git a/src/lib/game/__tests__/store-actions.test.ts b/src/lib/game/__tests__/store-actions.test.ts index 977cb79..4bb6d86 100644 --- a/src/lib/game/__tests__/store-actions.test.ts +++ b/src/lib/game/__tests__/store-actions.test.ts @@ -26,7 +26,7 @@ function resetCombatStore() { currentAction: 'meditate', castProgress: 0, spireMode: false, - currentRoom: { roomType: 'combat', enemies: [], cleared: false }, + currentRoom: { roomType: 'combat', enemies: [] }, clearedFloors: {}, climbDirection: null, isDescending: false, diff --git a/src/lib/game/__tests__/tick-integration.test.ts b/src/lib/game/__tests__/tick-integration.test.ts index 4304fa3..5850391 100644 --- a/src/lib/game/__tests__/tick-integration.test.ts +++ b/src/lib/game/__tests__/tick-integration.test.ts @@ -42,7 +42,7 @@ function resetAllStores() { currentAction: 'meditate', castProgress: 0, spireMode: false, - currentRoom: { roomType: 'combat', enemies: [], cleared: false }, + currentRoom: { roomType: 'combat', enemies: [] }, clearedFloors: {}, climbDirection: null, isDescending: false, diff --git a/src/lib/game/data/disciplines/base.ts b/src/lib/game/data/disciplines/base.ts index 5f4a310..2378e21 100644 --- a/src/lib/game/data/disciplines/base.ts +++ b/src/lib/game/data/disciplines/base.ts @@ -1,13 +1,14 @@ // ─── Base Disciplines ───────────────────────────────────────────────────────── // Disciplines available to all attunements +import { DisciplinesAttunementType } from '../../types/disciplines'; import type { DisciplineDefinition } from '../../types/disciplines'; export const baseDisciplines: DisciplineDefinition[] = [ { id: 'raw-mastery', name: 'Raw Mana Mastery', - attunement: 'base', + attunement: DisciplinesAttunementType.BASE, manaType: 'raw', baseCost: 5, description: 'Learn to harness raw mana more efficiently.', @@ -35,7 +36,7 @@ export const baseDisciplines: DisciplineDefinition[] = [ { id: 'elemental-attunement', name: 'Elemental Attunement', - attunement: 'base', + attunement: DisciplinesAttunementType.BASE, manaType: 'fire', baseCost: 10, description: 'Begin focusing raw mana into fire.', diff --git a/src/lib/game/data/disciplines/enchanter-special.ts b/src/lib/game/data/disciplines/enchanter-special.ts index f209a48..400f0c9 100644 --- a/src/lib/game/data/disciplines/enchanter-special.ts +++ b/src/lib/game/data/disciplines/enchanter-special.ts @@ -1,13 +1,14 @@ // ─── Enchanter Special Disciplines ───────────────────────────────────────────── // Disciplines for unlocking unique and powerful special enchantment effects +import { DisciplinesAttunementType } from '../../types/disciplines'; import type { DisciplineDefinition } from '../../types/disciplines'; export const enchanterSpecialDisciplines: DisciplineDefinition[] = [ { id: 'study-special-enchantments', name: 'Study Special Enchantments', - attunement: 'enchanter', + attunement: DisciplinesAttunementType.ENCHANTER, manaType: 'death', baseCost: 22, description: 'Learn to enchant equipment with unique and powerful effects.', diff --git a/src/lib/game/data/disciplines/enchanter-spells.ts b/src/lib/game/data/disciplines/enchanter-spells.ts index e907a7d..92a7a7a 100644 --- a/src/lib/game/data/disciplines/enchanter-spells.ts +++ b/src/lib/game/data/disciplines/enchanter-spells.ts @@ -1,13 +1,14 @@ // ─── Enchanter Spell Disciplines ─────────────────────────────────────────────── // Disciplines for unlocking spell enchantment effects on casters +import { DisciplinesAttunementType } from '../../types/disciplines'; import type { DisciplineDefinition } from '../../types/disciplines'; export const enchanterSpellDisciplines: DisciplineDefinition[] = [ { id: 'study-basic-spell-enchantments', name: 'Study Basic Spell Enchantments', - attunement: 'enchanter', + attunement: DisciplinesAttunementType.ENCHANTER, manaType: 'air', baseCost: 18, description: 'Learn to enchant casters with basic spell effects.', @@ -85,7 +86,7 @@ export const enchanterSpellDisciplines: DisciplineDefinition[] = [ { id: 'study-intermediate-spell-enchantments', name: 'Study Intermediate Spell Enchantments', - attunement: 'enchanter', + attunement: DisciplinesAttunementType.ENCHANTER, manaType: 'earth', baseCost: 25, description: 'Learn to enchant casters with intermediate and compound spell effects.', @@ -148,7 +149,7 @@ export const enchanterSpellDisciplines: DisciplineDefinition[] = [ { id: 'study-advanced-spell-enchantments', name: 'Study Advanced Spell Enchantments', - attunement: 'enchanter', + attunement: DisciplinesAttunementType.ENCHANTER, manaType: 'dark', baseCost: 35, description: 'Learn to enchant casters with master and exotic spell effects.', diff --git a/src/lib/game/data/disciplines/enchanter-utility.ts b/src/lib/game/data/disciplines/enchanter-utility.ts index a0760de..b3c3116 100644 --- a/src/lib/game/data/disciplines/enchanter-utility.ts +++ b/src/lib/game/data/disciplines/enchanter-utility.ts @@ -1,13 +1,14 @@ // ─── Enchanter Utility & Mana Disciplines ────────────────────────────────────── // Disciplines for unlocking utility and mana enchantment effects +import { DisciplinesAttunementType } from '../../types/disciplines'; import type { DisciplineDefinition } from '../../types/disciplines'; export const enchanterUtilityDisciplines: DisciplineDefinition[] = [ { id: 'study-utility-enchantments', name: 'Study Utility Enchantments', - attunement: 'enchanter', + attunement: DisciplinesAttunementType.ENCHANTER, manaType: 'light', baseCost: 8, description: 'Learn to enchant equipment with utility effects.', @@ -45,7 +46,7 @@ export const enchanterUtilityDisciplines: DisciplineDefinition[] = [ { id: 'study-mana-enchantments', name: 'Study Mana Enchantments', - attunement: 'enchanter', + attunement: DisciplinesAttunementType.ENCHANTER, manaType: 'water', baseCost: 15, description: 'Learn to enchant equipment with mana-boosting effects.', diff --git a/src/lib/game/data/disciplines/enchanter.ts b/src/lib/game/data/disciplines/enchanter.ts index 2399989..e4eddb9 100644 --- a/src/lib/game/data/disciplines/enchanter.ts +++ b/src/lib/game/data/disciplines/enchanter.ts @@ -1,13 +1,14 @@ // ─── Enchanter Disciplines ──────────────────────────────────────────────────── // Core enchanter disciplines and weapon enchantment studies +import { DisciplinesAttunementType } from '../../types/disciplines'; import type { DisciplineDefinition } from '../../types/disciplines'; export const enchanterDisciplines: DisciplineDefinition[] = [ { id: 'enchant-crafting', name: 'Enchantment Crafting', - attunement: 'enchanter', + attunement: DisciplinesAttunementType.ENCHANTER, manaType: 'transference', baseCost: 8, description: 'Improve your ability to apply enchantments to equipment.', @@ -35,7 +36,7 @@ export const enchanterDisciplines: DisciplineDefinition[] = [ { id: 'mana-channeling', name: 'Mana Channeling', - attunement: 'enchanter', + attunement: DisciplinesAttunementType.ENCHANTER, manaType: 'lightning', baseCost: 12, description: 'Use lightning to transfer mana to equipment.', @@ -56,7 +57,7 @@ export const enchanterDisciplines: DisciplineDefinition[] = [ { id: 'study-basic-weapon-enchantments', name: 'Study Basic Weapon Enchantments', - attunement: 'enchanter', + attunement: DisciplinesAttunementType.ENCHANTER, manaType: 'fire', baseCost: 10, description: 'Learn to enchant weapons with basic elemental effects.', @@ -94,7 +95,7 @@ export const enchanterDisciplines: DisciplineDefinition[] = [ { id: 'study-advanced-weapon-enchantments', name: 'Study Advanced Weapon Enchantments', - attunement: 'enchanter', + attunement: DisciplinesAttunementType.ENCHANTER, manaType: 'dark', baseCost: 20, description: 'Learn to enchant weapons with exotic and combat effects.', diff --git a/src/lib/game/data/disciplines/fabricator.ts b/src/lib/game/data/disciplines/fabricator.ts index 0ae0b43..1c4e460 100644 --- a/src/lib/game/data/disciplines/fabricator.ts +++ b/src/lib/game/data/disciplines/fabricator.ts @@ -1,13 +1,14 @@ // ─── Fabricator Discipline Files ────────────────────────────────────────────── // Attunement-focused disciplines for Fabricator role +import { DisciplinesAttunementType } from '../../types/disciplines'; import type { DisciplineDefinition } from '../../types/disciplines'; export const fabricatorDisciplines: DisciplineDefinition[] = [ { id: 'golem-crafting', name: 'Golem Crafting', - attunement: 'fabricator', + attunement: DisciplinesAttunementType.FABRICATOR, manaType: 'earth', baseCost: 10, description: 'Improve your ability to craft and maintain golems.', @@ -35,7 +36,7 @@ export const fabricatorDisciplines: DisciplineDefinition[] = [ { id: 'crafting-efficiency', name: 'Crafting Efficiency', - attunement: 'fabricator', + attunement: DisciplinesAttunementType.FABRICATOR, manaType: 'sand', baseCost: 12, description: 'Reduce material costs for crafting.', diff --git a/src/lib/game/data/disciplines/invoker.ts b/src/lib/game/data/disciplines/invoker.ts index fdf90e6..006c872 100644 --- a/src/lib/game/data/disciplines/invoker.ts +++ b/src/lib/game/data/disciplines/invoker.ts @@ -1,13 +1,14 @@ // ─── Invoker Discipline Files ───────────────────────────────────────────────── // Attunement-focused disciplines for Invoker role +import { DisciplinesAttunementType } from '../../types/disciplines'; import type { DisciplineDefinition } from '../../types/disciplines'; export const invokerDisciplines: DisciplineDefinition[] = [ { id: 'spell-casting', name: 'Spell Casting', - attunement: 'invoker', + attunement: DisciplinesAttunementType.INVOKER, manaType: 'light', baseCost: 10, description: 'Improve spell power and effectiveness.', @@ -35,7 +36,7 @@ export const invokerDisciplines: DisciplineDefinition[] = [ { id: 'void-manipulation', name: 'Void Manipulation', - attunement: 'invoker', + attunement: DisciplinesAttunementType.INVOKER, manaType: 'void', baseCost: 15, description: 'Master the exotic void mana for devastating effects.', diff --git a/src/lib/game/data/enchantment-effects.ts b/src/lib/game/data/enchantment-effects.ts index da7ea85..b3634e5 100755 --- a/src/lib/game/data/enchantment-effects.ts +++ b/src/lib/game/data/enchantment-effects.ts @@ -2,10 +2,8 @@ // Re-exports from category-specific files for backward compatibility // All enchantment effect definitions have been moved to src/lib/game/data/enchantments/ -export { +export { ENCHANTMENT_EFFECTS, - type EnchantmentEffectCategory, - type EnchantmentEffectDef, getEnchantmentEffect, getEffectsForEquipment, canApplyEffect, @@ -19,3 +17,5 @@ export { UTILITY_EFFECTS, SPECIAL_EFFECTS, } from './enchantments/index' + +export type { EnchantmentEffectCategory, EnchantmentEffectDef } from './enchantment-types' diff --git a/src/lib/game/data/enchantments/index.ts b/src/lib/game/data/enchantments/index.ts index ca14ec4..9775cfe 100644 --- a/src/lib/game/data/enchantments/index.ts +++ b/src/lib/game/data/enchantments/index.ts @@ -59,6 +59,7 @@ export function calculateEffectCapacityCost(effectId: string, stacks: number, ef } // Re-export category-specific collections for direct access if needed +export type { EnchantmentEffectCategory, EnchantmentEffectDef } from '../enchantment-types'; export { SPELL_EFFECTS, MANA_EFFECTS, diff --git a/src/lib/game/data/equipment/types.ts b/src/lib/game/data/equipment/types.ts index c454437..1585c0e 100644 --- a/src/lib/game/data/equipment/types.ts +++ b/src/lib/game/data/equipment/types.ts @@ -1,6 +1,7 @@ // ─── Equipment Types ───────────────────────────────────────────────── -export type { EquipmentSlot } from '../../types/equipmentSlot'; +import type { EquipmentSlot } from '../../types/equipmentSlot'; +export type { EquipmentSlot }; export type EquipmentCategory = 'caster' | 'shield' | 'catalyst' | 'sword' | 'head' | 'body' | 'hands' | 'feet' | 'accessory'; // All equipment slots in order diff --git a/src/lib/game/data/golems/types.ts b/src/lib/game/data/golems/types.ts index a348d7a..c24d26e 100644 --- a/src/lib/game/data/golems/types.ts +++ b/src/lib/game/data/golems/types.ts @@ -1,6 +1,6 @@ // ─── Golem Types ───────────────────────────────────────────────── -import type { SpellCost } from '../types'; +import type { SpellCost } from '../../types'; // Golem mana cost helper export function elemCost(element: string, amount: number): SpellCost { diff --git a/src/lib/game/data/loot-drops.ts b/src/lib/game/data/loot-drops.ts index af9ea17..7858198 100755 --- a/src/lib/game/data/loot-drops.ts +++ b/src/lib/game/data/loot-drops.ts @@ -1,6 +1,6 @@ // ─── Loot Drop Definitions ───────────────────────────────────────────────────── -import type { LootDrop } from '../types'; +import type { LootDrop } from '../types/game'; export const LOOT_DROPS: Record = { // ─── Materials (used for crafting) ─── diff --git a/src/lib/game/hooks/useGameDerived.ts b/src/lib/game/hooks/useGameDerived.ts index 748dfa2..83f7098 100755 --- a/src/lib/game/hooks/useGameDerived.ts +++ b/src/lib/game/hooks/useGameDerived.ts @@ -26,9 +26,6 @@ import { hasSpecial, SPECIAL_EFFECTS } from '../effects/special-effects'; * Hook for all mana-related derived stats */ export function useManaStats() { - const skills = useGameStore((s) => s.skills); - const skillUpgrades = useGameStore((s) => s.skillUpgrades); - const skillTiers = useGameStore((s) => s.skillTiers); const prestigeUpgrades = usePrestigeStore((s) => s.prestigeUpgrades); const rawMana = useManaStore((s) => s.rawMana); const meditateTicks = useManaStore((s) => s.meditateTicks); @@ -36,28 +33,28 @@ export function useManaStats() { const hour = useGameStore((s) => s.hour); const upgradeEffects = useMemo( - () => computeEffects(skillUpgrades || {}, skillTiers || {}), - [skillUpgrades, skillTiers] + () => computeEffects({}, {}), + [] ); const maxMana = useMemo( - () => computeMaxMana({ skills, prestigeUpgrades, skillUpgrades, skillTiers }, upgradeEffects), - [skills, prestigeUpgrades, skillUpgrades, skillTiers, upgradeEffects] + () => computeMaxMana({ skills: {}, prestigeUpgrades, skillUpgrades: {}, skillTiers: {} }, upgradeEffects), + [prestigeUpgrades, upgradeEffects] ); const baseRegen = useMemo( - () => computeRegen({ skills, prestigeUpgrades, skillUpgrades, skillTiers }, upgradeEffects), - [skills, prestigeUpgrades, skillUpgrades, skillTiers, upgradeEffects] + () => computeRegen({ skills: {} as Record, prestigeUpgrades, attunements: {} } as any, upgradeEffects), + [prestigeUpgrades, upgradeEffects] ); const clickMana = useMemo( - () => computeClickMana({ skills }), - [skills] + () => computeClickMana({}), + [] ); const meditationMultiplier = useMemo( - () => getMeditationBonus(meditateTicks, skills, upgradeEffects.meditationEfficiency), - [meditateTicks, skills, upgradeEffects.meditationEfficiency] + () => getMeditationBonus(meditateTicks, {}, upgradeEffects.meditationEfficiency), + [meditateTicks, upgradeEffects.meditationEfficiency] ); const incursionStrength = useMemo( @@ -107,7 +104,6 @@ export function useManaStats() { * Hook for combat-related derived stats */ export function useCombatStats() { - const skills = useGameStore((s) => s.skills); const signedPacts = usePrestigeStore((s) => s.signedPacts); const currentFloor = useCombatStore((s) => s.currentFloor); const activeSpell = useCombatStore((s) => s.activeSpell); @@ -153,26 +149,26 @@ export function useCombatStats() { if (!activeSpellDef) return 0; const spellCastSpeed = activeSpellDef.castSpeed || 1; - const quickCastBonus = 1 + (skills.quickCast || 0) * 0.05; + const quickCastBonus = 1; const attackSpeedMult = upgradeEffects.attackSpeedMultiplier; const totalCastSpeed = spellCastSpeed * quickCastBonus * attackSpeedMult; - const damagePerCast = calcDamage({ skills, signedPacts }, activeSpell, floorElem); + const damagePerCast = calcDamage({ skills: {}, signedPacts }, activeSpell, floorElem); const castsPerSecond = totalCastSpeed * HOURS_PER_TICK / (TICK_MS / 1000); return damagePerCast * castsPerSecond; - }, [activeSpellDef, skills, signedPacts, activeSpell, floorElem, upgradeEffects.attackSpeedMultiplier]); + }, [activeSpellDef, signedPacts, activeSpell, floorElem, upgradeEffects.attackSpeedMultiplier]); // Damage breakdown for display const damageBreakdown = useMemo(() => { if (!activeSpellDef) return null; const baseDmg = activeSpellDef.dmg; - const combatTrainBonus = (skills.combatTrain || 0) * 5; - const arcaneFuryMult = 1 + (skills.arcaneFury || 0) * 0.1; - const elemMasteryMult = 1 + (skills.elementalMastery || 0) * 0.15; - const guardianBaneMult = isGuardianFloor ? (1 + (skills.guardianBane || 0) * 0.2) : 1; - const precisionChance = (skills.precision || 0) * 0.05; + const combatTrainBonus = 0; + const arcaneFuryMult = 1; + const elemMasteryMult = 1; + const guardianBaneMult = 1; + const precisionChance = 0; // Calculate elemental bonus const elemBonus = getElementalBonus(activeSpellDef.elem, floorElem); @@ -195,9 +191,9 @@ export function useCombatStats() { precisionChance, elemBonus, elemBonusText, - total: calcDamage({ skills, signedPacts }, activeSpell, floorElem), + total: calcDamage({ skills: {}, signedPacts }, activeSpell, floorElem), }; - }, [activeSpellDef, skills, signedPacts, activeSpell, floorElem, isGuardianFloor, pactMultiplier]); + }, [activeSpellDef, signedPacts, activeSpell, floorElem, isGuardianFloor, pactMultiplier]); return { floorElem, @@ -216,23 +212,19 @@ export function useCombatStats() { * Hook for study-related derived stats */ export function useStudyStats() { - const skills = useGameStore((s) => s.skills); - const skillUpgrades = useGameStore((s) => s.skillUpgrades); - const skillTiers = useGameStore((s) => s.skillTiers); - const studySpeedMult = useMemo( - () => getStudySpeedMultiplier(skills), - [skills] + () => getStudySpeedMultiplier({}), + [] ); const studyCostMult = useMemo( - () => getStudyCostMultiplier(skills), - [skills] + () => getStudyCostMultiplier({}), + [] ); const upgradeEffects = useMemo( - () => computeEffects(skillUpgrades || {}, skillTiers || {}), - [skillUpgrades, skillTiers] + () => computeEffects({}, {}), + [] ); const effectiveStudySpeedMult = studySpeedMult * upgradeEffects.studySpeedMultiplier; diff --git a/src/lib/game/stores/crafting-initial-state.ts b/src/lib/game/stores/crafting-initial-state.ts index 426e7cc..0d946d8 100644 --- a/src/lib/game/stores/crafting-initial-state.ts +++ b/src/lib/game/stores/crafting-initial-state.ts @@ -2,10 +2,11 @@ // Helper to generate the starting equipment instances for new games. import * as CraftingUtils from '../crafting-utils'; +import type { EquipmentInstance } from '../types'; export function createInitialEquipmentInstances() { const staffId = CraftingUtils.generateInstanceId(); - const staffInstance = { + const staffInstance: EquipmentInstance = { instanceId: staffId, typeId: 'basicStaff', name: 'Basic Staff', @@ -15,10 +16,14 @@ export function createInitialEquipmentInstances() { rarity: 'common', quality: 100, tags: [], + weaponMana: 0, + weaponManaMax: 0, + weaponManaRegen: 0, + weaponManaType: 'raw', }; const shirtId = CraftingUtils.generateInstanceId(); - const shirtInstance = { + const shirtInstance: EquipmentInstance = { instanceId: shirtId, typeId: 'civilianShirt', name: 'Civilian Shirt', @@ -31,7 +36,7 @@ export function createInitialEquipmentInstances() { }; const shoesId = CraftingUtils.generateInstanceId(); - const shoesInstance = { + const shoesInstance: EquipmentInstance = { instanceId: shoesId, typeId: 'civilianShoes', name: 'Civilian Shoes', @@ -48,7 +53,7 @@ export function createInitialEquipmentInstances() { [staffId]: staffInstance, [shirtId]: shirtInstance, [shoesId]: shoesInstance, - } as Record, + } as Record, equippedInstances: { mainHand: staffId, offHand: null, diff --git a/src/lib/game/stores/craftingStore.ts b/src/lib/game/stores/craftingStore.ts index 191ce02..7d99989 100644 --- a/src/lib/game/stores/craftingStore.ts +++ b/src/lib/game/stores/craftingStore.ts @@ -45,6 +45,7 @@ export const useCraftingStore = create()( selectedDesign: null, selectedEquipmentInstance: null, }, + lastError: null, // Actions setDesignProgress: (progress) => set({ designProgress: progress }), @@ -336,6 +337,8 @@ export const useCraftingStore = create()( unequipItemAction(slot, set); }, + clearLastError: () => set({ lastError: null }), + unlockEffects: (effectIds: string[]) => { set((state) => { const existing = new Set(state.unlockedEffects); @@ -367,6 +370,7 @@ export const useCraftingStore = create()( equippedInstances: state.equippedInstances, lootInventory: state.lootInventory, enchantmentSelection: state.enchantmentSelection, + lastError: state.lastError, }), } ) diff --git a/src/lib/game/stores/discipline-slice.ts b/src/lib/game/stores/discipline-slice.ts index 70d235d..390646d 100644 --- a/src/lib/game/stores/discipline-slice.ts +++ b/src/lib/game/stores/discipline-slice.ts @@ -3,6 +3,7 @@ import { create } from 'zustand'; import { persist } from 'zustand/middleware'; import { createSafeStorage } from '../utils/safe-persist'; import type { DisciplineState } from '../types/disciplines'; +import type { ElementState } from '../types'; import { calculateManaDrain, calculateStatBonus, @@ -40,9 +41,9 @@ export interface DisciplineStoreState { export interface DisciplineStoreActions { activate: (id: string, gameState?: { elements?: Record }) => void; deactivate: (id: string) => void; - processTick: (mana: { rawMana: number; elements: Record }) => { + processTick: (mana: { rawMana: number; elements: Record }) => { rawMana: number; - elements: Record; + elements: Record; unlockedEffects: string[]; }; } @@ -170,6 +171,6 @@ export const useDisciplineStore = create()( return { rawMana, elements, unlockedEffects: newUnlockedEffects }; }, }), - { storage: createSafeStorage(), name: 'mana-loop-discipline-store' } + { storage: createSafeStorage(), name: 'mana-loop-discipline-store', partialize: (state) => ({ disciplines: state.disciplines, activeIds: state.activeIds, concurrentLimit: state.concurrentLimit, totalXP: state.totalXP, processedPerks: state.processedPerks }) } ) ); \ No newline at end of file diff --git a/src/lib/game/stores/gameStore.ts b/src/lib/game/stores/gameStore.ts index 3d1772e..688cbaf 100755 --- a/src/lib/game/stores/gameStore.ts +++ b/src/lib/game/stores/gameStore.ts @@ -342,10 +342,11 @@ export const useGameStore = create()( setDiscipline: (w) => useDisciplineStore.setState(w), addLogs: (msgs) => msgs.forEach((m) => useUIStore.getState().addLog(m)), }); - } catch (error) { + } catch (error: unknown) { // Log error to UI store if available, otherwise console error try { - useUIStore.getState().addLog(`⚠️ Tick error: ${error.message}`); + const msg = error instanceof Error ? error.message : String(error); + useUIStore.getState().addLog(`⚠️ Tick error: ${msg}`); } catch { console.error('Tick error:', error); } diff --git a/src/lib/game/stores/uiStore.ts b/src/lib/game/stores/uiStore.ts index f1f7e34..99bcd39 100755 --- a/src/lib/game/stores/uiStore.ts +++ b/src/lib/game/stores/uiStore.ts @@ -66,6 +66,6 @@ export const useUIStore = create()( }); }, }), - { storage: createSafeStorage(), name: 'mana-loop-ui-storage' } + { storage: createSafeStorage(), name: 'mana-loop-ui-storage', partialize: (state) => ({ logs: state.logs, paused: state.paused, gameOver: state.gameOver, victory: state.victory }) } ) ); diff --git a/src/lib/game/types.ts b/src/lib/game/types.ts index 1a9dae5..dff73f7 100755 --- a/src/lib/game/types.ts +++ b/src/lib/game/types.ts @@ -60,6 +60,19 @@ export type { PrestigeDef } from './types/game'; export type { EquipmentSlot } from './types/equipmentSlot'; +// ─── Skill Upgrade Choice ──────────────────────────────────────────────────── +export interface SkillUpgradeChoice { + id: string; + name: string; + desc: string; + effect: { + type: 'bonus' | 'multiplier' | 'special'; + stat?: string; + value?: number; + specialDesc?: string; + }; +} + // ─── New: Memory Type Definition ───────────────────────────────────────────── export interface Memory { skillId: string; diff --git a/src/lib/game/types/game.ts b/src/lib/game/types/game.ts index 289bcc1..2e3aeb8 100644 --- a/src/lib/game/types/game.ts +++ b/src/lib/game/types/game.ts @@ -7,6 +7,7 @@ import type { EquipmentInstance, EnchantmentDesign, DesignProgress, PreparationP // ─── Activity Log Types ───────────────────────────────────────────────── export type ActivityEventType = + | 'combat' | 'damage_dealt' | 'enemy_defeated' | 'floor_cleared' @@ -54,6 +55,7 @@ export interface FloorState { puzzleRequired?: number; // Total progress needed puzzleId?: string; // Which puzzle type puzzleAttunements?: string[]; // Which attunements speed up this puzzle + cleared?: boolean; // Whether this floor has been cleared // Recovery room fields recoveryProgress?: number; recoveryRequired?: number; diff --git a/src/lib/game/types/spells.ts b/src/lib/game/types/spells.ts index 080d1c7..de0d132 100644 --- a/src/lib/game/types/spells.ts +++ b/src/lib/game/types/spells.ts @@ -16,6 +16,7 @@ export interface SpellDef { unlock: number; // Mana cost to start studying studyTime?: number; // Hours needed to study (optional, defaults based on tier) castSpeed?: number; // Casts per hour (default 1, higher = faster) + baseCastTime?: number; // Base cast time in seconds (default 1.0) desc?: string; // Optional spell description effects?: SpellEffect[]; // Optional special effects isAoe?: boolean; // AOE spell that hits multiple enemies diff --git a/src/lib/game/utils/floor-utils.ts b/src/lib/game/utils/floor-utils.ts index 25b29fe..8521696 100644 --- a/src/lib/game/utils/floor-utils.ts +++ b/src/lib/game/utils/floor-utils.ts @@ -14,5 +14,9 @@ export function getFloorMaxHP(floor: number): number { } export function getFloorElement(floor: number): string { - return FLOOR_ELEM_CYCLE[(floor - 1) % FLOOR_ELEM_CYCLE.length]; + const len = FLOOR_ELEM_CYCLE.length; + const idx = ((floor - 1) % len + len) % len; + return FLOOR_ELEM_CYCLE[idx]; } + +export { getDodgeChance } from './room-utils'; diff --git a/src/lib/game/utils/mana-utils.ts b/src/lib/game/utils/mana-utils.ts index 108e175..0b1bd03 100644 --- a/src/lib/game/utils/mana-utils.ts +++ b/src/lib/game/utils/mana-utils.ts @@ -13,8 +13,8 @@ export interface DisciplineBonuses { // ─── Mana Params ──────────────────────────────────────────────────────────── export interface ManaComputeParams { - skills: Record; - prestigeUpgrades: Record; + skills?: Record; + prestigeUpgrades?: Record; skillUpgrades?: Record; skillTiers?: Record; } @@ -31,15 +31,15 @@ export interface EffectiveRegenParams extends RegenComputeParams { // ─── Max Mana ──────────────────────────────────────────────────────────────── export function computeMaxMana( - state: Pick & Partial>, + state: Partial, effects?: ComputedEffects, discipline?: DisciplineBonuses, ): number { - const pu = state.prestigeUpgrades; + const pu = state.prestigeUpgrades || {}; const base = 100 + ((state.skills || {}).manaWell || 0) * 100 + - ((pu || {}).manaWell || 0) * 500 + + (pu.manaWell || 0) * 500 + (discipline?.bonuses?.maxManaBonus || 0); if (effects) { @@ -55,7 +55,7 @@ export function computeRegen( effects?: ComputedEffects, discipline?: DisciplineBonuses, ): number { - const pu = state.prestigeUpgrades; + const pu = state.prestigeUpgrades || {}; const temporalBonus = 1 + (pu.temporalEcho || 0) * 0.1; const base = 2 + diff --git a/src/lib/game/utils/safe-persist.ts b/src/lib/game/utils/safe-persist.ts index 0ce9138..c89f0bf 100644 --- a/src/lib/game/utils/safe-persist.ts +++ b/src/lib/game/utils/safe-persist.ts @@ -10,13 +10,14 @@ import type { StateStorage } from 'zustand/middleware'; * - Quota exceeded → logs warning, skips write * - Other errors → logs warning, graceful fallback */ -export function createSafeStorage(): StateStorage { - return { - getItem: (name: string): unknown => { +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export function createSafeStorage(): any { + const storage: StateStorage = { + getItem: (name: string): string | null | Promise => { try { const str = localStorage.getItem(name); if (str === null) return null; - return JSON.parse(str); + return str; } catch (error) { console.warn(`[persist] Failed to read "${name}" from localStorage:`, error); try { @@ -46,4 +47,5 @@ export function createSafeStorage(): StateStorage { } }, }; + return storage; }