diff --git a/docs/circular-deps.txt b/docs/circular-deps.txt index b814315..d44be8a 100644 --- a/docs/circular-deps.txt +++ b/docs/circular-deps.txt @@ -1,8 +1,8 @@ # Circular Dependencies -Generated: 2026-05-18T08:33:19.846Z +Generated: 2026-05-18T09:26:29.031Z Found: 7 circular chain(s) — these MUST be fixed before modifying involved files. -1. Processed 151 files (1.4s) (37 warnings) +1. Processed 151 files (1.5s) (37 warnings) 2. 1) data/equipment/index.ts > data/equipment/utils.ts 3. 2) data/golems/index.ts > data/golems/utils.ts 4. 3) stores/combat-actions.ts > stores/combatStore.ts diff --git a/docs/dependency-graph.json b/docs/dependency-graph.json index 1bbc527..ff6229f 100644 --- a/docs/dependency-graph.json +++ b/docs/dependency-graph.json @@ -1,6 +1,6 @@ { "_meta": { - "generated": "2026-05-18T08:33:18.234Z", + "generated": "2026-05-18T09:26:27.302Z", "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." }, diff --git a/docs/project-structure.txt b/docs/project-structure.txt index 78e739d..7f8125f 100644 --- a/docs/project-structure.txt +++ b/docs/project-structure.txt @@ -78,7 +78,6 @@ Mana-Loop/ │ │ │ │ ├── index.tsx │ │ │ │ └── types.ts │ │ │ ├── StatsTab/ -│ │ │ │ ├── ActiveUpgradesSection.tsx │ │ │ │ ├── CombatStatsSection.tsx │ │ │ │ ├── ElementStatsSection.tsx │ │ │ │ ├── LoopStatsSection.tsx @@ -105,20 +104,8 @@ Mana-Loop/ │ │ │ │ ├── GolemDebug.tsx │ │ │ │ ├── PactDebug.tsx │ │ │ │ └── index.tsx -│ │ │ ├── layout/ -│ │ │ │ ├── Header.tsx -│ │ │ │ └── TabBar.tsx │ │ │ ├── shared/ -│ │ │ │ ├── MemorySlotPicker.tsx -│ │ │ │ ├── StudyProgress.tsx -│ │ │ │ └── UpgradeDialog.tsx -│ │ │ ├── stats/ -│ │ │ │ ├── CombatStatsSection.tsx -│ │ │ │ ├── ManaStatsSection.tsx -│ │ │ │ ├── ManaTypeBreakdown.tsx -│ │ │ │ ├── StudyStatsSection.tsx -│ │ │ │ ├── UpgradeEffectsSection.tsx -│ │ │ │ └── index.tsx +│ │ │ │ └── MemorySlotPicker.tsx │ │ │ ├── tabs/ │ │ │ │ └── DisciplinesTab.tsx │ │ │ ├── AchievementsDisplay.tsx @@ -178,11 +165,6 @@ Mana-Loop/ │ │ │ ├── store-method-tests/ │ │ │ ├── bug-fixes.test.ts │ │ │ └── computed-stats.test.ts -│ │ ├── attunements/ -│ │ │ ├── data.ts -│ │ │ ├── index.ts -│ │ │ ├── types.ts -│ │ │ └── utils.ts │ │ ├── constants/ │ │ │ ├── spells-modules/ │ │ │ │ ├── advanced-spells.ts @@ -280,7 +262,6 @@ Mana-Loop/ │ │ │ ├── combatSlice.ts │ │ │ ├── computed.ts │ │ │ ├── craftingSlice.ts -│ │ │ ├── index.ts │ │ │ ├── manaSlice.ts │ │ │ ├── pactSlice.ts │ │ │ ├── prestigeSlice.ts @@ -328,7 +309,6 @@ Mana-Loop/ │ │ │ ├── index.ts │ │ │ ├── mana-utils.ts │ │ │ └── room-utils.ts -│ │ ├── computed-stats.ts │ │ ├── constants.ts │ │ ├── crafting-apply.ts │ │ ├── crafting-attunements.ts @@ -341,14 +321,10 @@ Mana-Loop/ │ │ ├── debug-context.tsx │ │ ├── dynamic-compute.ts │ │ ├── effects.ts -│ │ ├── effects.ts.fix -│ │ ├── formatting.ts -│ │ ├── navigation-slice.ts │ │ ├── special-effects.ts │ │ ├── store.test.ts │ │ ├── store.ts │ │ ├── stores.test.ts -│ │ ├── study-slice.ts │ │ ├── types.ts │ │ ├── upgrade-effects.ts │ │ └── upgrade-effects.types.ts diff --git a/src/app/components/LeftPanel.tsx b/src/app/components/LeftPanel.tsx index 278f98c..54df15f 100644 --- a/src/app/components/LeftPanel.tsx +++ b/src/app/components/LeftPanel.tsx @@ -9,7 +9,7 @@ import { ActionButtons } from '@/components/game'; import { AttunementStatus } from '@/components/game/AttunementStatus'; import { ActivityLogPanel } from '@/components/game/ActivityLogPanel'; import { DebugName } from '@/lib/game/debug-context'; -import { useGameStore, useManaStore, useSkillStore, useCombatStore, useCraftingStore, usePrestigeStore } from '@/lib/game/stores'; +import { useGameStore, useManaStore, useCombatStore, useCraftingStore, usePrestigeStore } from '@/lib/game/stores'; import { getUnifiedEffects } from '@/lib/game/effects'; import { getMeditationBonus, getIncursionStrength } from '@/lib/game/stores'; import { computeTotalMaxMana, computeTotalRegen, computeTotalClickMana } from '@/lib/game/effects'; @@ -20,9 +20,6 @@ export function LeftPanel() { const rawMana = useManaStore((s) => s.rawMana); const elements = useManaStore((s) => s.elements); const meditateTicks = useManaStore((s) => s.meditateTicks); - const skills = useSkillStore((s) => s.skills); - const skillTiers = useSkillStore((s) => s.skillTiers); - const skillUpgrades = useSkillStore((s) => s.skillUpgrades); const prestigeUpgrades = usePrestigeStore((s) => s.prestigeUpgrades); const equippedInstances = useCraftingStore((s) => s.equippedInstances); const equipmentInstances = useCraftingStore((s) => s.equipmentInstances); @@ -30,7 +27,6 @@ export function LeftPanel() { const spireMode = useCombatStore((s) => s.spireMode); const enterSpireMode = useCombatStore((s) => s.enterSpireMode); const currentAction = useCombatStore((s) => s.currentAction); - const currentStudyTarget = useSkillStore((s) => s.currentStudyTarget); const designProgress = useCraftingStore((s) => s.designProgress); const designProgress2 = useCraftingStore((s) => s.designProgress2); const preparationProgress = useCraftingStore((s) => s.preparationProgress); @@ -56,11 +52,11 @@ export function LeftPanel() { return () => cancelAnimationFrame(animationFrameId); }, [isGathering, gatherMana]); - const upgradeEffects = getUnifiedEffects({ skillUpgrades, skillTiers, equippedInstances, equipmentInstances }); - const maxMana = computeTotalMaxMana({ skills, prestigeUpgrades, skillUpgrades, skillTiers, equippedInstances, equipmentInstances }, upgradeEffects); - const baseRegen = computeTotalRegen({ skills, prestigeUpgrades, skillUpgrades, skillTiers, equippedInstances, equipmentInstances }, upgradeEffects); - const clickMana = computeTotalClickMana({ skills, skillUpgrades, skillTiers, equippedInstances, equipmentInstances }, upgradeEffects); - const meditationMultiplier = getMeditationBonus(meditateTicks, skills, upgradeEffects.meditationEfficiency); + const upgradeEffects = getUnifiedEffects({ skillUpgrades: {}, skillTiers: {}, equippedInstances, equipmentInstances }); + const maxMana = computeTotalMaxMana({ skills: {}, prestigeUpgrades, skillUpgrades: {}, skillTiers: {}, equippedInstances, equipmentInstances }, upgradeEffects); + const baseRegen = computeTotalRegen({ skills: {}, prestigeUpgrades, skillUpgrades: {}, skillTiers: {}, equippedInstances, equipmentInstances }, upgradeEffects); + const clickMana = computeTotalClickMana({ skills: {}, skillUpgrades: {}, skillTiers: {}, equippedInstances, equipmentInstances }, upgradeEffects); + const meditationMultiplier = getMeditationBonus(meditateTicks, {}, upgradeEffects.meditationEfficiency); const incursionStrength = getIncursionStrength(useGameStore((s) => s.day), useGameStore((s) => s.hour)); const effectiveRegen = baseRegen * (1 - incursionStrength) * meditationMultiplier; @@ -84,7 +80,7 @@ export function LeftPanel() { {/* 2. Spire Entry */} {!spireMode && ( - @@ -98,7 +94,6 @@ export function LeftPanel() { ); -} \ No newline at end of file +} diff --git a/src/app/page.tsx b/src/app/page.tsx index 480e799..1bb12e8 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,14 +1,12 @@ 'use client'; import { useEffect, useState, lazy, Suspense } from 'react'; -import type { JSX } from 'react'; // Import from new modular stores import { useGameStore, useUIStore, useManaStore, - useSkillStore, useCombatStore, usePrestigeStore, useCraftingStore, @@ -21,23 +19,16 @@ import { } from '@/lib/game/stores'; import { useGameLoop } from '@/lib/game/stores/gameHooks'; import { getUnifiedEffects } from '@/lib/game/effects'; -import { - getStudySpeedMultiplier, - getStudyCostMultiplier, - SPELLS_DEF, - ELEMENTS, - GUARDIANS, -} from '@/lib/game/constants'; -import { getActiveEquipmentSpells, getTotalDPS } from '@/lib/game/computed-stats'; +import { SPELLS_DEF } from '@/lib/game/constants'; import { TimeDisplay } from '@/components/game'; import { hasSpecial, SPECIAL_EFFECTS } from '@/lib/game/effects'; -import { Button } from '@/components/ui/button'; + import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; -import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; + import { Badge } from '@/components/ui/badge'; import { ScrollArea } from '@/components/ui/scroll-area'; -import { RotateCcw, Mountain } from 'lucide-react'; + import { TooltipProvider } from '@/components/ui/tooltip'; import { ErrorBoundary } from '@/components/ErrorBoundary'; import { DebugName } from '@/lib/game/debug-context'; @@ -48,7 +39,6 @@ import { LeftPanel } from './components/LeftPanel'; // Lazy load tab components const SpireTab = lazy(() => import('@/components/game/tabs').then(module => ({ default: module.SpireTab }))); -const SkillsTab = lazy(() => import('@/components/game/tabs').then(module => ({ default: module.SkillsTab }))); const SpellsTab = lazy(() => import('@/components/game/tabs').then(module => ({ default: module.SpellsTab }))); const StatsTab = lazy(() => import('@/components/game/tabs').then(module => ({ default: module.StatsTab }))); @@ -143,10 +133,6 @@ export default function ManaLoopGame() { const initGame = useGameStore((s) => s.initGame); useGameLoop(); - const skills = useSkillStore((s) => s.skills); - const skillTiers = useSkillStore((s) => s.skillTiers); - const skillUpgrades = useSkillStore((s) => s.skillUpgrades); - const prestigeUpgrades = usePrestigeStore((s) => s.prestigeUpgrades); const insight = usePrestigeStore((s) => s.insight); const loopInsight = usePrestigeStore((s) => s.loopInsight); @@ -154,8 +140,7 @@ export default function ManaLoopGame() { const rawMana = useManaStore((s) => s.rawMana); const meditateTicks = useManaStore((s) => s.meditateTicks); - const maxFloorReached = useCombatStore((s) => s.maxFloorReached); - const spells = useCombatStore((s) => s.spells); + const spireMode = useCombatStore((s) => s.spireMode); const gameOver = useUIStore((s) => s.gameOver); @@ -166,34 +151,34 @@ export default function ManaLoopGame() { // Derived state const upgradeEffects = getUnifiedEffects({ - skillUpgrades, - skillTiers, + skillUpgrades: {}, + skillTiers: {}, equippedInstances, equipmentInstances }); const maxMana = computeMaxMana({ - skills, + skills: {}, prestigeUpgrades, - skillUpgrades, - skillTiers + skillUpgrades: {}, + skillTiers: {} }, upgradeEffects); const baseRegen = computeRegen({ - skills, + skills: {}, prestigeUpgrades, - skillUpgrades, - skillTiers + skillUpgrades: {}, + skillTiers: {} }, upgradeEffects); const clickMana = computeClickMana({ - skills, + skills: {}, prestigeUpgrades, - skillUpgrades, - skillTiers + skillUpgrades: {}, + skillTiers: {} }); - const meditationMultiplier = getMeditationBonus(meditateTicks, skills, upgradeEffects.meditationEfficiency); + const meditationMultiplier = getMeditationBonus(meditateTicks, {}, upgradeEffects.meditationEfficiency); const incursionStrength = getIncursionStrength(day, hour); // Effective regen with incursion penalty @@ -258,7 +243,6 @@ export default function ManaLoopGame() { ⚔️ Spire ✨ Attune 🗿 Golems - 📚 Skills 🔮 Spells 🛡️ Gear 🔧 Craft @@ -294,14 +278,6 @@ export default function ManaLoopGame() { - - skills tab failed to load.}> - }> - - - - - spells tab failed to load.}> }> diff --git a/src/components/game/CraftingProgress.tsx b/src/components/game/CraftingProgress.tsx index 4ea64ee..4181ee7 100755 --- a/src/components/game/CraftingProgress.tsx +++ b/src/components/game/CraftingProgress.tsx @@ -4,7 +4,7 @@ import { Button } from '@/components/ui/button'; import { Progress } from '@/components/ui/progress'; import { Target, FlaskConical, Sparkles, Play, Pause, X } from 'lucide-react'; import { fmt } from '@/lib/game/stores'; -import { formatStudyTime } from '@/lib/game/formatting'; +import { formatStudyTime } from '@/lib/game/utils/formatting'; import type { EquipmentInstance, EnchantmentDesign } from '@/lib/game/types'; interface CraftingProgressProps { diff --git a/src/components/game/GameContext/Provider.tsx b/src/components/game/GameContext/Provider.tsx index 03975e6..01e9564 100644 --- a/src/components/game/GameContext/Provider.tsx +++ b/src/components/game/GameContext/Provider.tsx @@ -1,7 +1,6 @@ 'use client'; import { useMemo, type ReactNode } from 'react'; -import { useSkillStore } from '@/lib/game/stores/skillStore'; import { useManaStore } from '@/lib/game/stores/manaStore'; import { usePrestigeStore } from '@/lib/game/stores/prestigeStore'; import { useUIStore } from '@/lib/game/stores/uiStore'; @@ -9,7 +8,6 @@ import { useCombatStore } from '@/lib/game/stores/combatStore'; import { useGameStore } from '@/lib/game/stores/gameStore'; import { computeEffects } from '@/lib/game/upgrade-effects'; import { hasSpecial, SPECIAL_EFFECTS } from '@/lib/game/special-effects'; -import { getStudySpeedMultiplier, getStudyCostMultiplier } from '@/lib/game/constants'; import { computeMaxMana, computeRegen, @@ -32,7 +30,6 @@ import { GameContext } from './context-create'; function createUnifiedStore( gameStore: ReturnType, - skillState: ReturnType, manaState: ReturnType, prestigeState: ReturnType, uiState: ReturnType, @@ -49,7 +46,7 @@ function createUnifiedStore( resetGame: gameStore.resetGame, gatherMana: gameStore.gatherMana, startNewLoop: gameStore.startNewLoop, - + // From manaStore rawMana: manaState.rawMana, meditateTicks: manaState.meditateTicks, @@ -61,25 +58,7 @@ function createUnifiedStore( convertMana: manaState.convertMana, unlockElement: manaState.unlockElement, craftComposite: manaState.craftComposite, - - // From skillStore - skills: skillState.skills, - skillProgress: skillState.skillProgress, - skillUpgrades: skillState.skillUpgrades, - skillTiers: skillState.skillTiers, - paidStudySkills: skillState.paidStudySkills, - currentStudyTarget: skillState.currentStudyTarget, - parallelStudyTarget: skillState.parallelStudyTarget, - setSkillLevel: skillState.setSkillLevel, - startStudyingSkill: skillState.startStudyingSkill, - startStudyingSpell: skillState.startStudyingSpell, - cancelStudy: skillState.cancelStudy, - selectSkillUpgrade: skillState.selectSkillUpgrade, - deselectSkillUpgrade: skillState.deselectSkillUpgrade, - commitSkillUpgrades: skillState.commitSkillUpgrades, - tierUpSkill: skillState.tierUpSkill, - getSkillUpgradeChoices: skillState.getSkillUpgradeChoices, - + // From prestigeStore loopCount: prestigeState.loopCount, insight: prestigeState.insight, @@ -101,7 +80,7 @@ function createUnifiedStore( cancelPactRitual: prestigeState.cancelPactRitual, removePact: prestigeState.removePact, defeatGuardian: prestigeState.defeatGuardian, - + // From combatStore currentFloor: combatState.currentFloor, floorHP: combatState.floorHP, @@ -115,7 +94,7 @@ function createUnifiedStore( setSpell: combatState.setSpell, learnSpell: combatState.learnSpell, advanceFloor: combatState.advanceFloor, - + // From uiStore log: uiState.logs, paused: uiState.paused, @@ -131,125 +110,114 @@ function createUnifiedStore( export function GameProvider({ children }: { children: ReactNode }) { // Get all individual stores const gameStore = useGameStore(); - const skillState = useSkillStore(); const manaState = useManaStore(); const prestigeState = usePrestigeStore(); const uiState = useUIStore(); const combatState = useCombatStore(); - + // Create unified store object for backward compatibility const unifiedStore = useMemo( - () => createUnifiedStore(gameStore, skillState, manaState, prestigeState, uiState, combatState), - [gameStore, skillState, manaState, prestigeState, uiState, combatState] + () => createUnifiedStore(gameStore, manaState, prestigeState, uiState, combatState), + [gameStore, manaState, prestigeState, uiState, combatState] ); - + // Computed effects from upgrades const upgradeEffects = useMemo( - () => computeEffects(skillState.skillUpgrades || {}, skillState.skillTiers || {}), - [skillState.skillUpgrades, skillState.skillTiers] + () => computeEffects({}, {}), + [] ); - + // Create a minimal state object for compute functions const stateForCompute = useMemo(() => ({ - skills: skillState.skills, prestigeUpgrades: prestigeState.prestigeUpgrades, - skillUpgrades: skillState.skillUpgrades, - skillTiers: skillState.skillTiers, signedPacts: prestigeState.signedPacts, rawMana: manaState.rawMana, meditateTicks: manaState.meditateTicks, incursionStrength: gameStore.incursionStrength, - }), [skillState, prestigeState, manaState, gameStore.incursionStrength]); - + }), [prestigeState, manaState, gameStore.incursionStrength]); + // Derived stats const maxMana = useMemo( () => computeMaxMana(stateForCompute, upgradeEffects), [stateForCompute, upgradeEffects] ); - + const baseRegen = useMemo( () => computeRegen(stateForCompute, upgradeEffects), [stateForCompute, upgradeEffects] ); - + const clickMana = useMemo(() => computeClickMana(stateForCompute), [stateForCompute]); - + // Floor element from combat store const floorElem = useMemo(() => getFloorElement(combatState.currentFloor), [combatState.currentFloor]); const floorElemDef = ELEMENTS[floorElem]; const isGuardianFloor = !!GUARDIANS[combatState.currentFloor]; const currentGuardian = GUARDIANS[combatState.currentFloor]; const activeSpellDef = SPELLS_DEF[combatState.activeSpell]; - + const meditationMultiplier = useMemo( - () => getMeditationBonus(manaState.meditateTicks, skillState.skills, upgradeEffects.meditationEfficiency), - [manaState.meditateTicks, skillState.skills, upgradeEffects.meditationEfficiency] + () => getMeditationBonus(manaState.meditateTicks, {}, upgradeEffects.meditationEfficiency), + [manaState.meditateTicks, upgradeEffects.meditationEfficiency] ); - + const incursionStrength = useMemo( () => getIncursionStrength(gameStore.day, gameStore.hour), [gameStore.day, gameStore.hour] ); - - const studySpeedMult = useMemo( - () => getStudySpeedMultiplier(skillState.skills), - [skillState.skills] - ); - - const studyCostMult = useMemo( - () => getStudyCostMultiplier(skillState.skills), - [skillState.skills] - ); - + + const studySpeedMult = 1; + + const studyCostMult = 1; + // Effective regen calculations const effectiveRegenWithSpecials = baseRegen * (1 - incursionStrength); - + const manaCascadeBonus = hasSpecial(upgradeEffects, SPECIAL_EFFECTS.MANA_CASCADE) ? Math.floor(maxMana / 100) * 0.1 : 0; - + const manaWaterfallBonus = hasSpecial(upgradeEffects, SPECIAL_EFFECTS.MANA_WATERFALL) ? Math.floor(maxMana / 100) * 0.25 : 0; - + const effectiveRegen = (effectiveRegenWithSpecials + manaCascadeBonus + manaWaterfallBonus) * meditationMultiplier; - + // Has special flags for UI const hasManaWaterfall = hasSpecial(upgradeEffects, SPECIAL_EFFECTS.MANA_WATERFALL); const hasFlowSurge = hasSpecial(upgradeEffects, SPECIAL_EFFECTS.FLOW_SURGE); const hasManaOverflow = hasSpecial(upgradeEffects, SPECIAL_EFFECTS.MANA_OVERFLOW); const hasEternalFlow = hasSpecial(upgradeEffects, SPECIAL_EFFECTS.ETERNAL_FLOW); - + // Active boons const activeBoons = useMemo( () => getBoonBonuses(prestigeState.signedPacts), [prestigeState.signedPacts] ); - + // DPS calculation - based on active spell, attack speed, and damage const dps = useMemo(() => { if (!activeSpellDef) return 0; const baseDmg = calcDamage( - { skills: skillState.skills, signedPacts: prestigeState.signedPacts }, + { signedPacts: prestigeState.signedPacts }, combatState.activeSpell, floorElem ); const dmgWithEffects = baseDmg * upgradeEffects.baseDamageMultiplier + upgradeEffects.baseDamageBonus; - const attackSpeed = (1 + (skillState.skills.quickCast || 0) * 0.05) * upgradeEffects.attackSpeedMultiplier; + const attackSpeed = (1 + 0 * 0.05) * upgradeEffects.attackSpeedMultiplier; const castSpeed = activeSpellDef.castSpeed || 1; return dmgWithEffects * attackSpeed * castSpeed; - }, [activeSpellDef, skillState.skills, prestigeState.signedPacts, floorElem, upgradeEffects, combatState.activeSpell]); - + }, [activeSpellDef, prestigeState.signedPacts, floorElem, upgradeEffects, combatState.activeSpell]); + // Helper functions const canCastSpell = (spellId: string): boolean => { const spell = SPELLS_DEF[spellId]; if (!spell) return false; return canAffordSpellCost(spell.cost, manaState.rawMana, manaState.elements); }; - + const value: GameContextValue = { store: unifiedStore, - skillStore: skillState, manaStore: manaState, prestigeStore: prestigeState, uiStore: uiState, diff --git a/src/components/game/GameContext/types.ts b/src/components/game/GameContext/types.ts index e80550c..b97b077 100644 --- a/src/components/game/GameContext/types.ts +++ b/src/components/game/GameContext/types.ts @@ -1,5 +1,4 @@ import type { ElementDef, GuardianDef, SpellDef, GameAction } from '@/lib/game/types'; -import { useSkillStore } from '@/lib/game/stores/skillStore'; import { useManaStore } from '@/lib/game/stores/manaStore'; import { usePrestigeStore } from '@/lib/game/stores/prestigeStore'; import { useUIStore } from '@/lib/game/stores/uiStore'; @@ -20,7 +19,7 @@ export interface UnifiedStore { resetGame: () => void; gatherMana: () => void; startNewLoop: () => void; - + // From manaStore rawMana: number; meditateTicks: number; @@ -32,34 +31,7 @@ export interface UnifiedStore { convertMana: (element: string, amount: number) => boolean; unlockElement: (element: string, cost: number) => boolean; craftComposite: (target: string, recipe: string[]) => boolean; - - // From skillStore - skills: Record; - skillProgress: Record; - skillUpgrades: Record; - skillTiers: Record; - paidStudySkills: Record; - currentStudyTarget: { type: 'skill' | 'spell' | 'blueprint'; id: string; progress: number; required: number } | null; - parallelStudyTarget: { type: 'skill' | 'spell' | 'blueprint'; id: string; progress: number; required: number } | null; - setSkillLevel: (skillId: string, level: number) => void; - startStudyingSkill: (skillId: string, rawMana: number) => { started: boolean; cost: number }; - startStudyingSpell: (spellId: string, rawMana: number, studyTime: number) => { started: boolean; cost: number }; - cancelStudy: (retentionBonus: number) => void; - selectSkillUpgrade: (skillId: string, upgradeId: string) => void; - deselectSkillUpgrade: (skillId: string, upgradeId: string) => void; - commitSkillUpgrades: (skillId: string, upgradeIds: string[]) => void; - tierUpSkill: (skillId: string) => void; - getSkillUpgradeChoices: (skillId: string, milestone: 5 | 10) => { - available: Array<{ - id: string; - name: string; - desc: string; - milestone: 5 | 10; - effect: { type: string; stat?: string; value?: number; specialId?: string } - }>; - selected: string[] - }; - + // From prestigeStore loopCount: number; insight: number; @@ -81,7 +53,7 @@ export interface UnifiedStore { cancelPactRitual: () => void; removePact: (floor: number) => void; defeatGuardian: (floor: number) => void; - + // From combatStore currentFloor: number; floorHP: number; @@ -95,7 +67,7 @@ export interface UnifiedStore { setSpell: (spellId: string) => void; learnSpell: (spellId: string) => void; advanceFloor: () => void; - + // From uiStore log: string[]; paused: boolean; @@ -110,17 +82,16 @@ export interface UnifiedStore { export interface GameContextValue { // Unified store for backward compatibility store: UnifiedStore; - + // Individual stores for direct access if needed - skillStore: ReturnType; manaStore: ReturnType; prestigeStore: ReturnType; uiStore: ReturnType; combatStore: ReturnType; - + // Computed effects from upgrades upgradeEffects: ReturnType; - + // Derived stats maxMana: number; baseRegen: number; @@ -134,25 +105,25 @@ export interface GameContextValue { incursionStrength: number; studySpeedMult: number; studyCostMult: number; - + // Effective regen calculations effectiveRegenWithSpecials: number; manaCascadeBonus: number; manaWaterfallBonus: number; effectiveRegen: number; - + // Has special flags hasManaWaterfall: boolean; hasFlowSurge: boolean; hasManaOverflow: boolean; hasEternalFlow: boolean; - + // DPS calculation dps: number; - + // Boons activeBoons: ReturnType; - + // Helpers canCastSpell: (spellId: string) => boolean; hasSpecial: (effects: ReturnType, specialId: string) => boolean; diff --git a/src/components/game/SpellsTab.tsx b/src/components/game/SpellsTab.tsx index 6c8a35d..c03ab06 100755 --- a/src/components/game/SpellsTab.tsx +++ b/src/components/game/SpellsTab.tsx @@ -1,13 +1,11 @@ 'use client'; import { canAffordSpellCost, fmt } from '@/lib/game/stores'; -import { useCombatStore, useSkillStore, useManaStore } from '@/lib/game/stores'; +import { useCombatStore, useManaStore } from '@/lib/game/stores'; import { ELEMENTS, SPELLS_DEF } from '@/lib/game/constants'; -import { useStudyStats } from '@/lib/game/hooks/useGameDerived'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; -import { Progress } from '@/components/ui/progress'; // Format spell cost for display function formatSpellCost(cost: { type: 'raw' | 'element'; element?: string; amount: number }): string { @@ -26,33 +24,24 @@ function getSpellCostColor(cost: { type: 'raw' | 'element'; element?: string; am return ELEMENTS[cost.element || '']?.color || '#9CA3AF'; } -// Format study time -function formatStudyTime(hours: number): string { - if (hours < 1) return `${Math.round(hours * 60)}m`; - return `${hours.toFixed(1)}h`; -} - export function SpellsTab() { const spells = useCombatStore((s) => s.spells); const activeSpell = useCombatStore((s) => s.activeSpell); const setSpell = useCombatStore((s) => s.setSpell); - const currentStudyTarget = useSkillStore((s) => s.currentStudyTarget); - const setCurrentStudyTarget = useSkillStore((s) => s.setCurrentStudyTarget); const rawMana = useManaStore((s) => s.rawMana); const elements = useManaStore((s) => s.elements); - const { studySpeedMult, studyCostMult } = useStudyStats(); - + const spellTiers = [0, 1, 2, 3, 4]; - + return (
{spellTiers.map(tier => { const spellsInTier = Object.entries(SPELLS_DEF).filter(([, def]) => def.tier === tier); if (spellsInTier.length === 0) return null; - + const tierNames = ['Basic Spells (Raw Mana)', 'Tier 1 - Elemental', 'Tier 2 - Advanced', 'Tier 3 - Master', 'Tier 4 - Legendary']; const tierColors = ['text-gray-400', 'text-green-400', 'text-blue-400', 'text-purple-400', 'text-amber-400']; - + return (

{tierNames[tier]}

@@ -60,23 +49,14 @@ export function SpellsTab() { {spellsInTier.map(([id, def]) => { const state = spells?.[id]; const learned = state?.learned; - const isStudying = currentStudyTarget?.id === id; const elemDef = def.elem === 'raw' ? null : ELEMENTS[def.elem]; - const baseStudyTime = def.studyTime || (def.tier * 4); const isActive = activeSpell === id; const canCast = learned && canAffordSpellCost(def.cost, rawMana, elements); - - // Apply skill modifiers - const studyTime = baseStudyTime / studySpeedMult; - const unlockCost = Math.floor(def.unlock * studyCostMult); - - // Can start studying? - const canStudy = !learned && !isStudying && rawMana >= unlockCost; - + return (
@@ -95,16 +75,16 @@ export function SpellsTab() { {def.elem !== 'raw' && {elemDef?.sym} {elemDef?.name}} ⚔️ {def.dmg} dmg
- + {/* Cost display */}
Cost: {formatSpellCost(def.cost)}
- + {def.desc && (
{def.desc}
)} - + {def.effects && Array.isArray(def.effects) && def.effects.length > 0 && (
{def.effects.map((eff, i) => ( @@ -117,7 +97,7 @@ export function SpellsTab() { ))}
)} - + {learned ? (
Learned @@ -128,36 +108,9 @@ export function SpellsTab() { )}
- ) : isStudying ? ( -
- -
- Studying... {formatStudyTime(state?.studyProgress || 0)}/{formatStudyTime(studyTime)} -
-
) : ( -
-
- 1 ? 'text-green-400' : ''}> - Study: {formatStudyTime(studyTime)}{studySpeedMult > 1 && ({Math.round(studySpeedMult * 100)}% speed)} - - {' • '} - - Cost: {fmt(unlockCost)} mana{studyCostMult < 1 && ({Math.round(studyCostMult * 100)}% cost)} - -
- +
+ Not yet learned
)} diff --git a/src/components/game/StatsTab.tsx b/src/components/game/StatsTab.tsx index ccee194..56461c7 100755 --- a/src/components/game/StatsTab.tsx +++ b/src/components/game/StatsTab.tsx @@ -1,59 +1,24 @@ 'use client'; -import { useSkillStore, usePrestigeStore, fmt, fmtDec } from '@/lib/game/stores'; +import { usePrestigeStore, fmt, fmtDec } from '@/lib/game/stores'; import { ELEMENTS } from '@/lib/game/constants'; -import { SKILL_EVOLUTION_PATHS, getTierMultiplier } from '@/lib/game/skill-evolution'; import { useManaStats, useCombatStats, useStudyStats } from '@/lib/game/hooks/useGameDerived'; import { ManaStatsSection } from './StatsTab/ManaStatsSection'; import { CombatStatsSection } from './StatsTab/CombatStatsSection'; import { PactStatusSection } from './StatsTab/PactStatusSection'; import { StudyStatsSection } from './StatsTab/StudyStatsSection'; import { ElementStatsSection } from './StatsTab/ElementStatsSection'; -import { ActiveUpgradesSection } from './StatsTab/ActiveUpgradesSection'; import { LoopStatsSection } from './StatsTab/LoopStatsSection'; -import type { SkillUpgradeChoice } from '@/lib/game/types'; export function StatsTab() { - const skillUpgrades = useSkillStore((s) => s.skillUpgrades); - const skills = useSkillStore((s) => s.skills); - const skillTiers = useSkillStore((s) => s.skillTiers); const prestigeUpgrades = usePrestigeStore((s) => s.prestigeUpgrades); - + const manaStats = useManaStats(); const combatStats = useCombatStats(); const studyStats = useStudyStats(); - // Compute element max - const elemMax = (() => { - const ea = skillTiers?.elemAttune || 1; - const tieredSkillId = ea > 1 ? `elemAttune_t${ea}` : 'elemAttune'; - const level = skills[tieredSkillId] || skills.elemAttune || 0; - const tierMult = getTierMultiplier(tieredSkillId); - return 10 + level * 50 * tierMult + (prestigeUpgrades.elementalAttune || 0) * 25; - })(); - - // Get all selected skill upgrades - const getAllSelectedUpgrades = (): { skillId: string; upgrade: SkillUpgradeChoice }[] => { - const upgrades: { skillId: string; upgrade: SkillUpgradeChoice }[] = []; - for (const [skillId, selectedIds] of Object.entries(skillUpgrades)) { - const baseSkillId = skillId.includes('_t') ? skillId.split('_t')[0] : skillId; - const path = SKILL_EVOLUTION_PATHS[baseSkillId]; - if (!path) continue; - for (const tier of path.tiers) { - if (tier.skillId === skillId) { - for (const upgradeId of selectedIds) { - const upgrade = (tier as any).upgrades?.find((u: any) => u.id === upgradeId); - if (upgrade) { - upgrades.push({ skillId, upgrade }); - } - } - } - } - } - return upgrades; - }; - - const selectedUpgrades = getAllSelectedUpgrades(); + // Compute element max (base + prestige only) + const elemMax = 10 + (prestigeUpgrades.elementalAttune || 0) * 25; return (
@@ -65,7 +30,6 @@ export function StatsTab() { meditationMultiplier={manaStats.meditationMultiplier} upgradeEffects={manaStats.upgradeEffects} elemMax={elemMax} - selectedUpgrades={selectedUpgrades} /> -
); diff --git a/src/components/game/StatsTab/ActiveUpgradesSection.tsx b/src/components/game/StatsTab/ActiveUpgradesSection.tsx deleted file mode 100644 index a13df87..0000000 --- a/src/components/game/StatsTab/ActiveUpgradesSection.tsx +++ /dev/null @@ -1,71 +0,0 @@ -'use client'; - -import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; -import { Star } from 'lucide-react'; -import { Badge } from '@/components/ui/badge'; -import { SKILLS_DEF } from '@/lib/game/constants'; -import { SKILL_EVOLUTION_PATHS } from '@/lib/game/skill-evolution'; -import type { SkillUpgradeChoice } from '@/lib/game/types'; - -interface ActiveUpgradesSectionProps { - selectedUpgrades: { skillId: string; upgrade: SkillUpgradeChoice }[]; -} - -export function ActiveUpgradesSection({ selectedUpgrades }: ActiveUpgradesSectionProps) { - if (selectedUpgrades.length === 0) { - return ( - - - - - Active Skill Upgrades (0) - - - -
No skill upgrades selected yet. Level skills to 5 or 10 to choose upgrades.
-
-
- ); - } - - return ( - - - - - Active Skill Upgrades ({selectedUpgrades.length}) - - - -
- {selectedUpgrades.map(({ skillId, upgrade }) => ( -
-
- {upgrade.name} - - {SKILLS_DEF[skillId]?.name || skillId} - -
-
{upgrade.desc}
- {upgrade.effect.type === 'multiplier' && ( -
- +{Math.round((upgrade.effect.value! - 1) * 100)}% {upgrade.effect.stat} -
- )} - {upgrade.effect.type === 'bonus' && ( -
- +{upgrade.effect.value} {upgrade.effect.stat} -
- )} - {upgrade.effect.type === 'special' && ( -
- ⚡ {upgrade.effect.specialDesc || 'Special effect active'} -
- )} -
- ))} -
-
-
- ); -} \ No newline at end of file diff --git a/src/components/game/StatsTab/CombatStatsSection.tsx b/src/components/game/StatsTab/CombatStatsSection.tsx index 5e6e37d..41ff884 100644 --- a/src/components/game/StatsTab/CombatStatsSection.tsx +++ b/src/components/game/StatsTab/CombatStatsSection.tsx @@ -3,8 +3,6 @@ import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Swords } from 'lucide-react'; import { fmt, fmtDec } from '@/lib/game/stores'; -import { useSkillStore } from '@/lib/game/stores'; -import { getUnifiedEffects } from '@/lib/game/effects'; interface CombatStatsSectionProps { activeSpellDef: any; @@ -12,17 +10,6 @@ interface CombatStatsSectionProps { } export function CombatStatsSection({ activeSpellDef, pactMultiplier }: CombatStatsSectionProps) { - const skills = useSkillStore((s) => s.skills); - const skillUpgrades = useSkillStore((s) => s.skillUpgrades); - const skillTiers = useSkillStore((s) => s.skillTiers); - - const upgradeEffects = getUnifiedEffects({ - skillUpgrades, - skillTiers, - equippedInstances: {}, - equipmentInstances: {}, - }); - return ( @@ -38,36 +25,6 @@ export function CombatStatsSection({ activeSpellDef, pactMultiplier }: CombatSta Active Spell Base Damage: {activeSpellDef?.dmg || 5}
-
- Combat Training Bonus: - +{(skills.combatTrain || 0) * 5} -
-
- Arcane Fury Multiplier: - ×{fmtDec(1 + (skills.arcaneFury || 0) * 0.1, 2)} -
-
- Elemental Mastery: - ×{fmtDec(1 + (skills.elementalMastery || 0) * 0.15, 2)} -
-
- Guardian Bane: - ×{fmtDec(1 + (skills.guardianBane || 0) * 0.2, 2)} (vs guardians) -
-
-
-
- Critical Hit Chance: - {((skills.precision || 0) * 5)}% -
-
- Critical Multiplier: - 1.5x -
-
- Spell Echo Chance: - {((skills.spellEcho || 0) * 10)}% -
Pact Multiplier: ×{fmtDec(pactMultiplier, 2)} @@ -77,8 +34,14 @@ export function CombatStatsSection({ activeSpellDef, pactMultiplier }: CombatSta {fmt(activeSpellDef ? activeSpellDef.dmg * pactMultiplier : 0)}
+
+
+ Critical Multiplier: + 1.5x +
+
); -} \ No newline at end of file +} diff --git a/src/components/game/StatsTab/ElementStatsSection.tsx b/src/components/game/StatsTab/ElementStatsSection.tsx index f1be1bc..6aeff15 100644 --- a/src/components/game/StatsTab/ElementStatsSection.tsx +++ b/src/components/game/StatsTab/ElementStatsSection.tsx @@ -4,28 +4,17 @@ import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Separator } from '@/components/ui/separator'; import { FlaskConical } from 'lucide-react'; import { ELEMENTS } from '@/lib/game/constants'; -import { getTierMultiplier } from '@/lib/game/skill-evolution'; import { fmt, fmtDec } from '@/lib/game/stores'; -import { useSkillStore, usePrestigeStore, useManaStore } from '@/lib/game/stores'; +import { usePrestigeStore, useManaStore } from '@/lib/game/stores'; interface ElementStatsSectionProps { elemMax: number; } export function ElementStatsSection({ elemMax }: ElementStatsSectionProps) { - const skills = useSkillStore((s) => s.skills); - const skillTiers = useSkillStore((s) => s.skillTiers); const prestigeUpgrades = usePrestigeStore((s) => s.prestigeUpgrades); const elements = useManaStore((s) => s.elements); - - const getElemAttunementBonus = () => { - const ea = skillTiers?.elemAttune || 1; - const tieredSkillId = ea > 1 ? `elemAttune_t${ea}` : 'elemAttune'; - const level = skills[tieredSkillId] || skills.elemAttune || 0; - const tierMult = getTierMultiplier(tieredSkillId); - return level * 50 * tierMult; - }; - + return ( @@ -41,10 +30,6 @@ export function ElementStatsSection({ elemMax }: ElementStatsSectionProps) { Element Capacity: {elemMax} -
- Elem. Attunement Bonus: - +{getElemAttunementBonus()} -
Prestige Attunement: +{(prestigeUpgrades.elementalAttune || 0) * 25} @@ -55,10 +40,6 @@ export function ElementStatsSection({ elemMax }: ElementStatsSectionProps) { Unlocked Elements: {Object.values(elements || {}).filter((e: any) => e.unlocked).length} / {Object.keys(ELEMENTS).length}
-
- Elem. Crafting Bonus: - ×{fmtDec(1 + (skills.elemCrafting || 0) * 0.25, 2)} -
@@ -79,4 +60,4 @@ export function ElementStatsSection({ elemMax }: ElementStatsSectionProps) {
); -} \ No newline at end of file +} diff --git a/src/components/game/StatsTab/LoopStatsSection.tsx b/src/components/game/StatsTab/LoopStatsSection.tsx index 99c6716..d7cf77c 100644 --- a/src/components/game/StatsTab/LoopStatsSection.tsx +++ b/src/components/game/StatsTab/LoopStatsSection.tsx @@ -4,21 +4,19 @@ import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Separator } from '@/components/ui/separator'; import { RotateCcw } from 'lucide-react'; import { fmt } from '@/lib/game/stores'; -import { useCombatStore, usePrestigeStore, useManaStore, useSkillStore } from '@/lib/game/stores'; +import { useCombatStore, usePrestigeStore, useManaStore } from '@/lib/game/stores'; export function LoopStatsSection() { const spells = useCombatStore((s) => s.spells); - const skills = useSkillStore((s) => s.skills); const insight = usePrestigeStore((s) => s.insight); const totalInsight = usePrestigeStore((s) => s.totalInsight); const maxFloorReached = useCombatStore((s) => s.maxFloorReached); const totalManaGathered = useManaStore((s) => s.totalManaGathered); const loopCount = usePrestigeStore((s) => s.loopCount); const memorySlots = usePrestigeStore((s) => s.memorySlots); - + const spellsLearned = Object.values(spells || {}).filter((s: any) => s.learned).length; - const totalSkillLevels = Object.values(skills || {}).reduce((a: number, b: number) => a + b, 0); - + return ( @@ -52,10 +50,6 @@ export function LoopStatsSection() {
{spellsLearned}
Spells Learned
-
-
{totalSkillLevels}
-
Total Skill Levels
-
{fmt(totalManaGathered)}
Total Mana Gathered
diff --git a/src/components/game/StatsTab/ManaStatsSection.tsx b/src/components/game/StatsTab/ManaStatsSection.tsx index d217faa..4ff971e 100644 --- a/src/components/game/StatsTab/ManaStatsSection.tsx +++ b/src/components/game/StatsTab/ManaStatsSection.tsx @@ -3,8 +3,6 @@ import { fmt, fmtDec } from '@/lib/game/stores'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Droplet } from 'lucide-react'; -import type { SkillUpgradeChoice } from '@/lib/game/types'; -import { useSkillStore, usePrestigeStore } from '@/lib/game/stores'; interface ManaStatsSectionProps { maxMana: number; @@ -14,7 +12,6 @@ interface ManaStatsSectionProps { meditationMultiplier: number; upgradeEffects: any; elemMax: number; - selectedUpgrades: { skillId: string; upgrade: SkillUpgradeChoice }[]; } export function ManaStatsSection({ @@ -25,16 +22,7 @@ export function ManaStatsSection({ meditationMultiplier, upgradeEffects, elemMax, - selectedUpgrades, }: ManaStatsSectionProps) { - const skills = useSkillStore((s) => s.skills); - const skillTiers = useSkillStore((s) => s.skillTiers); - const prestigeUpgrades = usePrestigeStore((s) => s.prestigeUpgrades); - - const getTierMultiplier = (skillId: string) => { - return 1; - }; - return ( @@ -50,22 +38,6 @@ export function ManaStatsSection({ Base Max Mana: 100
-
- Mana Well Bonus: - - {(() => { - const mw = skillTiers?.manaWell || 1; - const tieredSkillId = mw > 1 ? `manaWell_t${mw}` : 'manaWell'; - const level = (skills || {})[tieredSkillId] || (skills || {}).manaWell || 0; - const tierMult = getTierMultiplier(tieredSkillId); - return `+${fmt(level * 100 * tierMult)} (${level} lvl × 100 × ${tierMult}x tier)`; - })()} - -
-
- Prestige Mana Well: - +{fmt((prestigeUpgrades.manaWell || 0) * 500)} -
{upgradeEffects.maxManaBonus > 0 && (
Upgrade Mana Bonus: @@ -88,30 +60,6 @@ export function ManaStatsSection({ Base Regen: 2/hr
-
- Mana Flow Bonus: - - {(() => { - const mf = skillTiers?.manaFlow || 1; - const tieredSkillId = mf > 1 ? `manaFlow_t${mf}` : 'manaFlow'; - const level = skills[tieredSkillId] || skills.manaFlow || 0; - const tierMult = getTierMultiplier(tieredSkillId); - return `+${fmtDec(level * 1 * tierMult, 2)}/hr (${level} lvl × 1 × ${tierMult}x tier)`; - })()} - -
-
- Mana Spring Bonus: - +{(skills.manaSpring || 0) * 2}/hr -
-
- Prestige Mana Flow: - +{fmtDec((prestigeUpgrades.manaFlow || 0) * 0.5)}/hr -
-
- Temporal Echo: - ×{fmtDec(1 + (prestigeUpgrades.temporalEcho || 0) * 0.1, 2)} -
Base Regen: {fmtDec(baseRegen, 2)}/hr @@ -142,18 +90,6 @@ export function ManaStatsSection({ Click Mana Value: +{clickMana}
-
- Mana Tap Bonus: - +{skills.manaTap || 0} -
-
- Mana Surge Bonus: - +{(skills.manaSurge || 0) * 3} -
-
- Mana Overflow: - ×{fmtDec(1 + (skills.manaOverflow || 0) * 0.25, 2)} -
@@ -173,7 +109,7 @@ export function ManaStatsSection({
{/* Special Effects */} - {(upgradeEffects.hasSteadyStream || upgradeEffects.hasManaTorrent || + {(upgradeEffects.hasSteadyStream || upgradeEffects.hasManaTorrent || upgradeEffects.hasDesperateWells || upgradeEffects.manaCascadeBonus > 0 || upgradeEffects.manaWaterfallBonus > 0) && ( <> @@ -240,4 +176,4 @@ export function ManaStatsSection({
); -} \ No newline at end of file +} diff --git a/src/components/game/StatsTab/StudyStatsSection.tsx b/src/components/game/StatsTab/StudyStatsSection.tsx index 9eaf6f5..d119494 100644 --- a/src/components/game/StatsTab/StudyStatsSection.tsx +++ b/src/components/game/StatsTab/StudyStatsSection.tsx @@ -3,7 +3,6 @@ import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { BookOpen } from 'lucide-react'; import { fmtDec } from '@/lib/game/stores'; -import { useSkillStore } from '@/lib/game/stores'; interface StudyStatsSectionProps { studySpeedMult: number; @@ -11,8 +10,6 @@ interface StudyStatsSectionProps { } export function StudyStatsSection({ studySpeedMult, studyCostMult }: StudyStatsSectionProps) { - const skills = useSkillStore((s) => s.skills); - return ( @@ -28,29 +25,21 @@ export function StudyStatsSection({ studySpeedMult, studyCostMult }: StudyStatsS Study Speed: ×{fmtDec(studySpeedMult, 2)} -
- Quick Learner Bonus: - +{((skills.quickLearner || 0) * 10)}% -
Study Cost: {Math.round(studyCostMult * 100)}%
-
- Focused Mind Bonus: - -{((skills.focusedMind || 0) * 5)}% -
Progress Retention: - {Math.round((1 + (skills.knowledgeRetention || 0) * 0.2) * 100)}% + 100%
); -} \ No newline at end of file +} diff --git a/src/components/game/StudyProgress.tsx b/src/components/game/StudyProgress.tsx index 23f2966..59f3f9a 100755 --- a/src/components/game/StudyProgress.tsx +++ b/src/components/game/StudyProgress.tsx @@ -4,7 +4,7 @@ import { Button } from '@/components/ui/button'; import { Progress } from '@/components/ui/progress'; import { BookOpen, X } from 'lucide-react'; import { SKILLS_DEF, SPELLS_DEF } from '@/lib/game/constants'; -import { formatStudyTime } from '@/lib/game/formatting'; +import { formatStudyTime } from '@/lib/game/utils/formatting'; import type { StudyTarget } from '@/lib/game/types'; interface StudyProgressProps { diff --git a/src/components/game/TimeDisplay.tsx b/src/components/game/TimeDisplay.tsx index 346b99d..0cf02f6 100755 --- a/src/components/game/TimeDisplay.tsx +++ b/src/components/game/TimeDisplay.tsx @@ -1,7 +1,7 @@ 'use client'; import { fmt } from '@/lib/game/stores'; -import { formatHour } from '@/lib/game/formatting'; +import { formatHour } from '@/lib/game/utils/formatting'; interface TimeDisplayProps { day: number; diff --git a/src/components/game/crafting/EnchantmentDesigner.tsx b/src/components/game/crafting/EnchantmentDesigner.tsx index 86bccf0..d38b1ee 100644 --- a/src/components/game/crafting/EnchantmentDesigner.tsx +++ b/src/components/game/crafting/EnchantmentDesigner.tsx @@ -23,7 +23,6 @@ import { removeEffectFromDesign, } from './EnchantmentDesigner/utils'; import { useCraftingStore } from '@/lib/game/stores'; -import { useSkillStore } from '@/lib/game/stores'; export function EnchantmentDesigner({ selectedEquipmentType, @@ -44,15 +43,8 @@ export function EnchantmentDesigner({ const unlockedEffects = useCraftingStore((s) => s.unlockedEffects); const equipmentInstances = useCraftingStore((s) => s.equipmentInstances); - // Skill store selectors - const skills = useSkillStore((s) => s.skills); - const skillUpgrades = useSkillStore((s) => s.skillUpgrades); - - const enchantingLevel = skills?.enchanting || 0; - const efficiencyBonus = (skillUpgrades?.['efficientEnchant'] || []).length * 0.05 || 0; - // Calculate total capacity cost for current design - const designCapacityCost = calculateDesignCapacityCost(selectedEffects, efficiencyBonus); + const designCapacityCost = calculateDesignCapacityCost(selectedEffects, 0); // Get capacity limit for selected equipment type const selectedEquipmentCapacity = getEquipmentCapacity(selectedEquipmentType); @@ -62,7 +54,7 @@ export function EnchantmentDesigner({ // Add effect to design const addEffect = (effectId: string) => { - addEffectToDesign(effectId, selectedEffects, efficiencyBonus, setSelectedEffects); + addEffectToDesign(effectId, selectedEffects, 0, setSelectedEffects); }; // Remove effect from design @@ -117,8 +109,8 @@ export function EnchantmentDesigner({ setSelectedEffects={setSelectedEffects} availableEffects={availableEffects} incompatibleEffects={incompatibleEffects} - enchantingLevel={enchantingLevel} - efficiencyBonus={efficiencyBonus} + enchantingLevel={0} + efficiencyBonus={0} designProgress={designProgress} addEffect={addEffect} removeEffect={removeEffect} diff --git a/src/components/game/crafting/EnchantmentPreparer.tsx b/src/components/game/crafting/EnchantmentPreparer.tsx index 3581f5b..0f05a57 100644 --- a/src/components/game/crafting/EnchantmentPreparer.tsx +++ b/src/components/game/crafting/EnchantmentPreparer.tsx @@ -14,7 +14,7 @@ import { EQUIPMENT_TYPES } from '@/lib/game/data/equipment'; import type { EquipmentInstance, AppliedEnchantment, LootInventory, EquipmentCraftingProgress } from '@/lib/game/types'; import type { EquipmentSlot } from '@/lib/game/types'; import { fmt } from '@/lib/game/stores'; -import { useGameStore, useCraftingStore, useManaStore, useSkillStore } from '@/lib/game/stores'; +import { useGameStore, useCraftingStore, useManaStore } from '@/lib/game/stores'; import { useGameToast } from '@/components/game/GameToast'; export interface EnchantmentPreparerProps { @@ -31,7 +31,6 @@ export function EnchantmentPreparer({ const equipmentInstances = useCraftingStore((s) => s.equipmentInstances); const preparationProgress = useCraftingStore((s) => s.preparationProgress); const rawMana = useManaStore((s) => s.rawMana); - const skills = useSkillStore((s) => s.skills); const startPreparing = useCraftingStore((s) => s.startPreparing); const cancelPreparation = useCraftingStore((s) => s.cancelPreparation); diff --git a/src/components/game/index.ts b/src/components/game/index.ts index 212d241..5f5bed2 100755 --- a/src/components/game/index.ts +++ b/src/components/game/index.ts @@ -2,11 +2,9 @@ // Re-exports all game tab components for cleaner imports // Tab components -export { CraftingTab } from './tabs/CraftingTab'; -export { SpireTab } from './tabs/SpireTab'; -export { SpellsTab } from './tabs/SpellsTab'; -export { SkillsTab } from './SkillsTab'; -export { StatsTab } from './tabs/StatsTab'; +export { CraftingTab } from './crafting'; +export { SpellsTab } from './SpellsTab'; +export { StatsTab } from './StatsTab'; // UI components export { ActionButtons } from './ActionButtons'; diff --git a/src/components/game/layout/Header.tsx b/src/components/game/layout/Header.tsx deleted file mode 100644 index c48bd46..0000000 --- a/src/components/game/layout/Header.tsx +++ /dev/null @@ -1,45 +0,0 @@ -'use client'; - -import { fmt } from '@/lib/game/stores'; -import { formatHour } from '@/lib/game/formatting'; -import { TimeDisplay } from '@/components/game/TimeDisplay'; - -interface HeaderProps { - day: number; - hour: number; - insight: number; -} - -export function Header({ day, hour, insight }: HeaderProps) { - return ( -
-
- {/* Game Title - always visible */} -

MANA LOOP

- - {/* Desktop header content */} -
- -
- - {/* Mobile header content - compact */} -
-
-
- D{day} {formatHour(hour)} -
-
- {fmt(insight)} 💎 -
-
-
-
-
- ); -} - -Header.displayName = "Header"; diff --git a/src/components/game/layout/TabBar.tsx b/src/components/game/layout/TabBar.tsx deleted file mode 100644 index 6280472..0000000 --- a/src/components/game/layout/TabBar.tsx +++ /dev/null @@ -1,142 +0,0 @@ -'use client'; - -import { useState } from 'react'; -import { TabsTrigger } from '@/components/ui/tabs'; -import { Separator } from '@/components/ui/separator'; -import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'; -import { - Mountain, - Sparkles, - Brain, - Wand2, - Bone, - Shield, - Hammer, - Gem, - Trophy, - FlaskConical, - BarChart3, - BookOpen, - Wrench -} from 'lucide-react'; - -interface TabBarProps { - activeTab: string; - onTabChange: (value: string) => void; - isMobile?: boolean; -} - -// Tab configuration with groups -const TAB_GROUPS = [ - { - name: 'World', - tabs: [ - { value: 'spire', label: 'Spire', icon: Mountain, mobileLabel: 'Spire' }, - { value: 'attunements', label: 'Attune', icon: Sparkles, mobileLabel: 'Attune' }, - ] - }, - { - name: 'Power', - tabs: [ - { value: 'skills', label: 'Skills', icon: Brain, mobileLabel: 'Skills' }, - { value: 'spells', label: 'Spells', icon: Wand2, mobileLabel: 'Spells' }, - { value: 'golemancy', label: 'Golems', icon: Bone, mobileLabel: 'Golems' }, - ] - }, - { - name: 'Gear', - tabs: [ - { value: 'equipment', label: 'Gear', icon: Shield, mobileLabel: 'Gear' }, - { value: 'crafting', label: 'Craft', icon: Hammer, mobileLabel: 'Craft' }, - { value: 'loot', label: 'Loot', icon: Gem, mobileLabel: 'Loot' }, - ] - }, - { - name: 'Meta', - tabs: [ - { value: 'achievements', label: 'Achieve', icon: Trophy, mobileLabel: 'Achieve' }, - { value: 'stats', label: 'Stats', icon: BarChart3, mobileLabel: 'Stats' }, - { value: 'debug', label: 'Debug', icon: Wrench, mobileLabel: 'Debug' }, - ] - } -]; - -export function TabBar({ activeTab, onTabChange, isMobile = false }: TabBarProps) { - if (isMobile) { - return ( - -
- {TAB_GROUPS.map((group, groupIndex) => ( -
- {groupIndex > 0 && ( - - )} - {group.tabs.map((tab) => { - const Icon = tab.icon; - const isActive = activeTab === tab.value; - return ( - - - - - -

{tab.label}

-
-
- ); - })} -
- ))} -
-
- ); - } - - // Desktop view - grouped tabs with separators - return ( -
- {TAB_GROUPS.map((group, groupIndex) => ( -
- {groupIndex > 0 && ( - - )} - {group.tabs.map((tab) => { - const isActive = activeTab === tab.value; - return ( - - {tab.label} - - ); - })} -
- ))} -
- ); -} - -TabBar.displayName = "TabBar"; diff --git a/src/components/game/shared/StudyProgress.tsx b/src/components/game/shared/StudyProgress.tsx deleted file mode 100755 index e934950..0000000 --- a/src/components/game/shared/StudyProgress.tsx +++ /dev/null @@ -1,62 +0,0 @@ -'use client'; - -import { Button } from '@/components/ui/button'; -import { Progress } from '@/components/ui/progress'; -import { BookOpen, X } from 'lucide-react'; -import { useGameContext } from '../GameContext'; -import { formatStudyTime } from '../types'; -import { SKILLS_DEF, SPELLS_DEF } from '@/lib/game/constants'; - -interface StudyProgressProps { - target: NonNullable['store']['currentStudyTarget']>; - showCancel?: boolean; - speedLabel?: string; -} - -export function StudyProgress({ target, showCancel = true, speedLabel }: StudyProgressProps) { - const { store, studySpeedMult } = useGameContext(); - - const progressPct = Math.min(100, (target.progress / target.required) * 100); - const isSkill = target.type === 'skill'; - const def = isSkill ? SKILLS_DEF[target.id] : SPELLS_DEF[target.id]; - const currentLevel = isSkill ? store.skills[target.id] || 0 : 0; - - const handleCancel = () => { - // Calculate retention bonus from knowledge retention skill - const retentionBonus = 0.2 * (store.skills.knowledgeRetention || 0); - store.cancelStudy(retentionBonus); - }; - - return ( -
-
-
- - - {def?.name} - {isSkill && ` Lv.${currentLevel + 1}`} - -
- {showCancel && ( - - )} -
- -
- - {formatStudyTime(target.progress)} / {formatStudyTime(target.required)} - - {speedLabel ?? `${studySpeedMult.toFixed(1)}x speed`} -
-
- ); -} - -StudyProgress.displayName = "StudyProgress"; diff --git a/src/components/game/shared/UpgradeDialog.tsx b/src/components/game/shared/UpgradeDialog.tsx deleted file mode 100755 index 9fda489..0000000 --- a/src/components/game/shared/UpgradeDialog.tsx +++ /dev/null @@ -1,128 +0,0 @@ -'use client'; - -import { useState } from 'react'; -import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription } from '@/components/ui/dialog'; -import { Button } from '@/components/ui/button'; -import { Badge } from '@/components/ui/badge'; -import { useGameContext } from '../GameContext'; -import { SKILLS_DEF } from '@/lib/game/constants'; - -interface UpgradeDialogProps { - skillId: string | null; - milestone: 5 | 10; - onClose: () => void; -} - -export function UpgradeDialog({ skillId, milestone, onClose }: UpgradeDialogProps) { - const { store } = useGameContext(); - - const skillDef = skillId ? SKILLS_DEF[skillId] : null; - const { available, selected: alreadySelected } = skillId - ? store.getSkillUpgradeChoices(skillId, milestone) - : { available: [], selected: [] }; - - // Use local state for selections within this dialog session - const [pendingSelections, setPendingSelections] = useState(() => [...alreadySelected]); - - const toggleUpgrade = (upgradeId: string) => { - setPendingSelections((prev) => { - if (prev.includes(upgradeId)) { - return prev.filter((id) => id !== upgradeId); - } else if (prev.length < 2) { - return [...prev, upgradeId]; - } - return prev; - }); - }; - - const handleDone = () => { - if (pendingSelections.length === 2 && skillId) { - store.commitSkillUpgrades(skillId, pendingSelections); - } - onClose(); - }; - - const handleOpenChange = (open: boolean) => { - if (!open) { - setPendingSelections([...alreadySelected]); - onClose(); - } - }; - - // Don't render if no skill selected - if (!skillId) return null; - - return ( - - - - Choose Upgrade - {skillDef?.name || skillId} - - Level {milestone} Milestone - Select 2 upgrades ({pendingSelections.length}/2 chosen) - - - -
- {available.map((upgrade) => { - const isSelected = pendingSelections.includes(upgrade.id); - const canToggle = pendingSelections.length < 2 || isSelected; - - return ( -
{ - if (canToggle) { - toggleUpgrade(upgrade.id); - } - }} - > -
-
{upgrade.name}
- {isSelected && Selected} -
-
{upgrade.desc}
- {upgrade.effect.type === 'multiplier' && ( -
- +{Math.round((upgrade.effect.value! - 1) * 100)}% {upgrade.effect.stat} -
- )} - {upgrade.effect.type === 'bonus' && ( -
- +{upgrade.effect.value} {upgrade.effect.stat} -
- )} - {upgrade.effect.type === 'special' && ( -
⚡ {upgrade.desc || 'Special effect'}
- )} -
- ); - })} -
- -
- - -
-
-
- ); -} - -UpgradeDialog.displayName = "UpgradeDialog"; diff --git a/src/components/game/stats/CombatStatsSection.tsx b/src/components/game/stats/CombatStatsSection.tsx deleted file mode 100644 index 1686eaa..0000000 --- a/src/components/game/stats/CombatStatsSection.tsx +++ /dev/null @@ -1,67 +0,0 @@ -'use client'; - -import { fmtDec } from '@/lib/game/stores'; -import { GUARDIANS } from '@/lib/game/constants'; -import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; -import { Swords } from 'lucide-react'; - -// Modular stores -import { useSkillStore, usePrestigeStore } from '@/lib/game/stores'; - -export function CombatStatsSection() { - // Get state from modular stores - const skills = useSkillStore((s) => s.skills); - const signedPacts = usePrestigeStore((s) => s.signedPacts); - - return ( - - - - - Combat Stats - - - -
-
-
- Combat Training Bonus: - +{(skills.combatTrain || 0) * 5} -
-
- Arcane Fury Multiplier: - ×{fmtDec(1 + (skills.arcaneFury || 0) * 0.1, 2)} -
-
- Elemental Mastery: - ×{fmtDec(1 + (skills.elementalMastery || 0) * 0.15, 2)} -
-
- Guardian Bane: - ×{fmtDec(1 + (skills.guardianBane || 0) * 0.2, 2)} (vs guardians) -
-
-
-
- Critical Hit Chance: - {(skills.precision || 0) * 5}% -
-
- Critical Multiplier: - 1.5x -
-
- Spell Echo Chance: - {(skills.spellEcho || 0) * 10}% -
-
- ×{fmtDec(signedPacts.reduce((m, f) => m * (GUARDIANS[f]?.pact || 1), 1), 2)} -
-
-
-
-
- ); -} - -CombatStatsSection.displayName = "CombatStatsSection"; diff --git a/src/components/game/stats/ManaStatsSection.tsx b/src/components/game/stats/ManaStatsSection.tsx deleted file mode 100644 index 5bb7539..0000000 --- a/src/components/game/stats/ManaStatsSection.tsx +++ /dev/null @@ -1,268 +0,0 @@ -'use client'; - -import { getTierMultiplier } from '@/lib/game/skill-evolution'; -import { hasSpecial, SPECIAL_EFFECTS } from '@/lib/game/effects'; -import { fmt, fmtDec } from '@/lib/game/stores'; -import type { UnifiedEffects } from '@/lib/game/effects'; -import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; -import { Separator } from '@/components/ui/separator'; -import { Droplet } from 'lucide-react'; - -// Modular stores -import { useSkillStore, usePrestigeStore, useManaStore } from '@/lib/game/stores'; - -export interface ManaStatsSectionProps { - upgradeEffects: UnifiedEffects; - maxMana: number; - baseRegen: number; - clickMana: number; - meditationMultiplier: number; - effectiveRegen: number; - incursionStrength: number; - manaCascadeBonus: number; - manaWaterfallBonus: number; - hasManaWaterfall: boolean; - hasFlowSurge: boolean; - hasManaOverflow: boolean; - hasEternalFlow: boolean; -} - -export function ManaStatsSection({ - upgradeEffects, - maxMana, - baseRegen, - clickMana, - meditationMultiplier, - effectiveRegen, - incursionStrength, - manaCascadeBonus, - manaWaterfallBonus, - hasManaWaterfall, - hasFlowSurge, - hasManaOverflow, - hasEternalFlow, -}: ManaStatsSectionProps) { - // Get state from modular stores - const skills = useSkillStore((s) => s.skills); - const skillTiers = useSkillStore((s) => s.skillTiers); - const prestigeUpgrades = usePrestigeStore((s) => s.prestigeUpgrades); - const rawMana = useManaStore((s) => s.rawMana); - - return ( - - - - - Mana Stats - - - -
-
-
- Base Max Mana: - 100 -
-
- Mana Well Bonus: - - {(() => { - const mw = skillTiers?.manaWell || 1; - const tieredSkillId = mw > 1 ? `manaWell_t${mw}` : 'manaWell'; - const level = skills[tieredSkillId] || skills.manaWell || 0; - const tierMult = getTierMultiplier(tieredSkillId); - return `+${fmt(level * 100 * tierMult)} (${level} lvl × 100 × ${tierMult}x tier)`; - })()} - -
-
- Prestige Mana Well: - +{fmt((prestigeUpgrades.manaWell || 0) * 500)} -
- {upgradeEffects.maxManaBonus > 0 && ( -
- Upgrade Mana Bonus: - +{fmt(upgradeEffects.maxManaBonus)} -
- )} - {upgradeEffects.maxManaMultiplier > 1 && ( -
- Upgrade Mana Multiplier: - ×{fmtDec(upgradeEffects.maxManaMultiplier, 2)} -
- )} -
- Total Max Mana: - {fmt(maxMana)} -
-
-
-
- Base Regen: - 2/hr -
-
- Mana Flow Bonus: - - {(() => { - const mf = skillTiers?.manaFlow || 1; - const tieredSkillId = mf > 1 ? `manaFlow_t${mf}` : 'manaFlow'; - const level = skills[tieredSkillId] || skills.manaFlow || 0; - const tierMult = getTierMultiplier(tieredSkillId); - return `+${fmtDec(level * 1 * tierMult)}/hr (${level} lvl × 1 × ${tierMult}x tier)`; - })()} - -
-
- Mana Spring Bonus: - +{(skills.manaSpring || 0) * 2}/hr -
-
- Prestige Mana Flow: - +{fmtDec((prestigeUpgrades.manaFlow || 0) * 0.5)}/hr -
-
- Temporal Echo: - ×{fmtDec(1 + (prestigeUpgrades.temporalEcho || 0) * 0.1, 2)} -
-
- Base Regen: - {fmtDec(baseRegen, 2)}/hr -
- {upgradeEffects.regenBonus > 0 && ( -
- Upgrade Regen Bonus: - +{fmtDec(upgradeEffects.regenBonus, 2)}/hr -
- )} - {upgradeEffects.permanentRegenBonus > 0 && ( -
- Permanent Regen Bonus: - +{fmtDec(upgradeEffects.permanentRegenBonus, 2)}/hr -
- )} - {upgradeEffects.regenMultiplier > 1 && ( -
- Upgrade Regen Multiplier: - ×{fmtDec(upgradeEffects.regenMultiplier, 2)} -
- )} -
-
- - {upgradeEffects.activeUpgrades.length > 0 && ( - <> -
- Active Skill Upgrades -
-
- {upgradeEffects.activeUpgrades.map((upgrade, idx) => ( -
- {upgrade.name} - {upgrade.desc} -
- ))} -
- - - )} -
-
-
- Click Mana Value: - +{clickMana} -
-
- Mana Tap Bonus: - +{skills.manaTap || 0} -
-
- Mana Surge Bonus: - +{(skills.manaSurge || 0) * 3} -
-
- Mana Overflow: - ×{fmtDec(1 + (skills.manaOverflow || 0) * 0.25, 2)} -
-
-
-
- 1.5 ? 'text-purple-400' : 'text-gray-300'}`}> - Meditation Multiplier: - - 1.5 ? 'text-purple-400' : 'text-gray-300'}`}> - {fmtDec(meditationMultiplier, 2)}x - -
-
- Effective Regen: - {fmtDec(effectiveRegen, 2)}/hr -
- {incursionStrength > 0 && !hasSpecial(upgradeEffects, SPECIAL_EFFECTS.STEADY_STREAM) && ( -
- Incursion Penalty: - -{Math.round(incursionStrength * 100)}% -
- )} - {hasSpecial(upgradeEffects, SPECIAL_EFFECTS.STEADY_STREAM) && incursionStrength > 0 && ( -
- Steady Stream: - Immune to incursion -
- )} - {manaCascadeBonus > 0 && ( -
- Mana Cascade Bonus: - +{fmtDec(manaCascadeBonus, 2)}/hr -
- )} - {manaWaterfallBonus > 0 && ( -
- Mana Waterfall Bonus: - +{fmtDec(manaWaterfallBonus, 2)}/hr -
- )} - {hasManaWaterfall && ( -
- Mana Waterfall: - +0.25 regen per 100 max mana -
- )} - {hasFlowSurge && ( -
- Flow Surge: - Clicks activate +100% regen for 1hr -
- )} - {hasManaOverflow && ( -
- Mana Overflow: - Raw mana can exceed max by 20% -
- )} - {hasEternalFlow && ( -
- Eternal Flow: - Regen immune to ALL penalties -
- )} - {hasSpecial(upgradeEffects, SPECIAL_EFFECTS.MANA_TORRENT) && rawMana > maxMana * 0.75 && ( -
- Mana Torrent: - +50% regen (high mana) -
- )} - {hasSpecial(upgradeEffects, SPECIAL_EFFECTS.DESPERATE_WELLS) && rawMana < maxMana * 0.25 && ( -
- Desperate Wells: - +50% regen (low mana) -
- )} -
-
-
-
- ); -} - -ManaStatsSection.displayName = "ManaStatsSection"; diff --git a/src/components/game/stats/ManaTypeBreakdown.tsx b/src/components/game/stats/ManaTypeBreakdown.tsx deleted file mode 100644 index 9888f0a..0000000 --- a/src/components/game/stats/ManaTypeBreakdown.tsx +++ /dev/null @@ -1,239 +0,0 @@ -'use client'; - -import { ELEMENTS } from '@/lib/game/constants'; -import { fmt, fmtDec } from '@/lib/game/stores'; -import { ATTUNEMENTS_DEF, getAttunementConversionRate } from '@/lib/game/data/attunements'; -import { computeMaxMana, computeElementMax } from '@/lib/game/stores'; -import { computeEffectiveRegenForDisplay } from '@/lib/game/store-modules/computed-stats'; -import { getUnifiedEffects } from '@/lib/game/effects'; -import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; -import { Droplet } from 'lucide-react'; -import { Separator } from '@/components/ui/separator'; - -// Modular stores -import { useManaStore, useSkillStore, usePrestigeStore } from '@/lib/game/stores'; - -export function ManaTypeBreakdown() { - // Get state from modular stores - const skills = useSkillStore((s) => s.skills); - const skillTiers = useSkillStore((s) => s.skillTiers); - const skillUpgrades = useSkillStore((s) => s.skillUpgrades); - - const prestigeUpgrades = usePrestigeStore((s) => s.prestigeUpgrades); - const elements = useManaStore((s) => s.elements); - const rawMana = useManaStore((s) => s.rawMana); - // attunements is not in modular stores - using empty object as fallback - const attunements: Record = {}; - - // Compute unified effects for regen calculations - const effects = getUnifiedEffects({ - skillUpgrades, - skillTiers, - equippedInstances: {}, - equipmentInstances: {} - }); - - // Get effective regen info for raw mana - const regenInfo = computeEffectiveRegenForDisplay({ - skills, - prestigeUpgrades, - skillUpgrades, - skillTiers, - elements, - rawMana, - attunements - } as any, effects); - - // Compute max mana - const maxMana = computeMaxMana({ - skills, - prestigeUpgrades, - skillUpgrades, - skillTiers - }, effects); - - // Get unlocked elements sorted by category then name - const unlockedElements = Object.entries(elements) - .filter(([, state]) => state.unlocked) - .map(([id, state]) => { - const def = ELEMENTS[id]; - if (!def) return null; - const elemMax = computeElementMax({ - skills, - prestigeUpgrades, - skillUpgrades, - skillTiers - } as any, effects, id); - return { - id, - name: def.name, - sym: def.sym, - color: def.color, - current: state.current, - max: elemMax, - cat: def.cat, - recipe: def.recipe, - }; - }) - .filter(Boolean) - .sort((a, b) => { - if (!a || !b) return 0; - if (a.cat !== b.cat) return a.cat.localeCompare(b.cat); - return a.name.localeCompare(b.name); - }); - - return ( - - - - - Mana Type Breakdown - - - -
- {/* Raw Mana Section */} -
-
-
- 🌀 - Raw Mana - (base) -
-
- {fmt(rawMana)} - / - {fmt(maxMana)} -
-
- - {/* Progress bar */} -
-
-
- - {/* Regen info */} -
-
- Base Regen: - {fmtDec(regenInfo.rawRegen, 2)}/hr -
- {regenInfo.conversionDrain > 0 && ( -
- Conversion Drain: - -{fmtDec(regenInfo.conversionDrain, 2)}/hr -
- )} - -
- Effective Regen: - {fmtDec(regenInfo.effectiveRegen, 2)}/hr -
- - {/* Show conversion drains by attunement */} - {attunements && Object.keys(attunements).length > 0 && ( - <> - -
Conversion Drains:
- {Object.entries(attunements).map(([attId, attState]) => { - const attDef = ATTUNEMENTS_DEF[attId]; - if (!attDef || attState.level === 0) return null; - const rate = getAttunementConversionRate(attId, attState.level); - if (rate <= 0) return null; - return ( -
- {attDef.name}: - -{fmtDec(rate, 2)}/hr -
- ); - })} - - )} -
-
- - - - {/* Elemental Mana Sections */} - {unlockedElements.map((elem) => { - if (!elem) return null; - - // Find attunements that convert TO this element - const convertingAttunements = Object.entries(attunements || {}) - .filter(([attId, attState]) => { - if (!attState.active) return false; - const attDef = ATTUNEMENTS_DEF[attId]; - return attDef?.primaryManaType === elem.id && attDef.conversionRate > 0; - }); - - // Calculate total conversion rate TO this element - const totalConversionRate = convertingAttunements.reduce((total, [attId, attState]) => { - return total + getAttunementConversionRate(attId, attState.level || 1); - }, 0); - - return ( -
-
-
- {elem.sym} - {elem.name} - ({elem.cat}) -
-
- {fmt(elem.current)} - / - {fmt(elem.max)} -
-
- - {/* Progress bar */} -
-
-
- - {/* Conversion info */} - {totalConversionRate > 0 ? ( -
-
- Conversion Rate: - +{fmtDec(totalConversionRate, 2)}/hr -
- {convertingAttunements.length > 0 && ( -
- Source: {convertingAttunements.map(([attId]) => { - const attDef = ATTUNEMENTS_DEF[attId]; - const level = attunements[attId]?.level || 1; - return `${attDef?.name} (Lv.${level})`; - }).join(', ')} -
- )} -
- ) : ( -
No active conversion to this element
- )} - - {/* Show recipe for composite/exotic elements */} - {elem.recipe && ( -
- Recipe: {elem.recipe.map(r => `${ELEMENTS[r]?.sym} ${ELEMENTS[r]?.name || r}`).join(' + ')} -
- )} -
- ); - })} -
- - - ); -} - -ManaTypeBreakdown.displayName = "ManaTypeBreakdown"; diff --git a/src/components/game/stats/StudyStatsSection.tsx b/src/components/game/stats/StudyStatsSection.tsx deleted file mode 100644 index c7908dd..0000000 --- a/src/components/game/stats/StudyStatsSection.tsx +++ /dev/null @@ -1,62 +0,0 @@ -'use client'; - -import { fmtDec } from '@/lib/game/stores'; -import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; -import { BookOpen } from 'lucide-react'; - -// Modular stores -import { useSkillStore, usePrestigeStore } from '@/lib/game/stores'; - -export interface StudyStatsSectionProps { - studySpeedMult: number; - studyCostMult: number; -} - -export function StudyStatsSection({ studySpeedMult, studyCostMult }: StudyStatsSectionProps) { - // Get state from modular stores - const skills = useSkillStore((s) => s.skills); - const prestigeUpgrades = usePrestigeStore((s) => s.prestigeUpgrades); - - return ( - - - - - Study Stats - - - -
-
-
- Study Speed: - ×{fmtDec(studySpeedMult, 2)} -
-
- Quick Learner Bonus: - +{((skills.quickLearner || 0) * 10)}% -
-
-
-
- Study Cost: - {Math.round(studyCostMult * 100)}% -
-
- Focused Mind Bonus: - -{((skills.focusedMind || 0) * 5)}% -
-
-
-
- Progress Retention: - {Math.round((1 + (skills.knowledgeRetention || 0) * 0.2) * 100)}% -
-
-
-
-
- ); -} - -StudyStatsSection.displayName = "StudyStatsSection"; diff --git a/src/components/game/stats/UpgradeEffectsSection.tsx b/src/components/game/stats/UpgradeEffectsSection.tsx deleted file mode 100644 index 84d561c..0000000 --- a/src/components/game/stats/UpgradeEffectsSection.tsx +++ /dev/null @@ -1,86 +0,0 @@ -'use client'; - -import { SKILL_EVOLUTION_PATHS } from '@/lib/game/skill-evolution'; -import { SKILLS_DEF } from '@/lib/game/constants'; -import type { SkillUpgradeChoice } from '@/lib/game/types'; -import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; -import { Badge } from '@/components/ui/badge'; -import { Star } from 'lucide-react'; - -// Modular stores -import { useSkillStore } from '@/lib/game/stores'; - -export function UpgradeEffectsSection() { - // Get state from modular stores - const skillUpgrades = useSkillStore((s) => s.skillUpgrades); - - // Helper function to get all selected skill upgrades - function getAllSelectedUpgrades() { - const upgrades = []; - for (const [skillId, selectedIds] of Object.entries(skillUpgrades)) { - const baseSkillId = skillId.includes('_t') ? skillId.split('_t')[0] : skillId; - const path = SKILL_EVOLUTION_PATHS[baseSkillId]; - if (!path) continue; - for (const tier of path.tiers) { - if (tier.skillId === skillId) { - for (const upgradeId of selectedIds) { - const upgrade = tier.upgrades.find(u => u.id === upgradeId); - if (upgrade) { - upgrades.push({ skillId, upgrade }); - } - } - } - } - } - return upgrades; - } - - const selectedUpgrades = getAllSelectedUpgrades(); - - return ( - - - - - Active Skill Upgrades ({selectedUpgrades.length}) - - - - {selectedUpgrades.length === 0 ? ( -
No skill upgrades selected yet. Level skills to 5 or 10 to choose upgrades.
- ) : ( -
- {selectedUpgrades.map(({ skillId, upgrade }) => ( -
-
- {upgrade.name} - - {SKILLS_DEF[skillId]?.name || skillId} - -
-
{upgrade.desc}
- {upgrade.effect && upgrade.effect.type === 'multiplier' && ( -
- +{Math.round((upgrade.effect.value - 1) * 100)}% {upgrade.effect.stat} -
- )} - {upgrade.effect && upgrade.effect.type === 'bonus' && ( -
- +{upgrade.effect.value} {upgrade.effect.stat} -
- )} - {upgrade.effect && upgrade.effect.type === 'special' && ( -
- ⚡ {upgrade.effect.specialDesc || 'Special effect active'} -
- )} -
- ))} -
- )} -
-
- ); -} - -UpgradeEffectsSection.displayName = "UpgradeEffectsSection"; diff --git a/src/components/game/stats/index.tsx b/src/components/game/stats/index.tsx deleted file mode 100644 index 6b51408..0000000 --- a/src/components/game/stats/index.tsx +++ /dev/null @@ -1,11 +0,0 @@ -export { ManaStatsSection } from './ManaStatsSection'; -export type { ManaStatsSectionProps } from './ManaStatsSection'; - -export { CombatStatsSection } from './CombatStatsSection'; -export type { CombatStatsSectionProps } from './CombatStatsSection'; - -export { StudyStatsSection } from './StudyStatsSection'; -export type { StudyStatsSectionProps } from './StudyStatsSection'; - -export { UpgradeEffectsSection } from './UpgradeEffectsSection'; -export type { UpgradeEffectsSectionProps } from './UpgradeEffectsSection'; diff --git a/src/lib/game/__tests__/computed-stats.test.ts b/src/lib/game/__tests__/computed-stats.test.ts index c570733..9e88cc4 100755 --- a/src/lib/game/__tests__/computed-stats.test.ts +++ b/src/lib/game/__tests__/computed-stats.test.ts @@ -11,7 +11,7 @@ import { computeClickMana, getMeditationBonus, getIncursionStrength, -} from '../computed-stats'; +} from '../utils'; import { MAX_DAY, INCURSION_START_DAY, HOURS_PER_TICK } from '../constants'; describe('fmt', () => { diff --git a/src/lib/game/attunements/data.ts b/src/lib/game/attunements/data.ts deleted file mode 100644 index 082fa05..0000000 --- a/src/lib/game/attunements/data.ts +++ /dev/null @@ -1,345 +0,0 @@ -// ─── Attunement Definitions ───────────────────────────────────────── -// Data file containing all attunement definitions - -import type { AttunementDef, AttunementType } from '../types'; - -export const ATTUNEMENTS: Record = { - // ═══════════════════════════════════════════════════════════════════════════ - // ENCHANTER - Right Hand - // The starting attunement. Grants access to enchanting and transference magic. - // ═══════════════════════════════════════════════════════════════════════════ - enchanter: { - id: 'enchanter', - name: 'Enchanter', - slot: 'rightHand', - description: 'Channel mana through your right hand to imbue equipment with magical properties.', - capability: 'Unlock enchanting. Apply enchantments using transference mana.', - primaryManaType: 'transference', - rawManaRegen: 0.5, - autoConvertRate: 0.2, // 0.2 transference per hour per raw regen - icon: 'Wand2', - color: '#8B5CF6', // Purple - skills: { - // Core enchanting skills - enchanting: { - name: 'Enchanting', - desc: 'Apply magical effects to equipment', - cat: 'enchanter', - max: 10, - base: 100, - studyTime: 8, - }, - efficientEnchant: { - name: 'Efficient Enchanting', - desc: 'Reduce enchantment mana costs', - cat: 'enchanter', - max: 5, - base: 200, - studyTime: 12, - req: { enchanting: 3 }, - }, - - enchantSpeed: { - name: 'Swift Enchanting', - desc: 'Faster enchantment application', - cat: 'enchanter', - max: 5, - base: 175, - studyTime: 10, - req: { enchanting: 2 }, - }, - transferenceMastery: { - name: 'Transference Mastery', - desc: 'Increased transference mana pool and regen', - cat: 'enchanter', - max: 10, - base: 250, - studyTime: 15, - }, - }, - }, - // ... rest of attunement definitions (same as original data.ts) - caster: { - id: 'caster', - name: 'Caster', - slot: 'leftHand', - description: 'Shape mana into devastating spell patterns through your left hand.', - capability: 'Form mana shaping. +25% spell damage bonus.', - primaryManaType: 'form', - rawManaRegen: 0.3, - autoConvertRate: 0.15, - icon: 'Hand', - color: '#3B82F6', // Blue - skills: { - spellShaping: { - name: 'Spell Shaping', - desc: 'Increase spell damage and efficiency', - cat: 'caster', - max: 10, - base: 100, - studyTime: 8, - }, - quickCast: { - name: 'Quick Cast', - desc: 'Faster spell casting speed', - cat: 'caster', - max: 10, - base: 120, - studyTime: 8, - }, - spellEcho: { - name: 'Spell Echo', - desc: 'Chance to cast spells twice', - cat: 'caster', - max: 5, - base: 300, - studyTime: 15, - req: { spellShaping: 5 }, - }, - formMastery: { - name: 'Form Mastery', - desc: 'Increased form mana pool and regen', - cat: 'caster', - max: 10, - base: 250, - studyTime: 15, - }, - }, - }, - seer: { - id: 'seer', - name: 'Seer', - slot: 'head', - description: 'See beyond the veil. Reveal hidden truths and enemy weaknesses.', - capability: 'Reveal floor weaknesses. +20% critical hit chance.', - primaryManaType: 'vision', - rawManaRegen: 0.2, - autoConvertRate: 0.1, - icon: 'Eye', - color: '#F59E0B', // Amber - skills: { - insight: { - name: 'Insight', - desc: 'Increased critical hit chance', - cat: 'seer', - max: 10, - base: 100, - studyTime: 8, - }, - revealWeakness: { - name: 'Reveal Weakness', - desc: 'Show enemy elemental weaknesses', - cat: 'seer', - max: 5, - base: 200, - studyTime: 12, - }, - foresight: { - name: 'Foresight', - desc: 'Chance to anticipate and dodge attacks', - cat: 'seer', - max: 5, - base: 250, - studyTime: 15, - req: { insight: 5 }, - }, - visionMastery: { - name: 'Vision Mastery', - desc: 'Increased vision mana pool and regen', - cat: 'seer', - max: 10, - base: 250, - studyTime: 15, - }, - }, - }, - warden: { - id: 'warden', - name: 'Warden', - slot: 'back', - description: 'Shield yourself with protective wards and barriers.', - capability: 'Generate protective shields. -10% damage taken.', - primaryManaType: 'barrier', - rawManaRegen: 0.25, - autoConvertRate: 0.12, - icon: 'Shield', - color: '#10B981', // Green - skills: { - warding: { - name: 'Warding', - desc: 'Generate protective shields', - cat: 'warden', - max: 10, - base: 100, - studyTime: 8, - }, - fortitude: { - name: 'Fortitude', - desc: 'Reduce damage taken', - cat: 'warden', - max: 10, - base: 150, - studyTime: 10, - }, - reflection: { - name: 'Reflection', - desc: 'Chance to reflect damage to attacker', - cat: 'warden', - max: 5, - base: 300, - studyTime: 15, - req: { warding: 5 }, - }, - barrierMastery: { - name: 'Barrier Mastery', - desc: 'Increased barrier mana pool and regen', - cat: 'warden', - max: 10, - base: 250, - studyTime: 15, - }, - }, - }, - invoker: { - id: 'invoker', - name: 'Invoker', - slot: 'chest', - description: 'Form pacts with spire guardians and channel their elemental power.', - capability: 'Pact with guardians. Gain mana types from pacted guardians.', - primaryManaType: null, // Uses guardian types instead - rawManaRegen: 0.4, - autoConvertRate: 0, // No auto-convert; mana comes from guardian pacts - icon: 'Heart', - color: '#EF4444', // Red - skills: { - pactMaking: { - name: 'Pact Making', - desc: 'Form stronger pacts with guardians', - cat: 'invoker', - max: 10, - base: 100, - studyTime: 8, - }, - guardianChannel: { - name: 'Guardian Channeling', - desc: 'Channel guardian powers more effectively', - cat: 'invoker', - max: 10, - base: 150, - studyTime: 10, - }, - elementalBurst: { - name: 'Elemental Burst', - desc: 'Unleash stored guardian energy', - cat: 'invoker', - max: 5, - base: 300, - studyTime: 15, - req: { pactMaking: 5, guardianChannel: 3 }, - }, - soulResonance: { - name: 'Soul Resonance', - desc: 'Deep bond with pacted guardians', - cat: 'invoker', - max: 5, - base: 400, - studyTime: 20, - req: { pactMaking: 8 }, - }, - }, - }, - strider: { - id: 'strider', - name: 'Strider', - slot: 'leftLeg', - description: 'Move with supernatural speed and grace.', - capability: 'Enhanced mobility. +15% attack speed.', - primaryManaType: 'flow', - rawManaRegen: 0.3, - autoConvertRate: 0.15, - icon: 'Zap', - color: '#06B6D4', // Cyan - skills: { - swiftness: { - name: 'Swiftness', - desc: 'Increased attack and movement speed', - cat: 'strider', - max: 10, - base: 100, - studyTime: 8, - }, - evasive: { - name: 'Evasive', - desc: 'Chance to avoid damage', - cat: 'strider', - max: 5, - base: 200, - studyTime: 12, - }, - momentum: { - name: 'Momentum', - desc: 'Build speed over consecutive attacks', - cat: 'strider', - max: 5, - base: 250, - studyTime: 15, - req: { swiftness: 5 }, - }, - flowMastery: { - name: 'Flow Mastery', - desc: 'Increased flow mana pool and regen', - cat: 'strider', - max: 10, - base: 250, - studyTime: 15, - }, - }, - }, - anchor: { - id: 'anchor', - name: 'Anchor', - slot: 'rightLeg', - description: 'Stand firm against any force. Your foundation is unshakeable.', - capability: 'Increased stability. +100 max mana.', - primaryManaType: 'stability', - rawManaRegen: 0.35, - autoConvertRate: 0.18, - icon: 'Mountain', - color: '#78716C', // Stone gray - skills: { - grounding: { - name: 'Grounding', - desc: 'Increased max mana and stability', - cat: 'anchor', - max: 10, - base: 100, - studyTime: 8, - }, - endurance: { - name: 'Endurance', - desc: 'Reduced mana costs when below 50% mana', - cat: 'anchor', - max: 5, - base: 200, - studyTime: 12, - }, - ironWill: { - name: 'Iron Will', - desc: 'Prevent mana drain effects', - cat: 'anchor', - max: 5, - base: 250, - studyTime: 15, - req: { grounding: 5 }, - }, - stabilityMastery: { - name: 'Stability Mastery', - desc: 'Increased stability mana pool and regen', - cat: 'anchor', - max: 10, - base: 250, - studyTime: 15, - }, - }, - }, -}; diff --git a/src/lib/game/attunements/index.ts b/src/lib/game/attunements/index.ts deleted file mode 100644 index 3de5a0f..0000000 --- a/src/lib/game/attunements/index.ts +++ /dev/null @@ -1,30 +0,0 @@ -// ─── Attunement System ───────────────────────────────────────────────── -// Attunements are powerful magical bonds tied to specific body locations -// Each grants a unique capability, primary mana type, and skill tree - -// Re-export types -export type { - AttunementSlot, - AttunementType, - AttunementDef, - AttunementState, - ManaType -} from './types'; - -export { - ATTUNEMENT_SLOTS, - ATTUNEMENT_SLOT_NAMES -} from './types'; - -// Re-export data -export { ATTUNEMENTS } from './data'; - -// Re-export utils -export { - getAttunementForSlot, - getStartingAttunement, - isAttunementUnlocked, - getTotalAttunementRegen, - getManaTypeName, - getManaTypeColor, -} from './utils'; diff --git a/src/lib/game/attunements/types.ts b/src/lib/game/attunements/types.ts deleted file mode 100644 index c4b1a51..0000000 --- a/src/lib/game/attunements/types.ts +++ /dev/null @@ -1,100 +0,0 @@ -// ─── Attunement Types ───────────────────────────────────────────────────────── - -export type AttunementSlot = - | 'rightHand' - | 'leftHand' - | 'head' - | 'back' - | 'chest' - | 'leftLeg' - | 'rightLeg'; - -export const ATTUNEMENT_SLOTS: AttunementSlot[] = [ - 'rightHand', - 'leftHand', - 'head', - 'back', - 'chest', - 'leftLeg', - 'rightLeg', -]; - -// Slot display names -export const ATTUNEMENT_SLOT_NAMES: Record = { - rightHand: 'Right Hand', - leftHand: 'Left Hand', - head: 'Head', - back: 'Back', - chest: 'Heart', - leftLeg: 'Left Leg', - rightLeg: 'Right Leg', -}; - -// ─── Mana Types ─────────────────────────────────────────────────────────────── - -export type ManaType = - // Primary mana types from attunements - | 'transference' // Enchanter - moving/enchanting - | 'form' // Caster - shaping spells - | 'vision' // Seer - perception/revelation - | 'barrier' // Warden - protection/defense - | 'flow' // Strider - movement/swiftness - | 'stability' // Anchor - grounding/endurance - // Guardian pact types (Invoker) - | 'fire' - | 'water' - | 'earth' - | 'air' - | 'light' - | 'dark' - | 'life' - | 'death' - // Raw mana - | 'raw'; - -// ─── Attunement Types ───────────────────────────────────────────────────────── - -export type AttunementType = - | 'enchanter' - | 'caster' - | 'seer' - | 'warden' - | 'invoker' - | 'strider' - | 'anchor'; - -// ─── Attunement Definition ──────────────────────────────────────────────────── - -export interface AttunementDef { - id: AttunementType; - name: string; - slot: AttunementSlot; - description: string; - capability: string; // What this attunement unlocks - primaryManaType: ManaType | null; // null for Invoker (uses guardian types) - rawManaRegen: number; // Base raw mana regen bonus - autoConvertRate: number; // Raw mana -> primary mana per hour - skills: Record; // Attunement-specific skills - icon: string; // Lucide icon name - color: string; // Theme color -} - -// ─── Attunement State ───────────────────────────────────────────────────────── - -export interface AttunementState { - unlocked: boolean; - level: number; // Attunement level (from challenges) - manaPool: number; // Current primary mana - maxMana: number; // Max primary mana pool -} - -// Skill definition (imported from types but re-defined here for clarity) -export interface SkillDef { - name: string; - desc: string; - cat: string; - max: number; - base: number; - studyTime: number; - req?: Record; -} diff --git a/src/lib/game/attunements/utils.ts b/src/lib/game/attunements/utils.ts deleted file mode 100644 index 66a9b8b..0000000 --- a/src/lib/game/attunements/utils.ts +++ /dev/null @@ -1,94 +0,0 @@ -// ─── Attunement Helper Functions ───────────────────────── - -import type { AttunementSlot, AttunementType, AttunementState, ManaType, AttunementDef } from './types'; -import { ATTUNEMENTS } from './data'; - -/** - * Get the attunement for a specific body slot - */ -export function getAttunementForSlot(slot: AttunementSlot): AttunementDef | undefined { - return Object.values(ATTUNEMENTS).find(a => a.slot === slot) as AttunementDef | undefined; -} - -/** - * Get the starting attunement (Enchanter - right hand) - */ -export function getStartingAttunement(): AttunementDef { - return ATTUNEMENTS.enchanter; -} - -/** - * Check if an attunement is unlocked for the player - */ -export function isAttunementUnlocked( - attunementStates: Record, - attunementType: AttunementType -): boolean { - return attunementStates[attunementType]?.unlocked ?? false; -} - -/** - * Get total raw mana regen from all unlocked attunements - */ -export function getTotalAttunementRegen( - attunementStates: Record -): number { - let total = 0; - for (const [type, state] of Object.entries(attunementStates)) { - if (state.unlocked) { - const def = ATTUNEMENTS[type as AttunementType]; - if (def) { - total += def.rawManaRegen * (1 + state.level * 0.1); // +10% per level - } - } - } - return total; -} - -/** - * Get mana type display name - */ -export function getManaTypeName(type: ManaType): string { - const names: Record = { - raw: 'Raw Mana', - transference: 'Transference', - form: 'Form', - vision: 'Vision', - barrier: 'Barrier', - flow: 'Flow', - stability: 'Stability', - fire: 'Fire', - water: 'Water', - earth: 'Earth', - air: 'Air', - light: 'Light', - dark: 'Dark', - life: 'Life', - death: 'Death', - }; - return names[type] || type; -} - -/** - * Get mana type color - */ -export function getManaTypeColor(type: ManaType): string { - const colors: Record = { - raw: '#A78BFA', // Light purple - transference: '#8B5CF6', // Purple - form: '#3B82F6', // Blue - vision: '#F59E0B', // Amber - barrier: '#10B981', // Green - flow: '#06B6D4', // Cyan - stability: '#78716C', // Stone - fire: '#EF4444', // Red - water: '#3B82F6', // Blue - earth: '#A16207', // Brown - air: '#94A3B8', // Slate - light: '#FCD34D', // Yellow - dark: '#6B7280', // Gray - life: '#22C55E', // Green - death: '#7C3AED', // Violet - }; - return colors[type] || '#A78BFA'; -} diff --git a/src/lib/game/computed-stats.ts b/src/lib/game/computed-stats.ts deleted file mode 100755 index 7f4d397..0000000 --- a/src/lib/game/computed-stats.ts +++ /dev/null @@ -1,12 +0,0 @@ -// ─── Computed Stats and Utility Functions ─────────────────────────────────────── -// This module now re-exports from focused utility modules for better organization -// -// The functions have been split into: -// - ./utils/formatting.ts - Number formatting (fmt, fmtDec) -// - ./utils/floor-utils.ts - Floor functions (getFloorMaxHP, getFloorElement) -// - ./utils/mana-utils.ts - Mana calculations (computeMaxMana, computeElementMax, etc.) -// - ./utils/combat-utils.ts - Combat functions (calcDamage, calcInsight, getTotalDPS, etc.) -// -// All exports are maintained for backward compatibility. - -export * from './utils/index'; diff --git a/src/lib/game/effects.ts.fix b/src/lib/game/effects.ts.fix deleted file mode 100644 index 59d378e..0000000 --- a/src/lib/game/effects.ts.fix +++ /dev/null @@ -1,12 +0,0 @@ - -/** - * Helper to get unified effects from game state - */ -export function getUnifiedEffects(state: Pick): UnifiedEffects { - return computeAllEffects( - state.skillUpgrades || {}, - state.skillTiers || {}, - state.equipmentInstances || {}, - state.equippedInstances || {} - ); -} diff --git a/src/lib/game/formatting.ts b/src/lib/game/formatting.ts deleted file mode 100755 index 0da5648..0000000 --- a/src/lib/game/formatting.ts +++ /dev/null @@ -1,46 +0,0 @@ -// ─── Shared Formatting Utilities ───────────────────────────────────────────────── -// Utility functions for consistent formatting across components - -import { ELEMENTS } from '@/lib/game/constants'; -import type { SpellCost } from '@/lib/game/types'; - -// Re-export number formatting functions from computed-stats.ts -export { fmt, fmtDec } from './computed-stats'; - -/** - * Format a spell cost for display - */ -export function formatSpellCost(cost: SpellCost): string { - if (cost.type === 'raw') { - return `${cost.amount} raw`; - } - const elemDef = ELEMENTS[cost.element || '']; - return `${cost.amount} ${elemDef?.sym || '?'}`; -} - -/** - * Get the display color for a spell cost - */ -export function getSpellCostColor(cost: SpellCost): string { - if (cost.type === 'raw') { - return '#60A5FA'; // Blue for raw mana - } - return ELEMENTS[cost.element || '']?.color || '#9CA3AF'; -} - -/** - * Format study time in hours to human-readable string - */ -export function formatStudyTime(hours: number): string { - if (hours < 1) return `${Math.round(hours * 60)}m`; - return `${hours.toFixed(1)}h`; -} - -/** - * Format time (hour of day) to HH:MM format - */ -export function formatHour(hour: number): string { - const h = Math.floor(hour); - const m = Math.floor((hour % 1) * 60); - return `${h.toString().padStart(2, '0')}:${m.toString().padStart(2, '0')}`; -} diff --git a/src/lib/game/navigation-slice.ts b/src/lib/game/navigation-slice.ts deleted file mode 100755 index 0b5fd77..0000000 --- a/src/lib/game/navigation-slice.ts +++ /dev/null @@ -1,75 +0,0 @@ -// ─── Navigation Slice ───────────────────────────────────────────────────────── -// Actions for floor navigation: climbing direction and manual floor changes - -import type { GameState } from './types'; -import { getFloorMaxHP } from './computed-stats'; - -// ─── Navigation Actions Interface ───────────────────────────────────────────── - -export interface NavigationActions { - // Floor Navigation - setClimbDirection: (direction: 'up' | 'down') => void; - changeFloor: (direction: 'up' | 'down') => void; - resetFloorHP: () => void; -} - -// ─── Navigation Slice Factory ───────────────────────────────────────────────── - -export function createNavigationSlice( - set: (partial: Partial | ((state: GameState) => Partial)) => void, - get: () => GameState -): NavigationActions { - return { - // Set the climbing direction (up or down) - setClimbDirection: (direction: 'up' | 'down') => { - set({ climbDirection: direction }); - }, - - // Manually change floors by one - changeFloor: (direction: 'up' | 'down') => { - const state = get(); - const currentFloor = state.currentFloor; - - // Calculate next floor - const nextFloor = direction === 'up' - ? Math.min(currentFloor + 1, 100) - : Math.max(currentFloor - 1, 1); - - // Can't stay on same floor - if (nextFloor === currentFloor) return; - - // Mark current floor as cleared (it will respawn when we come back) - const clearedFloors = { ...state.clearedFloors }; - clearedFloors[currentFloor] = true; - - // Check if next floor was cleared (needs respawn) - const nextFloorCleared = clearedFloors[nextFloor]; - if (nextFloorCleared) { - // Respawn the floor - delete clearedFloors[nextFloor]; - } - - set({ - currentFloor: nextFloor, - floorMaxHP: getFloorMaxHP(nextFloor), - floorHP: getFloorMaxHP(nextFloor), - maxFloorReached: Math.max(state.maxFloorReached, nextFloor), - clearedFloors, - climbDirection: direction, - equipmentSpellStates: state.equipmentSpellStates.map(s => ({ ...s, castProgress: 0 })), - log: [`🚶 Moved to floor ${nextFloor}${nextFloorCleared ? ' (respawned)' : ''}.`, ...state.log.slice(0, 49)], - }); - }, - - // Reset current floor HP to max (useful when floor HP gets stuck) - resetFloorHP: () => { - const state = get(); - const maxHP = getFloorMaxHP(state.currentFloor); - set({ - floorMaxHP: maxHP, - floorHP: maxHP, - log: [`🔄 Floor ${state.currentFloor} HP reset to full.`, ...state.log.slice(0, 49)], - }); - }, - }; -} diff --git a/src/lib/game/store-modules/computed-stats.ts b/src/lib/game/store-modules/computed-stats.ts index da861e1..c49f561 100644 --- a/src/lib/game/store-modules/computed-stats.ts +++ b/src/lib/game/store-modules/computed-stats.ts @@ -5,50 +5,36 @@ import type { GameState, SpellCost, StudyTarget } from '../types'; import type { ComputedEffects } from '../upgrade-effects.types'; import type { UnifiedEffects } from '../effects'; -import { SPELLS_DEF, GUARDIANS, ELEMENT_OPPOSITES, SKILLS_DEF, HOURS_PER_TICK, TICK_MS, INCURSION_START_DAY, MAX_DAY, ELEMENTS } from '../constants'; +import { SPELLS_DEF, GUARDIANS, ELEMENT_OPPOSITES, HOURS_PER_TICK, TICK_MS, INCURSION_START_DAY, MAX_DAY, ELEMENTS } from '../constants'; import { getUnifiedEffects } from '../effects'; import { getTotalAttunementRegen, getTotalAttunementConversionDrain } from '../data/attunements'; import { hasSpecial, SPECIAL_EFFECTS } from '../special-effects'; -// Helper to get effective skill level accounting for tiers -function getEffectiveSkillLevel( - skills: Record, - baseSkillId: string, - skillTiers: Record = {} -): { level: number; tier: number; tierMultiplier: number } { - const currentTier = skillTiers[baseSkillId] || 1; - const tieredSkillId = currentTier > 1 ? `${baseSkillId}_t${currentTier}` : baseSkillId; - const level = skills[tieredSkillId] || skills[baseSkillId] || 0; - const tierMultiplier = Math.pow(10, currentTier - 1); - return { level, tier: currentTier, tierMultiplier }; -} - export function computeMaxMana( state: GameState, effects?: ComputedEffects | UnifiedEffects ): number { const pu = state.prestigeUpgrades; - const skillMult = (effects as any)?.skillLevelMultiplier || 1; - const base = 100 + ((state.skills || {}).manaWell || 0) * 100 * skillMult + ((pu || {}).manaWell || 0) * 500; - + const base = 100 + ((pu || {}).manaWell || 0) * 500; + // Check if we need to compute effects from equipment if (!effects && state.equipmentInstances && state.equippedInstances) { effects = getUnifiedEffects(state as any); } - + let maxMana: number; if (effects) { maxMana = Math.floor((base + effects.maxManaBonus) * effects.maxManaMultiplier); } else { maxMana = base; } - + if (effects && hasSpecial(effects, SPECIAL_EFFECTS.MANA_CONDENSE)) { const totalGathered = state.totalManaGathered || 0; const condensesBonus = Math.floor(totalGathered / 1000); maxMana = Math.floor(maxMana * (1 + condensesBonus * 0.01)); } - + return maxMana; } @@ -58,15 +44,15 @@ export function computeElementMax( element?: string ): number { const pu = state.prestigeUpgrades; - const base = 10 + (state.skills.elemAttune || 0) * 50 + (pu.elementalAttune || 0) * 25; - + const base = 10 + (pu.elementalAttune || 0) * 25; + let adjustedBase = base; if (element && state.unlockedManaTypeUpgrades) { const typeUpgrades = state.unlockedManaTypeUpgrades.filter(u => u.typeId === element); const totalLevels = typeUpgrades.reduce((sum, u) => sum + u.level, 0); adjustedBase = base + (totalLevels * 10); } - + if (effects) { let bonus = effects.elementCapBonus || 0; if (element && (effects as UnifiedEffects).perElementCapBonus) { @@ -86,17 +72,16 @@ export function computeRegen( ): number { const pu = state.prestigeUpgrades; const temporalBonus = 1 + (pu.temporalEcho || 0) * 0.1; - const skillMult = (effects as any)?.skillLevelMultiplier || 1; - const base = 2 + (state.skills.manaFlow || 0) * 1 * skillMult + (state.skills.manaSpring || 0) * 2 * skillMult + (pu.manaFlow || 0) * 0.5; - + const base = 2 + (pu.manaFlow || 0) * 0.5; + let regen = base * temporalBonus; const attunementRegen = getTotalAttunementRegen(state.attunements || {}); regen += attunementRegen; - + if (!effects && state.equipmentInstances && state.equippedInstances) { effects = getUnifiedEffects(state as any); } - + if (effects) { regen = (regen + effects.regenBonus + effects.permanentRegenBonus) * effects.regenMultiplier; } @@ -127,13 +112,12 @@ export function computeClickMana( state: GameState, effects?: ComputedEffects | UnifiedEffects ): number { - const skillMult = (effects as any)?.skillLevelMultiplier || 1; - const base = 1 + (state.skills.manaTap || 0) * 1 * skillMult + (state.skills.manaSurge || 0) * 3 * skillMult; - + const base = 1; + if (!effects && state.equipmentInstances && state.equippedInstances) { effects = getUnifiedEffects(state as any); } - + if (effects) { return Math.floor((base + effects.clickManaBonus) * effects.clickManaMultiplier); } @@ -156,14 +140,12 @@ export function calcDamage( ): number { const sp = SPELLS_DEF[spellId]; if (!sp) return 5; - const skills = state.skills; - const skillMult = (effects as any)?.skillLevelMultiplier || 1; - const baseDmg = sp.dmg + (skills.combatTrain || 0) * 5 * skillMult; - const pct = 1 + (skills.arcaneFury || 0) * 0.1 * skillMult; - const elemMasteryBonus = 1 + (skills.elementalMastery || 0) * 0.15 * skillMult; - const critChance = (skills.precision || 0) * 0.05; + const baseDmg = sp.dmg; + const pct = 1; + const elemMasteryBonus = 1; + const critChance = 0; const pactMult = state.signedPacts.reduce((m, f) => m * ((GUARDIANS as any)[f]?.pact || 1), 1); - + let damage = baseDmg * pct * pactMult * elemMasteryBonus; if (floorElem) { damage *= getElementalBonus(sp.elem, floorElem); @@ -176,20 +158,13 @@ export function calcDamage( export function calcInsight(state: Pick): number { const pu = state.prestigeUpgrades; - const skillBonus = 1 + (state.skills.insightHarvest || 0) * 0.1; - const mult = (1 + (pu.insightAmp || 0) * 0.25) * skillBonus; + const mult = (1 + (pu.insightAmp || 0) * 0.25); return Math.floor((state.maxFloorReached * 15 + state.totalManaGathered / 500 + state.signedPacts.length * 150) * mult); } -export function getMeditationBonus(meditateTicks: number, skills: Record, meditationEfficiency: number = 1): number { - const hasMeditation = skills.meditation === 1; - const hasDeepTrance = skills.deepTrance === 1; - const hasVoidMeditation = skills.voidMeditation === 1; +export function getMeditationBonus(meditateTicks: number, meditationEfficiency: number = 1): number { const hours = meditateTicks * HOURS_PER_TICK; let bonus = 1 + Math.min(hours / 4, 0.5); - if (hasMeditation && hours >= 4) bonus = 2.5; - if (hasDeepTrance && hours >= 6) bonus = 3.0; - if (hasVoidMeditation && hours >= 8) bonus = 5.0; bonus *= meditationEfficiency; return bonus; } diff --git a/src/lib/game/store/index.ts b/src/lib/game/store/index.ts deleted file mode 100755 index d59c983..0000000 --- a/src/lib/game/store/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -// ─── Store Module Exports ───────────────────────────────────────────────────── -// Re-exports from main store and adds new computed utilities -// This allows gradual migration while keeping existing functionality - -// Re-export everything from the main store -export * from '../store'; - -// Export new computed utilities -export * from './computed'; diff --git a/src/lib/game/stores/combat-actions.ts b/src/lib/game/stores/combat-actions.ts index ea9a520..cd1933c 100644 --- a/src/lib/game/stores/combat-actions.ts +++ b/src/lib/game/stores/combat-actions.ts @@ -10,7 +10,6 @@ import { usePrestigeStore } from './prestigeStore'; export function processCombatTick( get: () => CombatState, set: (state: Partial) => void, - skills: Record, rawMana: number, elements: Record, maxMana: number, @@ -36,9 +35,8 @@ export function processCombatTick( return { rawMana, elements, logMessages, totalManaGathered }; } - // Calculate cast speed - const baseAttackSpeed = 1 + (skills.quickCast || 0) * 0.05; - const totalAttackSpeed = baseAttackSpeed * attackSpeedMult; + // Calculate cast speed (no skill bonus) + const totalAttackSpeed = attackSpeedMult; const spellCastSpeed = spellDef.castSpeed || 1; const progressPerTick = HOURS_PER_TICK * spellCastSpeed * totalAttackSpeed; @@ -58,12 +56,12 @@ export function processCombatTick( // Calculate base damage const floorElement = getFloorElement(currentFloor); const damage = calcDamage( - { skills, signedPacts: usePrestigeStore.getState().signedPacts }, + { skills: {}, signedPacts: usePrestigeStore.getState().signedPacts }, spellId, floorElement, ); - // Let gameStore apply damage modifiers (executioner, berserker, spell echo) + // Let gameStore apply damage modifiers (executioner, berserker) const result = onDamageDealt(damage); rawMana = result.rawMana; elements = result.elements; @@ -114,7 +112,7 @@ export function processCombatTick( // Calculate damage const eFloorElement = getFloorElement(currentFloor); const eDamage = calcDamage( - { skills, signedPacts: usePrestigeStore.getState().signedPacts }, + { skills: {}, signedPacts: usePrestigeStore.getState().signedPacts }, eSpell.spellId, eFloorElement, ); @@ -151,11 +149,11 @@ export function makeInitialSpells(spellsToKeep: string[] = []): Record = { manaBolt: { learned: true, level: 1, studyProgress: 0 }, }; - + // Add kept spells for (const spellId of spellsToKeep) { startSpells[spellId] = { learned: true, level: 1, studyProgress: 0 }; } - + return startSpells; } diff --git a/src/lib/game/stores/combatStore.ts b/src/lib/game/stores/combatStore.ts index a695e15..4e94e99 100755 --- a/src/lib/game/stores/combatStore.ts +++ b/src/lib/game/stores/combatStore.ts @@ -17,60 +17,60 @@ export interface CombatState { floorHP: number; floorMaxHP: number; maxFloorReached: number; - + // Action state activeSpell: string; currentAction: GameAction; castProgress: number; - + // Spire mode spireMode: boolean; - + // Room system for special floors currentRoom: FloorState; - + // Spire climbing state clearedFloors: Record; climbDirection: 'up' | 'down' | null; isDescending: boolean; - + // Golemancy (summoned golems) golemancy: GolemancyState; - + // Equipment spell states for multi-casting equipmentSpellStates: EquipmentSpellState[]; - + // Combat special effect tracking comboHitCount: number; floorHitCount: number; - + // Spells spells: Record; - + // Activity Log (for Spire Mode UI) activityLog: ActivityLogEntry[]; - + // Achievements achievements: AchievementState; - + // Stats tracking totalSpellsCast: number; totalDamageDealt: number; totalCraftsCompleted: number; - + // Actions setCurrentFloor: (floor: number) => void; advanceFloor: () => void; setFloorHP: (hp: number) => void; setMaxFloorReached: (floor: number) => void; - + setAction: (action: GameAction) => void; setSpell: (spellId: string) => void; setCastProgress: (progress: number) => void; - + // Room state setCurrentRoom: (room: FloorState) => void; - + // Spire climbing setClimbDirection: (direction: 'up' | 'down' | null) => void; setClearedFloor: (floor: number, cleared: boolean) => void; @@ -79,29 +79,28 @@ export interface CombatState { exitSpireMode: () => void; startClimbUp: () => void; startClimbDown: () => void; - + // Golemancy toggleGolem: (golemId: string) => void; setEnabledGolems: (golemIds: string[]) => void; - + // Spells learnSpell: (spellId: string) => void; setSpellState: (spellId: string, state: Partial) => void; - + // Activity Log addActivityLog: (eventType: ActivityEventType, message: string, details?: ActivityLogEntry['details']) => void; - + // Stats incrementSpellsCast: () => void; addDamageDealt: (damage: number) => void; incrementCraftsCompleted: () => void; - + // Spire mode enterSpireMode: () => void; - + // Combat tick processCombatTick: ( - skills: Record, rawMana: number, elements: Record, maxMana: number, @@ -109,10 +108,10 @@ export interface CombatState { onFloorCleared: (floor: number, wasGuardian: boolean) => void, onDamageDealt: (damage: number) => { rawMana: number; elements: Record }, ) => { rawMana: number; elements: Record; logMessages: string[]; totalManaGathered: number }; - + // Reset resetCombat: (startFloor: number, spellsToKeep?: string[]) => void; - + // Debug helpers debugSetFloor: (floor: number) => void; resetFloorHP: () => void; @@ -130,48 +129,48 @@ export const useCombatStore = create()( currentAction: 'meditate', castProgress: 0, spireMode: false, - + // Room system currentRoom: generateFloorState(1), - + // Spire climbing state clearedFloors: {}, climbDirection: null, isDescending: false, - + // Golemancy golemancy: { enabledGolems: [], summonedGolems: [], lastSummonFloor: 0, }, - + // Equipment spell states equipmentSpellStates: [], - + // Combat tracking comboHitCount: 0, floorHitCount: 0, - + // Spells spells: { manaBolt: { learned: true, level: 1, studyProgress: 0 }, }, - + // Activity Log activityLog: [], - + // Achievements achievements: { unlocked: [], progress: {}, }, - + // Stats tracking totalSpellsCast: 0, totalDamageDealt: 0, totalCraftsCompleted: 0, - + setCurrentFloor: (floor: number) => { set({ currentFloor: floor, @@ -179,7 +178,7 @@ export const useCombatStore = create()( floorMaxHP: getFloorMaxHP(floor), }); }, - + advanceFloor: () => { set((state) => { const newFloor = Math.min(state.currentFloor + 1, 100); @@ -192,52 +191,52 @@ export const useCombatStore = create()( }; }); }, - + setFloorHP: (hp: number) => { set({ floorHP: Math.max(0, hp) }); }, - + setMaxFloorReached: (floor: number) => { set((state) => ({ maxFloorReached: Math.max(state.maxFloorReached, floor), })); }, - + setAction: (action: GameAction) => { set({ currentAction: action }); }, - + setSpell: (spellId: string) => { const state = get(); if (state.spells[spellId]?.learned) { set({ activeSpell: spellId }); } }, - + setCastProgress: (progress: number) => { set({ castProgress: progress }); }, - + // Room state setCurrentRoom: (room: FloorState) => { set({ currentRoom: room }); }, - + // Spire climbing setClimbDirection: (direction: 'up' | 'down' | null) => { set({ climbDirection: direction }); }, - + setClearedFloor: (floor: number, cleared: boolean) => { set((state) => ({ clearedFloors: { ...state.clearedFloors, [floor]: cleared }, })); }, - + setIsDescending: (descending: boolean) => { set({ isDescending: descending }); }, - + climbDownFloor: () => { set((s) => { if (s.currentFloor <= 1) return s; @@ -251,41 +250,41 @@ export const useCombatStore = create()( }; }); }, - + exitSpireMode: () => { set({ spireMode: false, currentAction: 'meditate', climbDirection: null, isDescending: false }); }, - + startClimbUp: () => set({ climbDirection: 'up', currentAction: 'climb' }), - + startClimbDown: () => set({ climbDirection: 'down', currentAction: 'climb' }), - + // Golemancy toggleGolem: (golemId: string) => { set((s) => { const enabledGolems = s.golemancy?.enabledGolems || []; const isEnabled = enabledGolems.includes(golemId); return { - golemancy: { - ...s.golemancy, - enabledGolems: isEnabled - ? enabledGolems.filter(id => id !== golemId) - : [...enabledGolems, golemId] + golemancy: { + ...s.golemancy, + enabledGolems: isEnabled + ? enabledGolems.filter(id => id !== golemId) + : [...enabledGolems, golemId] }, }; }); }, - + setEnabledGolems: (golemIds: string[]) => { set((s) => ({ golemancy: { ...s.golemancy, enabledGolems: golemIds }, })); }, - + enterSpireMode: () => { set({ spireMode: true }); }, - + learnSpell: (spellId: string) => { set((state) => ({ spells: { @@ -294,7 +293,7 @@ export const useCombatStore = create()( }, })); }, - + setSpellState: (spellId: string, spellState: Partial) => { set((state) => ({ spells: { @@ -303,29 +302,28 @@ export const useCombatStore = create()( }, })); }, - + // Activity Log addActivityLog: (eventType: ActivityEventType, message: string, details?: ActivityLogEntry['details']) => { set((state) => ({ activityLog: addActivityLogEntry(state, eventType, message, details), })); }, - + // Stats incrementSpellsCast: () => { set((state) => ({ totalSpellsCast: state.totalSpellsCast + 1 })); }, - + addDamageDealt: (damage: number) => { set((state) => ({ totalDamageDealt: state.totalDamageDealt + damage })); }, - + incrementCraftsCompleted: () => { set((state) => ({ totalCraftsCompleted: state.totalCraftsCompleted + 1 })); }, - + processCombatTick: ( - skills: Record, rawMana: number, elements: Record, maxMana: number, @@ -336,7 +334,6 @@ export const useCombatStore = create()( return processCombatTick( get, set, - skills, rawMana, elements, maxMana, @@ -345,10 +342,10 @@ export const useCombatStore = create()( onDamageDealt, ); }, - + resetCombat: (startFloor: number, spellsToKeep: string[] = []) => { const startSpells = makeInitialSpells(spellsToKeep); - + set({ currentFloor: startFloor, floorHP: getFloorMaxHP(startFloor), @@ -360,7 +357,7 @@ export const useCombatStore = create()( spells: startSpells, }); }, - + // Debug helpers debugSetFloor: (floor: number) => { set({ @@ -369,13 +366,13 @@ export const useCombatStore = create()( floorMaxHP: getFloorMaxHP(floor), }); }, - + resetFloorHP: () => { set((state) => ({ floorHP: state.floorMaxHP, })); }, - + debugSetTime: (day: number, hour: number) => { useGameStore.setState({ day, hour }); }, @@ -386,7 +383,7 @@ export const useCombatStore = create()( currentFloor: state.currentFloor, maxFloorReached: state.maxFloorReached, spells: state.spells, - activeSpell: state.activeSpell, + activeSpell: state.activeAction, }), } ) diff --git a/src/lib/game/stores/craftingStore.ts b/src/lib/game/stores/craftingStore.ts index 4e9c02d..338c1fc 100644 --- a/src/lib/game/stores/craftingStore.ts +++ b/src/lib/game/stores/craftingStore.ts @@ -1,28 +1,14 @@ // ─── Crafting Store ───────────────────────────────────────────────────── -// Handles equipment crafting, enchantment design, and crafting progress -// This is a modular store that manages all crafting-related state - import { create } from 'zustand'; import { persist } from 'zustand/middleware'; import type { DesignProgress, PreparationProgress, ApplicationProgress, EquipmentCraftingProgress, EnchantmentDesign, EquipmentInstance, DesignEffect } from '../types'; - -// Import crafting modules for action logic import * as CraftingUtils from '../crafting-utils'; import * as CraftingDesign from '../crafting-design'; -import { computeEffects } from '../upgrade-effects'; -import { hasSpecial, SPECIAL_EFFECTS } from '../special-effects'; - -// Import other stores to access required state -import { useSkillStore } from './skillStore'; -import { useGameStore } from './gameStore'; import { useManaStore } from './manaStore'; import { useCombatStore } from './combatStore'; -import { createStartingEquipment } from '../store/crafting-modules/starting-equipment'; +import { createStartingEquipment } from '../crafting-slice'; import { useUIStore } from './uiStore'; - -// Import action modules import * as ApplicationActions from '../crafting-actions/application-actions'; -import * as CraftingApply from '../crafting-apply'; import * as PreparationActions from '../crafting-actions/preparation-actions'; import * as CraftingEquipment from '../crafting-equipment'; @@ -142,25 +128,18 @@ export const useCraftingStore = create()( // Enchantment design actions startDesigningEnchantment: (name, equipmentTypeId, effects) => { - // Get state from other stores - const skillState = useSkillStore.getState(); const state = get(); // crafting state - const enchantingLevel = skillState.skills?.enchanting || 0; - const validation = CraftingDesign.validateDesignEffects(effects, equipmentTypeId, enchantingLevel); + const validation = CraftingDesign.validateDesignEffects(effects, equipmentTypeId, 0); if (!validation.valid) return false; const equipType = CraftingUtils.getEquipmentType(equipmentTypeId); if (!equipType) return false; - const efficiencyBonus = (skillState.skillUpgrades?.['efficientEnchant'] || []).length * 0.05 || 0; - const totalCapacityCost = CraftingDesign.calculateDesignCapacityCost(effects, efficiencyBonus); + const totalCapacityCost = CraftingDesign.calculateDesignCapacityCost(effects, 0); if (totalCapacityCost > equipType.baseCapacity) return false; - const computedEffects = computeEffects(skillState.skillUpgrades || {}, skillState.skillTiers || {}); - const hasEnchantMastery = hasSpecial(computedEffects, SPECIAL_EFFECTS.ENCHANT_MASTERY); - let updates: Partial = {}; if (!state.designProgress) { @@ -176,17 +155,6 @@ export const useCraftingStore = create()( }; // Update currentAction in combatStore useCombatStore.setState({ currentAction: 'design' }); - } else if (hasEnchantMastery && !state.designProgress2) { - updates = { - designProgress2: { - designId: CraftingUtils.generateDesignId(), - progress: 0, - required: CraftingDesign.calculateDesignTime(effects), - name, - equipmentType: equipmentTypeId, - effects, - }, - }; } else { return false; } diff --git a/src/lib/game/stores/gameActions.ts b/src/lib/game/stores/gameActions.ts index 6aa01d8..d894254 100644 --- a/src/lib/game/stores/gameActions.ts +++ b/src/lib/game/stores/gameActions.ts @@ -1,9 +1,7 @@ import { computeMaxMana } from '../utils'; -import { computeEffects } from '../upgrade-effects'; import { useUIStore } from './uiStore'; import { usePrestigeStore } from './prestigeStore'; import { useManaStore } from './manaStore'; -import { useSkillStore } from './skillStore'; import { useCombatStore } from './combatStore'; export const createResetGame = (set: (state: any) => void, initialState: any) => () => { @@ -12,7 +10,6 @@ export const createResetGame = (set: (state: any) => void, initialState: any) => localStorage.removeItem('mana-loop-ui-storage'); localStorage.removeItem('mana-loop-prestige-storage'); localStorage.removeItem('mana-loop-mana-storage'); - localStorage.removeItem('mana-loop-skill-storage'); localStorage.removeItem('mana-loop-combat-storage'); localStorage.removeItem('mana-loop-game-storage'); localStorage.removeItem('mana-loop-crafting-storage'); @@ -24,7 +21,6 @@ export const createResetGame = (set: (state: any) => void, initialState: any) => useUIStore.getState().resetUI(); usePrestigeStore.getState().resetPrestige(); useManaStore.getState().resetMana({}, {}, {}, {}); - useSkillStore.getState().resetSkills(); useCombatStore.getState().resetCombat(startFloor); set({ @@ -34,28 +30,20 @@ export const createResetGame = (set: (state: any) => void, initialState: any) => }; export const createGatherMana = () => () => { - const skillState = useSkillStore.getState(); const manaState = useManaStore.getState(); const prestigeState = usePrestigeStore.getState(); - // Compute click mana - let cm = 1 + - (skillState.skills.manaTap || 0) * 1 + - (skillState.skills.manaSurge || 0) * 3; + // Base click mana (no skill bonuses) + const cm = 1; - // Mana overflow bonus - const overflowBonus = 1 + (skillState.skills.manaOverflow || 0) * 0.25; - cm = Math.floor(cm * overflowBonus); - - const effects = computeEffects(skillState.skillUpgrades || {}, skillState.skillTiers || {}); const max = computeMaxMana( - { - skills: skillState.skills, - prestigeUpgrades: prestigeState.prestigeUpgrades, - skillUpgrades: skillState.skillUpgrades, - skillTiers: skillState.skillTiers + { + skills: {}, + prestigeUpgrades: prestigeState.prestigeUpgrades, + skillUpgrades: {}, + skillTiers: {} }, - effects + undefined ); useManaStore.getState().gatherMana(cm, max); diff --git a/src/lib/game/stores/gameHooks.ts b/src/lib/game/stores/gameHooks.ts index 6e0c62f..4e74d0d 100644 --- a/src/lib/game/stores/gameHooks.ts +++ b/src/lib/game/stores/gameHooks.ts @@ -1,7 +1,6 @@ import { useEffect } from 'react'; import { useGameStore } from './gameStore'; import { useManaStore } from './manaStore'; -import { useSkillStore } from './skillStore'; import { usePrestigeStore } from './prestigeStore'; import { useCombatStore } from './combatStore'; import { useUIStore } from './uiStore'; @@ -28,17 +27,15 @@ export function useGameLoop() { // ─── Shared Selector Hooks for Common Derived State ──────────────────────────── /** - * Get unified effects from all relevant stores + * Get unified effects from equipment only (skills removed) */ export function useUnifiedEffects() { - const skillUpgrades = useSkillStore((s) => s.skillUpgrades); - const skillTiers = useSkillStore((s) => s.skillTiers); const equippedInstances = useCraftingStore((s) => s.equippedInstances); const equipmentInstances = useCraftingStore((s) => s.equipmentInstances); return getUnifiedEffects({ - skillUpgrades, - skillTiers, + skillUpgrades: {}, + skillTiers: {}, equippedInstances, equipmentInstances, }); @@ -48,10 +45,7 @@ export function useUnifiedEffects() { * Get computed mana stats (maxMana, baseRegen, clickMana, meditationMultiplier, effectiveRegen) */ export function useManaStats() { - const skills = useSkillStore((s) => s.skills); const prestigeUpgrades = usePrestigeStore((s) => s.prestigeUpgrades); - const skillUpgrades = useSkillStore((s) => s.skillUpgrades); - const skillTiers = useSkillStore((s) => s.skillTiers); const meditateTicks = useManaStore((s) => s.meditateTicks); const day = useGameStore((s) => s.day); const hour = useGameStore((s) => s.hour); @@ -59,30 +53,27 @@ export function useManaStats() { const equipmentInstances = useCraftingStore((s) => s.equipmentInstances); const upgradeEffects = getUnifiedEffects({ - skillUpgrades, - skillTiers, + skillUpgrades: {}, + skillTiers: {}, equippedInstances, equipmentInstances, }); const maxMana = computeMaxMana( - { skills, prestigeUpgrades, skillUpgrades, skillTiers }, + { skills: {}, prestigeUpgrades, skillUpgrades: {}, skillTiers: {} }, upgradeEffects ); const baseRegen = computeRegen( - { skills, prestigeUpgrades, skillUpgrades, skillTiers }, + { skills: {}, prestigeUpgrades, skillUpgrades: {}, skillTiers: {} }, upgradeEffects ); const clickMana = computeClickMana({ - skills, - prestigeUpgrades, - skillUpgrades, - skillTiers, + skills: {}, }); - const meditationMultiplier = getMeditationBonus(meditateTicks, skills, upgradeEffects.meditationEfficiency); + const meditationMultiplier = getMeditationBonus(meditateTicks, {}, upgradeEffects.meditationEfficiency); const incursionStrength = getIncursionStrength(day, hour); const effectiveRegenWithSpecials = baseRegen * (1 - incursionStrength); @@ -115,22 +106,18 @@ export function useManaStats() { * Get combat-related derived state */ export function useCombatStats() { - const skills = useSkillStore((s) => s.skills); const signedPacts = usePrestigeStore((s) => s.signedPacts); const equippedInstances = useCraftingStore((s) => s.equippedInstances); const equipmentInstances = useCraftingStore((s) => s.equipmentInstances); - const skillUpgrades = useSkillStore((s) => s.skillUpgrades); - const skillTiers = useSkillStore((s) => s.skillTiers); const upgradeEffects = getUnifiedEffects({ - skillUpgrades, - skillTiers, + skillUpgrades: {}, + skillTiers: {}, equippedInstances, equipmentInstances, }); return { - skills, signedPacts, equippedInstances, equipmentInstances, diff --git a/src/lib/game/stores/gameLoopActions.ts b/src/lib/game/stores/gameLoopActions.ts index 0a07e6b..b8a00e6 100644 --- a/src/lib/game/stores/gameLoopActions.ts +++ b/src/lib/game/stores/gameLoopActions.ts @@ -4,21 +4,19 @@ import { SPELLS_DEF } from '../constants'; import { useUIStore } from './uiStore'; import { usePrestigeStore } from './prestigeStore'; import { useManaStore } from './manaStore'; -import { useSkillStore } from './skillStore'; import { useCombatStore } from './combatStore'; export const createStartNewLoop = (set: (state: any) => void) => () => { const prestigeState = usePrestigeStore.getState(); const combatState = useCombatStore.getState(); const manaState = useManaStore.getState(); - const skillState = useSkillStore.getState(); const insightGained = prestigeState.loopInsight || calcInsight({ maxFloorReached: combatState.maxFloorReached, totalManaGathered: manaState.totalManaGathered, signedPacts: prestigeState.signedPacts, prestigeUpgrades: prestigeState.prestigeUpgrades, - skills: skillState.skills, + skills: {}, }); const total = prestigeState.insight + insightGained; @@ -26,25 +24,6 @@ export const createStartNewLoop = (set: (state: any) => void) => () => { const pu = prestigeState.prestigeUpgrades; const startFloor = 1 + (pu.spireKey || 0) * 2; - // Apply saved memories - restore skill levels, tiers, and upgrades - const memories = prestigeState.memories || []; - const newSkills: Record = {}; - const newSkillTiers: Record = {}; - const newSkillUpgrades: Record = {}; - - if (memories.length > 0) { - for (const memory of memories) { - const tieredSkillId = memory.tier > 1 ? `${memory.skillId}_t${memory.tier}` : memory.skillId; - newSkills[tieredSkillId] = memory.level; - - if (memory.tier > 1) { - newSkillTiers[memory.skillId] = memory.tier; - } - - newSkillUpgrades[tieredSkillId] = memory.upgrades || []; - } - } - // Reset and update all stores for new loop useUIStore.setState({ gameOver: false, @@ -61,9 +40,7 @@ export const createStartNewLoop = (set: (state: any) => void) => () => { ); usePrestigeStore.getState().incrementLoopCount(); - useManaStore.getState().resetMana(pu, newSkills, newSkillUpgrades, newSkillTiers); - - useSkillStore.getState().resetSkills(newSkills, newSkillUpgrades, newSkillTiers); + useManaStore.getState().resetMana(pu, {}, {}, {}); // Reset combat with starting floor and any spells from prestige upgrades const startSpells = makeInitialSpells(); diff --git a/src/lib/game/stores/gameStore.ts b/src/lib/game/stores/gameStore.ts index 714d3c1..cc9daa2 100755 --- a/src/lib/game/stores/gameStore.ts +++ b/src/lib/game/stores/gameStore.ts @@ -5,7 +5,6 @@ import { create } from 'zustand'; import { persist } from 'zustand/middleware'; import { TICK_MS, HOURS_PER_TICK, MAX_DAY, SPELLS_DEF, GUARDIANS, getStudySpeedMultiplier } from '../constants'; -import { computeEffects } from '../upgrade-effects'; import { hasSpecial, SPECIAL_EFFECTS } from '../special-effects'; import { computeMaxMana, @@ -22,7 +21,6 @@ import { import { useUIStore } from './uiStore'; import { usePrestigeStore } from './prestigeStore'; import { useManaStore } from './manaStore'; -import { useSkillStore } from './skillStore'; import { useCombatStore, makeInitialSpells } from './combatStore'; import { useAttunementStore } from './attunementStore'; import { ATTUNEMENTS_DEF, getAttunementConversionRate } from '../data/attunements'; @@ -66,26 +64,22 @@ export const useGameStore = create()( tick: () => { const uiState = useUIStore.getState(); if (uiState.gameOver || uiState.paused) return; - + // Helper for logging const addLog = (msg: string) => useUIStore.getState().addLog(msg); // Get all store states const prestigeState = usePrestigeStore.getState(); const manaState = useManaStore.getState(); - const skillState = useSkillStore.getState(); const combatState = useCombatStore.getState(); - // Compute effects from upgrades - const effects = computeEffects(skillState.skillUpgrades || {}, skillState.skillTiers || {}); - const maxMana = computeMaxMana( - { skills: skillState.skills, prestigeUpgrades: prestigeState.prestigeUpgrades, skillUpgrades: skillState.skillUpgrades, skillTiers: skillState.skillTiers }, - effects + { skills: {}, prestigeUpgrades: prestigeState.prestigeUpgrades, skillUpgrades: {}, skillTiers: {} }, + undefined ); const baseRegen = computeRegen( - { skills: skillState.skills, prestigeUpgrades: prestigeState.prestigeUpgrades, skillUpgrades: skillState.skillUpgrades, skillTiers: skillState.skillTiers }, - effects + { skills: {}, prestigeUpgrades: prestigeState.prestigeUpgrades, skillUpgrades: {}, skillTiers: {}, attunement: {} }, + undefined ); // Time progression @@ -103,9 +97,9 @@ export const useGameStore = create()( totalManaGathered: manaState.totalManaGathered, signedPacts: prestigeState.signedPacts, prestigeUpgrades: prestigeState.prestigeUpgrades, - skills: skillState.skills, + skills: {}, }); - + addLog(`⏰ The loop ends. Gained ${insightGained} Insight.`); useUIStore.getState().setGameOver(true, false); usePrestigeStore.getState().setLoopInsight(insightGained); @@ -120,9 +114,9 @@ export const useGameStore = create()( totalManaGathered: manaState.totalManaGathered, signedPacts: prestigeState.signedPacts, prestigeUpgrades: prestigeState.prestigeUpgrades, - skills: skillState.skills, + skills: {}, }) * 3; - + addLog(`🏆 VICTORY! The Awakened One falls! Gained ${insightGained} Insight!`); useUIStore.getState().setGameOver(true, true); usePrestigeStore.getState().setLoopInsight(insightGained); @@ -135,10 +129,10 @@ export const useGameStore = create()( // Meditation bonus tracking and regen calculation let meditateTicks = manaState.meditateTicks; let meditationMultiplier = 1; - + if (combatState.currentAction === 'meditate') { meditateTicks++; - meditationMultiplier = getMeditationBonus(meditateTicks, skillState.skills, effects.meditationEfficiency); + meditationMultiplier = getMeditationBonus(meditateTicks, {}, 1); } else { meditateTicks = 0; } @@ -150,7 +144,7 @@ export const useGameStore = create()( if (!state.active) return; const def = ATTUNEMENTS_DEF[id]; if (!def || def.conversionRate <= 0 || !def.primaryManaType) return; - + const scaledRate = getAttunementConversionRate(id, state.level || 1); totalConversionPerTick += scaledRate * HOURS_PER_TICK; }); @@ -167,10 +161,10 @@ export const useGameStore = create()( if (!state.active) return; const def = ATTUNEMENTS_DEF[id]; if (!def || def.conversionRate <= 0 || !def.primaryManaType) return; - + const scaledRate = getAttunementConversionRate(id, state.level || 1); - const conversionThisTick = scaledRate * HOURS_PER_TICK; // per tick - + const conversionThisTick = scaledRate * HOURS_PER_TICK; + // Add to primary mana type (cost already deducted from regen) if (elements[def.primaryManaType]) { elements[def.primaryManaType].current = Math.min( @@ -181,32 +175,6 @@ export const useGameStore = create()( }); let totalManaGathered = manaState.totalManaGathered; - // Study progress - handled by skillStore - if (combatState.currentAction === 'study' && skillState.currentStudyTarget) { - const studySpeedMult = getStudySpeedMultiplier(skillState.skills); - const progressGain = HOURS_PER_TICK * studySpeedMult; - - const result = useSkillStore.getState().updateStudyProgress(progressGain); - - if (result.completed && result.target) { - if (result.target.type === 'skill') { - const skillId = result.target.id; - const currentLevel = skillState.skills[skillId] || 0; - // Update skill level - useSkillStore.getState().incrementSkillLevel(skillId); - useSkillStore.getState().clearPaidStudySkill(skillId); - useCombatStore.getState().setAction('meditate'); - addLog(`✅ ${skillId} Lv.${currentLevel + 1} mastered!`); - } else if (result.target.type === 'spell') { - const spellId = result.target.id; - useCombatStore.getState().learnSpell(spellId); - useSkillStore.getState().setCurrentStudyTarget(null); - useCombatStore.getState().setAction('meditate'); - addLog(`📖 ${SPELLS_DEF[spellId]?.name || spellId} learned!`); - } - } - } - // Convert action - delegate to manaStore if (combatState.currentAction === 'convert') { const convertResult = useManaStore.getState().processConvertAction(rawMana); @@ -238,11 +206,11 @@ export const useGameStore = create()( // Combat - delegate to combatStore if (combatState.currentAction === 'climb') { const combatResult = useCombatStore.getState().processCombatTick( - skillState.skills, + {}, rawMana, elements, maxMana, - effects.attackSpeedMultiplier, + 1, (floor, wasGuardian) => { if (wasGuardian) { addLog(`⚔️ ${GUARDIANS[floor]?.name || 'Guardian'} defeated! Visit the Grimoire to sign a pact.`); @@ -252,25 +220,18 @@ export const useGameStore = create()( }, (damage) => { // Apply upgrade damage multipliers and bonuses - let dmg = damage * effects.baseDamageMultiplier + effects.baseDamageBonus; + let dmg = damage; // Executioner: +100% damage to enemies below 25% HP - if (hasSpecial(effects, SPECIAL_EFFECTS.EXECUTIONER) && combatState.floorHP / combatState.floorMaxHP < 0.25) { + if (hasSpecial({}, SPECIAL_EFFECTS.EXECUTIONER) && combatState.floorHP / combatState.floorMaxHP < 0.25) { dmg *= 2; } // Berserker: +50% damage when below 50% mana - if (hasSpecial(effects, SPECIAL_EFFECTS.BERSERKER) && rawMana < maxMana * 0.5) { + if (hasSpecial({}, SPECIAL_EFFECTS.BERSERKER) && rawMana < maxMana * 0.5) { dmg *= 1.5; } - // Spell echo - chance to cast again - const echoChance = (skillState.skills.spellEcho || 0) * 0.1; - if (Math.random() < echoChance) { - dmg *= 2; - addLog(`✨ Spell Echo! Double damage!`); - } - return { rawMana, elements, modifiedDamage: dmg }; } ); diff --git a/src/lib/game/stores/index.ts b/src/lib/game/stores/index.ts index 4e15ad6..d1610ce 100755 --- a/src/lib/game/stores/index.ts +++ b/src/lib/game/stores/index.ts @@ -11,9 +11,6 @@ export type { PrestigeState } from './prestigeStore'; export { useManaStore, makeInitialElements } from './manaStore'; export type { ManaState } from './manaStore'; -export { useSkillStore } from './skillStore'; -export type { SkillState } from './skillStore'; - export { useCombatStore, makeInitialSpells } from './combatStore'; export type { CombatState } from './combatStore'; diff --git a/src/lib/game/study-slice.ts b/src/lib/game/study-slice.ts deleted file mode 100755 index cde385c..0000000 --- a/src/lib/game/study-slice.ts +++ /dev/null @@ -1,211 +0,0 @@ -// ─── Study Slice ───────────────────────────────────────────────────────────── -// Actions for studying skills and spells - -import type { GameState } from './types'; -import { SKILLS_DEF, SPELLS_DEF, getStudyCostMultiplier } from './constants'; -import { computeEffects } from './upgrade-effects'; -import { hasSpecial, SPECIAL_EFFECTS } from './special-effects'; - -// ─── Study Actions Interface ────────────────────────────────────────────────── - -export interface StudyActions { - startStudyingSkill: (skillId: string) => void; - startStudyingSpell: (spellId: string) => void; - cancelStudy: () => void; - startParallelStudySkill: (skillId: string) => void; - cancelParallelStudy: () => void; -} - -// ─── Study Slice Factory ────────────────────────────────────────────────────── - -export function createStudySlice( - set: (partial: Partial | ((state: GameState) => Partial)) => void, - get: () => GameState -): StudyActions { - return { - // Start studying a skill - mana is deducted per hour, not upfront - startStudyingSkill: (skillId: string) => { - const state = get(); - const sk = SKILLS_DEF[skillId]; - if (!sk) return; - - const currentLevel = state.skills[skillId] || 0; - if (currentLevel >= sk.max) return; - - // Check prerequisites - if (sk.req) { - for (const [r, rl] of Object.entries(sk.req)) { - if ((state.skills[r] || 0) < rl) return; - } - } - - // Calculate total mana cost and cost per hour - const costMult = getStudyCostMultiplier(state.skills); - let totalCost = Math.floor(sk.base * (currentLevel + 1) * costMult); - - // CHAIN_STUDY: -5% cost per maxed skill - const effects = computeEffects(state.skillUpgrades || {}, state.skillTiers || {}); - if (hasSpecial(effects, SPECIAL_EFFECTS.CHAIN_STUDY)) { - const maxedSkills = Object.entries(SKILLS_DEF).filter(([id, sk]) => - (state.skills[id] || 0) >= sk.max - ).length; - const discount = Math.pow(0.95, maxedSkills); // -5% per maxed skill - totalCost = Math.floor(totalCost * discount); - } - - const manaCostPerHour = Math.ceil(totalCost / sk.studyTime); - - // Must have at least 1 hour worth of mana to start - if (state.rawMana < manaCostPerHour) return; - - // KNOWLEDGE_TRANSFER: New skills start at 10% progress - let initialProgress = state.skillProgress[skillId] || 0; - if (hasSpecial(effects, SPECIAL_EFFECTS.KNOWLEDGE_TRANSFER) && initialProgress === 0) { - initialProgress = sk.studyTime * 0.10; // 10% of required time - log = [`📖 Knowledge Transfer: Starting with 10% progress!`, ...state.log.slice(0, 49)]; - } - - // Start studying (no upfront cost - mana is deducted per hour during study) - set({ - currentAction: 'study', - currentStudyTarget: { - type: 'skill', - id: skillId, - progress: initialProgress, - required: sk.studyTime, - manaCostPerHour: manaCostPerHour, - totalCost: totalCost, - }, - log: [`📚 Started studying ${sk.name} (${manaCostPerHour} mana/hr)...`, ...state.log.slice(0, 49)], - }); - }, - - // Start studying a spell - startStudyingSpell: (spellId: string) => { - const state = get(); - const sp = SPELLS_DEF[spellId]; - if (!sp || state.spells[spellId]?.learned) return; - - // Calculate total mana cost and cost per hour - const costMult = getStudyCostMultiplier(state.skills); - let totalCost = Math.floor(sp.unlock * costMult); - - // CHAIN_STUDY: -5% cost per maxed skill - const effects = computeEffects(state.skillUpgrades || {}, state.skillTiers || {}); - if (hasSpecial(effects, SPECIAL_EFFECTS.CHAIN_STUDY)) { - const maxedSkills = Object.entries(SKILLS_DEF).filter(([id, sk]) => - (state.skills[id] || 0) >= sk.max - ).length; - const discount = Math.pow(0.95, maxedSkills); // -5% per maxed skill - totalCost = Math.floor(totalCost * discount); - } - - const studyTime = sp.studyTime || (sp.tier * 4); - const manaCostPerHour = Math.ceil(totalCost / studyTime); - - // Must have at least 1 hour worth of mana to start - if (state.rawMana < manaCostPerHour) return; - - // Start studying (no upfront cost - mana is deducted per hour during study) - set({ - currentAction: 'study', - currentStudyTarget: { - type: 'spell', - id: spellId, - progress: state.spells[spellId]?.studyProgress || 0, - required: studyTime, - manaCostPerHour: manaCostPerHour, - totalCost: totalCost, - }, - spells: { - ...state.spells, - [spellId]: { ...(state.spells[spellId] || { learned: false, level: 0 }), studyProgress: state.spells[spellId]?.studyProgress || 0 }, - }, - log: [`📚 Started studying ${sp.name} (${manaCostPerHour} mana/hr)...`, ...state.log.slice(0, 49)], - }); - }, - - // Cancel current study (saves progress) - cancelStudy: () => { - const state = get(); - if (!state.currentStudyTarget) return; - - // Knowledge retention bonus - const retentionBonus = 1 + (state.skills.knowledgeRetention || 0) * 0.2; - const savedProgress = Math.min( - state.currentStudyTarget.progress, - state.currentStudyTarget.required * retentionBonus - ); - - // Save progress - if (state.currentStudyTarget.type === 'skill') { - set({ - currentStudyTarget: null, - currentAction: 'meditate', - skillProgress: { - ...state.skillProgress, - [state.currentStudyTarget.id]: savedProgress, - }, - log: [`📖 Study interrupted. Progress saved.`, ...state.log.slice(0, 49)], - }); - } else if (state.currentStudyTarget.type === 'spell') { - set({ - currentStudyTarget: null, - currentAction: 'meditate', - spells: { - ...state.spells, - [state.currentStudyTarget.id]: { - ...(state.spells[state.currentStudyTarget.id] || { learned: false, level: 0 }), - studyProgress: savedProgress, - }, - }, - log: [`📖 Study interrupted. Progress saved.`, ...state.log.slice(0, 49)], - }); - } - }, - - // Start parallel study of a skill (requires Parallel Mind upgrade) - startParallelStudySkill: (skillId: string) => { - const state = get(); - if (state.parallelStudyTarget) return; // Already have parallel study - if (!state.currentStudyTarget) return; // Need primary study - - const sk = SKILLS_DEF[skillId]; - if (!sk) return; - - const currentLevel = state.skills[skillId] || 0; - if (currentLevel >= sk.max) return; - - // Can't study same thing in parallel - if (state.currentStudyTarget.id === skillId) return; - - // Calculate mana cost for parallel study - const costMult = getStudyCostMultiplier(state.skills); - const totalCost = Math.floor(sk.base * (currentLevel + 1) * costMult); - const manaCostPerHour = Math.ceil(totalCost / sk.studyTime); - - set({ - parallelStudyTarget: { - type: 'skill', - id: skillId, - progress: state.skillProgress[skillId] || 0, - required: sk.studyTime, - manaCostPerHour: Math.ceil(manaCostPerHour / 2), // Half speed = half mana cost per tick - totalCost: totalCost, - }, - log: [`📚 Started parallel study of ${sk.name}... (50% speed)`, ...state.log.slice(0, 49)], - }); - }, - - // Cancel parallel study - cancelParallelStudy: () => { - set((state) => { - if (!state.parallelStudyTarget) return state; - return { - parallelStudyTarget: null, - log: ['📖 Parallel study cancelled.', ...state.log.slice(0, 49)], - }; - }); - }, - }; -} diff --git a/src/lib/game/types/index.ts b/src/lib/game/types/index.ts index 89be86b..9858350 100644 --- a/src/lib/game/types/index.ts +++ b/src/lib/game/types/index.ts @@ -11,67 +11,36 @@ export type { AttunementSlot, AttunementDef, AttunementState, GuardianBoon, Guar // Spell types export type { SpellCost, SpellDef, SpellEffect, SpellState } from './spells'; -// Skill types -export type { - SkillDef, - SkillUpgradeDef, - SkillUpgradeEffect, - SkillEvolutionPath, - SkillTierDef, - SkillPerkChoice, - SkillUpgradeChoice, - PrestigeDef, - SkillCost -} from './skills'; - // Equipment types -export type { - EquipmentDef, - EquipmentInstance, - AppliedEnchantment, - EnchantmentDesign, - DesignEffect, - DesignProgress, - PreparationProgress, - ApplicationProgress, - EquipmentCraftingProgress, - EquipmentSpellState, - BlueprintDef, - LootInventory, - EquipmentSlot +export type { + EquipmentDef, + EquipmentInstance, + AppliedEnchantment, + EnchantmentDesign, + DesignEffect, + DesignProgress, + PreparationProgress, + ApplicationProgress, + EquipmentCraftingProgress, + EquipmentSpellState, + BlueprintDef, + LootInventory, + EquipmentSlot } from './equipmentSlot'; // Game state types -export type { - RoomType, - EnemyState, - FloorState, - AchievementDef, - AchievementState, - GameAction, - ScheduleBlock, - StudyTarget, - SummonedGolem, - GolemancyState, - GameState, - GameActionType, - ActivityEventType, - ActivityLogEntry, -} from './game'; - -// Game state types -export type { - RoomType, - EnemyState, - FloorState, - AchievementDef, - AchievementState, - GameAction, - ScheduleBlock, - StudyTarget, - SummonedGolem, - GolemancyState, - GameState, +export type { + RoomType, + EnemyState, + FloorState, + AchievementDef, + AchievementState, + GameAction, + ScheduleBlock, + StudyTarget, + SummonedGolem, + GolemancyState, + GameState, GameActionType, ActivityEventType, ActivityLogEntry, diff --git a/src/lib/game/utils/formatting.ts b/src/lib/game/utils/formatting.ts index 238c6dc..563d02e 100644 --- a/src/lib/game/utils/formatting.ts +++ b/src/lib/game/utils/formatting.ts @@ -1,5 +1,8 @@ // ─── Formatting Functions ───────────────────────────────────────────────────── +import { ELEMENTS } from '@/lib/game/constants'; +import type { SpellCost } from '@/lib/game/types'; + export function fmt(n: number): string { if (!isFinite(n) || isNaN(n)) return '0'; if (n >= 1e9) return (n / 1e9).toFixed(2) + 'B'; @@ -11,3 +14,41 @@ export function fmt(n: number): string { export function fmtDec(n: number, d: number = 1): string { return isFinite(n) ? n.toFixed(d) : '0'; } + +/** + * Format a spell cost for display + */ +export function formatSpellCost(cost: SpellCost): string { + if (cost.type === 'raw') { + return `${cost.amount} raw`; + } + const elemDef = ELEMENTS[cost.element || '']; + return `${cost.amount} ${elemDef?.sym || '?'}`; +} + +/** + * Get the display color for a spell cost + */ +export function getSpellCostColor(cost: SpellCost): string { + if (cost.type === 'raw') { + return '#60A5FA'; // Blue for raw mana + } + return ELEMENTS[cost.element || '']?.color || '#9CA3AF'; +} + +/** + * Format study time in hours to human-readable string + */ +export function formatStudyTime(hours: number): string { + if (hours < 1) return `${Math.round(hours * 60)}m`; + return `${hours.toFixed(1)}h`; +} + +/** + * Format time (hour of day) to HH:MM format + */ +export function formatHour(hour: number): string { + const h = Math.floor(hour); + const m = Math.floor((hour % 1) * 60); + return `${h.toString().padStart(2, '0')}:${m.toString().padStart(2, '0')}`; +} diff --git a/src/lib/game/utils/index.ts b/src/lib/game/utils/index.ts index feefd1f..f7b59b2 100644 --- a/src/lib/game/utils/index.ts +++ b/src/lib/game/utils/index.ts @@ -1,7 +1,7 @@ // ─── Game Utilities - Barrel Export ────────────────────────────────────────── // Re-export everything from the focused modules -export { fmt, fmtDec } from './formatting'; +export { fmt, fmtDec, formatSpellCost, getSpellCostColor, formatStudyTime, formatHour } from './formatting'; export { getFloorMaxHP, getFloorElement } from './floor-utils'; export { computeMaxMana,