From c3a5f333da6a36bcaedb563002ab900b0a4e0f0f Mon Sep 17 00:00:00 2001 From: n8n-gitea Date: Mon, 18 May 2026 21:03:43 +0200 Subject: [PATCH] fix: resolve 22 remaining issues - type exports, dead code, state mutations, orphaned components --- docs/circular-deps.txt | 4 +- docs/dependency-graph.json | 6 +- docs/project-structure.txt | 11 - src/components/game/AchievementsDisplay.tsx | 205 ----------- src/components/game/CalendarDisplay.tsx | 53 --- src/components/game/ConfirmDialog.tsx | 193 ----------- src/components/game/CraftingProgress.tsx | 163 --------- .../LootInventory/LootInventoryDisplay.tsx | 310 ----------------- src/components/game/LootInventory/index.tsx | 318 ------------------ src/components/game/StudyProgress.tsx | 59 ---- src/components/game/index.ts | 3 - src/components/game/tabs/DisciplinesTab.tsx | 77 +++-- .../crafting-actions/equipment-actions.ts | 2 +- src/lib/game/crafting-utils.ts | 6 +- .../game/data/disciplines/base-disciplines.ts | 34 -- .../data/disciplines/enchanter-disciplines.ts | 34 -- .../disciplines/fabricator-disciplines.ts | 21 -- .../data/disciplines/invoker-disciplines.ts | 21 -- src/lib/game/data/enchantments/index.ts | 1 + src/lib/game/effects/upgrade-effects.types.ts | 20 +- src/lib/game/stores/combat-actions.ts | 4 - src/lib/game/stores/combatStore.ts | 2 +- src/lib/game/stores/discipline-slice.ts | 9 +- src/lib/game/stores/gameActions.ts | 2 +- src/lib/game/stores/gameStore.ts | 1 - src/lib/game/stores/uiStore.ts | 24 +- src/lib/game/types/elements.ts | 2 + src/lib/game/types/game.ts | 19 +- src/lib/game/types/index.ts | 4 +- src/lib/game/types/spells.ts | 1 + src/lib/game/utils/room-utils.ts | 18 - 31 files changed, 108 insertions(+), 1519 deletions(-) delete mode 100644 src/components/game/AchievementsDisplay.tsx delete mode 100755 src/components/game/CalendarDisplay.tsx delete mode 100644 src/components/game/ConfirmDialog.tsx delete mode 100755 src/components/game/CraftingProgress.tsx delete mode 100644 src/components/game/LootInventory/LootInventoryDisplay.tsx delete mode 100644 src/components/game/LootInventory/index.tsx delete mode 100755 src/components/game/StudyProgress.tsx delete mode 100644 src/lib/game/data/disciplines/base-disciplines.ts delete mode 100644 src/lib/game/data/disciplines/enchanter-disciplines.ts delete mode 100644 src/lib/game/data/disciplines/fabricator-disciplines.ts delete mode 100644 src/lib/game/data/disciplines/invoker-disciplines.ts diff --git a/docs/circular-deps.txt b/docs/circular-deps.txt index bf2494b..df1c7e9 100644 --- a/docs/circular-deps.txt +++ b/docs/circular-deps.txt @@ -1,8 +1,8 @@ # Circular Dependencies -Generated: 2026-05-18T18:09:58.620Z +Generated: 2026-05-18T18:30:51.103Z Found: 3 circular chain(s) — these MUST be fixed before modifying involved files. -1. Processed 123 files (1.3s) (29 warnings) +1. Processed 124 files (1.3s) (29 warnings) 2. 1) data/equipment/index.ts > data/equipment/utils.ts 3. 2) data/golems/index.ts > data/golems/utils.ts diff --git a/docs/dependency-graph.json b/docs/dependency-graph.json index 384bc6a..c31d073 100644 --- a/docs/dependency-graph.json +++ b/docs/dependency-graph.json @@ -1,6 +1,6 @@ { "_meta": { - "generated": "2026-05-18T18:09:57.081Z", + "generated": "2026-05-18T18:30:49.611Z", "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." }, @@ -260,6 +260,7 @@ "data/enchantments/spell-effects/index.ts": [ "data/enchantment-types.ts", "data/enchantments/spell-effects/basic-spells.ts", + "data/enchantments/spell-effects/legendary-spells.ts", "data/enchantments/spell-effects/lightning-spells.ts", "data/enchantments/spell-effects/metal-spells.ts", "data/enchantments/spell-effects/sand-spells.ts", @@ -267,6 +268,9 @@ "data/enchantments/spell-effects/tier3-spells.ts", "data/enchantments/spell-effects/types.ts" ], + "data/enchantments/spell-effects/legendary-spells.ts": [ + "data/enchantments/spell-effects/types.ts" + ], "data/enchantments/spell-effects/lightning-spells.ts": [ "data/enchantments/spell-effects/types.ts" ], diff --git a/docs/project-structure.txt b/docs/project-structure.txt index e278a03..b0be3a2 100644 --- a/docs/project-structure.txt +++ b/docs/project-structure.txt @@ -67,10 +67,8 @@ Mana-Loop/ │ │ │ │ ├── BlueprintsSection.tsx │ │ │ │ ├── EquipmentItem.tsx │ │ │ │ ├── EssenceItem.tsx -│ │ │ │ ├── LootInventoryDisplay.tsx │ │ │ │ ├── MaterialItem.tsx │ │ │ │ ├── icons.ts -│ │ │ │ ├── index.tsx │ │ │ │ └── types.ts │ │ │ ├── StatsTab/ │ │ │ │ ├── CombatStatsSection.tsx @@ -105,18 +103,13 @@ Mana-Loop/ │ │ │ │ ├── ActivityLog.tsx │ │ │ │ ├── DisciplinesTab.tsx │ │ │ │ └── index.ts -│ │ │ ├── AchievementsDisplay.tsx │ │ │ ├── ActionButtons.tsx │ │ │ ├── ActivityLogPanel.tsx │ │ │ ├── AttunementStatus.tsx -│ │ │ ├── CalendarDisplay.tsx -│ │ │ ├── ConfirmDialog.tsx -│ │ │ ├── CraftingProgress.tsx │ │ │ ├── GameToast.tsx │ │ │ ├── ManaDisplay.tsx │ │ │ ├── SpellsTab.tsx │ │ │ ├── StatsTab.tsx -│ │ │ ├── StudyProgress.tsx │ │ │ ├── TimeDisplay.tsx │ │ │ ├── UpgradeDialog.tsx │ │ │ ├── index.ts @@ -191,14 +184,10 @@ Mana-Loop/ │ │ │ └── preparation-actions.ts │ │ ├── data/ │ │ │ ├── disciplines/ -│ │ │ │ ├── base-disciplines.ts │ │ │ │ ├── base.ts -│ │ │ │ ├── enchanter-disciplines.ts │ │ │ │ ├── enchanter.ts -│ │ │ │ ├── fabricator-disciplines.ts │ │ │ │ ├── fabricator.ts │ │ │ │ ├── index.ts -│ │ │ │ ├── invoker-disciplines.ts │ │ │ │ └── invoker.ts │ │ │ ├── enchantments/ │ │ │ │ ├── spell-effects/ diff --git a/src/components/game/AchievementsDisplay.tsx b/src/components/game/AchievementsDisplay.tsx deleted file mode 100644 index 668dee5..0000000 --- a/src/components/game/AchievementsDisplay.tsx +++ /dev/null @@ -1,205 +0,0 @@ -'use client'; - -import { useState } from 'react'; -import { GameCard } from '@/components/ui/game-card'; -import { Badge } from '@/components/ui/badge'; -import { ActionButton } from '@/components/ui/action-button'; -import { ScrollArea } from '@/components/ui/scroll-area'; -import { ManaBar } from '@/components/ui/mana-bar'; -import { Trophy, Lock, CheckCircle, ChevronDown, ChevronUp } from 'lucide-react'; -import type { AchievementState } from '@/lib/game/types'; -import { ACHIEVEMENTS, getAchievementsByCategory, isAchievementRevealed } from '@/lib/game/data/achievements'; -import { GameState } from '@/lib/game/types'; - -// Map achievement categories to CSS variables for colors -const CATEGORY_COLOR_MAP: Record = { - combat: 'var(--color-danger)', - progression: 'var(--rarity-legendary)', - crafting: 'var(--mana-dark)', - magic: 'var(--mana-water)', - special: 'var(--mana-stellar)', -}; - -interface AchievementsProps { - achievements: AchievementState; - gameState: Pick; -} - -export function AchievementsDisplay({ achievements, gameState }: AchievementsProps) { - const [expandedCategory, setExpandedCategory] = useState('combat'); - - const categories = getAchievementsByCategory(); - const unlockedCount = achievements.unlocked.length; - const totalCount = Object.keys(ACHIEVEMENTS).length; - - // Calculate progress for each achievement - const getProgress = (achievementId: string): number => { - const achievement = ACHIEVEMENTS[achievementId]; - if (!achievement) return 0; - if (achievements.unlocked.includes(achievementId)) return achievement.requirement.value; - - const { type, subType } = achievement.requirement; - - switch (type) { - case 'floor': - if (subType === 'noPacts') { - return gameState.maxFloorReached >= achievement.requirement.value && gameState.signedPacts.length === 0 - ? achievement.requirement.value - : gameState.maxFloorReached; - } - return gameState.maxFloorReached; - case 'spells': - return gameState.totalSpellsCast || 0; - case 'damage': - return gameState.totalDamageDealt || 0; - case 'mana': - return gameState.totalManaGathered || 0; - case 'pact': - return gameState.signedPacts.length; - case 'craft': - return gameState.totalCraftsCompleted || 0; - default: - return achievements.progress[achievementId] || 0; - } - }; - - return ( - -
- -

- Achievements -

- - {unlockedCount} / {totalCount} - -
- - -
- {Object.entries(categories).map(([category, categoryAchievements]) => ( -
- setExpandedCategory(expandedCategory === category ? null : category)} - aria-expanded={expandedCategory === category} - aria-label={`${category} category - ${categoryAchievements.filter(a => achievements.unlocked.includes(a.id)).length} of ${categoryAchievements.length} unlocked`} - > - - {category.charAt(0).toUpperCase() + category.slice(1)} - - - {categoryAchievements.filter(a => achievements.unlocked.includes(a.id)).length} / {categoryAchievements.length} - - {expandedCategory === category ? ( - - ) : ( - - )} - - - {expandedCategory === category && ( -
- {categoryAchievements.map((achievement) => { - const isUnlocked = achievements.unlocked.includes(achievement.id); - const progress = getProgress(achievement.id); - const isRevealed = isAchievementRevealed(achievement, progress); - const progressPercent = Math.min(100, (progress / achievement.requirement.value) * 100); - - if (!isRevealed && !isUnlocked) { - return ( -
-
-
-
- ); - } - - return ( -
-
-
- {isUnlocked ? ( -
- {achievement.reward.title && isUnlocked && ( - - Title - - )} -
- -
- {achievement.desc} -
- - {!isUnlocked && ( -
- -
- {progress.toLocaleString()} / {achievement.requirement.value.toLocaleString()} - {progressPercent.toFixed(0)}% -
-
- )} - - {isUnlocked && achievement.reward && ( -
- Reward: - {achievement.reward.insight && ` +${achievement.reward.insight} Insight`} - {achievement.reward.manaBonus && ` +${achievement.reward.manaBonus} Max Mana`} - {achievement.reward.damageBonus && ` +${(achievement.reward.damageBonus * 100).toFixed(0)}% Damage`} - {achievement.reward.title && ` "${achievement.reward.title}"`} -
- )} -
- ); - })} -
- )} -
- ))} -
-
-
- ); -} - -AchievementsDisplay.displayName = "AchievementsDisplay"; diff --git a/src/components/game/CalendarDisplay.tsx b/src/components/game/CalendarDisplay.tsx deleted file mode 100755 index 2bca2a9..0000000 --- a/src/components/game/CalendarDisplay.tsx +++ /dev/null @@ -1,53 +0,0 @@ -'use client'; - -import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'; -import { MAX_DAY, INCURSION_START_DAY } from '@/lib/game/constants'; - -interface CalendarDisplayProps { - day: number; - hour: number; - incursionStrength?: number; -} - -export function CalendarDisplay({ day }: CalendarDisplayProps) { - const days: React.ReactElement[] = []; - - for (let d = 1; d <= MAX_DAY; d++) { - let dayClass = 'w-6 h-6 sm:w-7 sm:h-7 rounded text-xs flex items-center justify-center font-mono border transition-all '; - - if (d < day) { - dayClass += 'bg-blue-900/30 border-blue-800/50 text-blue-400'; - } else if (d === day) { - dayClass += 'bg-blue-600/40 border-blue-500 text-blue-300 shadow-lg shadow-blue-500/30'; - } else { - dayClass += 'bg-gray-800/30 border-gray-700/50 text-gray-500'; - } - - if (d >= INCURSION_START_DAY) { - dayClass += ' border-red-600/50'; - } - - days.push( - - -
- {d} -
-
- -

Day {d}

- {d >= INCURSION_START_DAY &&

Incursion Active

} -
-
- ); - } - - return ( -
- {days} -
- ); -} - -CalendarDisplay.displayName = "CalendarDisplay"; -CalendarDisplay.displayName = "CalendarDisplay"; diff --git a/src/components/game/ConfirmDialog.tsx b/src/components/game/ConfirmDialog.tsx deleted file mode 100644 index 1d3bc55..0000000 --- a/src/components/game/ConfirmDialog.tsx +++ /dev/null @@ -1,193 +0,0 @@ -'use client'; - -import { useState, type ReactNode } from 'react'; -import { - AlertDialog, - AlertDialogAction, - AlertDialogCancel, - AlertDialogContent, - AlertDialogDescription, - AlertDialogFooter, - AlertDialogHeader, - AlertDialogTitle, -} from '@/components/ui/alert-dialog'; -import { AlertTriangle, AlertCircle, Info, CheckCircle } from 'lucide-react'; -import { cn } from '@/lib/utils'; - -export type ConfirmDialogVariant = 'danger' | 'warning' | 'info' | 'success'; - -interface ConfirmDialogProps { - /** Whether the dialog is open */ - open: boolean; - /** Callback when open state changes */ - onOpenChange: (open: boolean) => void; - /** Dialog title */ - title: string; - /** Dialog description/content */ - description: ReactNode; - /** Cancel button text (default: "Cancel") */ - cancelText?: string; - /** Confirm button text (default: "Confirm") */ - confirmText?: string; - /** Dialog variant/type */ - variant?: ConfirmDialogVariant; - /** Callback when user confirms */ - onConfirm: () => void | Promise; - /** Callback when user cancels */ - onCancel?: () => void; - /** Whether the confirm action is destructive */ - destructive?: boolean; -} - -const VARIANT_ICONS = { - danger: AlertTriangle, - warning: AlertCircle, - info: Info, - success: CheckCircle, -}; - -const VARIANT_TITLE_COLORS = { - danger: 'text-[var(--color-danger)]', - warning: 'text-[var(--color-warning)]', - info: 'text-[var(--color-info)]', - success: 'text-[var(--color-success)]', -}; - -const VARIANT_ACTION_COLORS = { - danger: 'bg-[var(--color-danger)] hover:bg-[var(--interactive-danger-hover)] text-white', - warning: 'bg-[var(--color-warning)] hover:opacity-90 text-black', - info: 'bg-[var(--color-info)] hover:opacity-90 text-white', - success: 'bg-[var(--color-success)] hover:opacity-90 text-white', -}; - -/** - * Reusable confirmation dialog component. - * Uses the existing shadcn/ui AlertDialog. - * - * @example - * - */ -export function ConfirmDialog({ - open, - onOpenChange, - title, - description, - cancelText = 'Cancel', - confirmText = 'Confirm', - variant = 'warning', - onConfirm, - onCancel, - destructive = false, -}: ConfirmDialogProps) { - const [isLoading, setIsLoading] = useState(false); - const [error, setError] = useState(null); - - const Icon = VARIANT_ICONS[variant]; - const titleColor = VARIANT_TITLE_COLORS[variant]; - const actionClass = destructive ? VARIANT_ACTION_COLORS.danger : VARIANT_ACTION_COLORS[variant]; - - const handleConfirm = async () => { - setIsLoading(true); - setError(null); - try { - await onConfirm(); - onOpenChange(false); - } catch (e) { - setError(e instanceof Error ? e.message : 'Action failed'); - } finally { - setIsLoading(false); - } - }; - - const handleCancel = () => { - onCancel?.(); - onOpenChange(false); - }; - - return ( - - - - - - {title} - - - {description} - - {error && ( -
- {error} -
- )} -
- - - {cancelText} - - - {isLoading ? 'Processing...' : confirmText} - - -
-
- ); -} - -/** - * Hook to easily manage a confirmation dialog state. - * - * @example - * const { dialogProps, showConfirm } = useConfirmDialog(); - * - * showConfirm({ - * title: "Delete Item", - * description: "Are you sure?", - * onConfirm: () => deleteItem(), - * }); - */ -export function useConfirmDialog() { - const [dialogState, setDialogState] = useState<{ - open: boolean; - props: Omit; - }>({ - open: false, - props: { - title: '', - description: '', - onConfirm: () => {}, - }, - }); - - const showConfirm = (props: Omit) => { - setDialogState({ open: true, props }); - }; - - const dialogProps: ConfirmDialogProps = { - open: dialogState.open, - onOpenChange: (open: boolean) => setDialogState(prev => ({ ...prev, open })), - ...dialogState.props, - }; - - return { - dialogProps, - showConfirm, - ConfirmDialogComponent: , - }; -} - -export default ConfirmDialog; diff --git a/src/components/game/CraftingProgress.tsx b/src/components/game/CraftingProgress.tsx deleted file mode 100755 index 4181ee7..0000000 --- a/src/components/game/CraftingProgress.tsx +++ /dev/null @@ -1,163 +0,0 @@ -'use client'; - -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/utils/formatting'; -import type { EquipmentInstance, EnchantmentDesign } from '@/lib/game/types'; - -interface CraftingProgressProps { - designProgress: { designId: string; progress: number; required: number } | null; - preparationProgress: { equipmentInstanceId: string; progress: number; required: number; manaCostPaid: number } | null; - applicationProgress: { equipmentInstanceId: string; designId: string; progress: number; required: number; manaPerHour: number; paused: boolean } | null; - equipmentInstances: Record; - enchantmentDesigns: EnchantmentDesign[]; - cancelDesign: () => void; - cancelPreparation: () => void; - pauseApplication: () => void; - resumeApplication: () => void; - cancelApplication: () => void; -} - -export function CraftingProgress({ - designProgress, - preparationProgress, - applicationProgress, - equipmentInstances, - enchantmentDesigns, - cancelDesign, - cancelPreparation, - pauseApplication, - resumeApplication, - cancelApplication, -}: CraftingProgressProps) { - const progressSections: React.ReactNode[] = []; - - // Design progress - if (designProgress) { - const progressPct = Math.min(100, (designProgress.progress / designProgress.required) * 100); - progressSections.push( -
-
-
- - - Designing Enchantment - -
- -
- -
- {formatStudyTime(designProgress.progress)} / {formatStudyTime(designProgress.required)} - Design Time -
-
- ); - } - - // Preparation progress - if (preparationProgress) { - const progressPct = Math.min(100, (preparationProgress.progress / preparationProgress.required) * 100); - const instance = equipmentInstances[preparationProgress.equipmentInstanceId]; - progressSections.push( -
-
-
- - - Preparing {instance?.name || 'Equipment'} - -
- -
- -
- {formatStudyTime(preparationProgress.progress)} / {formatStudyTime(preparationProgress.required)} - Mana spent: {fmt(preparationProgress.manaCostPaid)} -
-
- ); - } - - // Application progress - if (applicationProgress) { - const progressPct = Math.min(100, (applicationProgress.progress / applicationProgress.required) * 100); - const instance = equipmentInstances[applicationProgress.equipmentInstanceId]; - const design = enchantmentDesigns.find(d => d.id === applicationProgress.designId); - progressSections.push( -
-
-
- - - Enchanting {instance?.name || 'Equipment'} - -
-
- {applicationProgress.paused ? ( - - ) : ( - - )} - -
-
- -
- {formatStudyTime(applicationProgress.progress)} / {formatStudyTime(applicationProgress.required)} - Mana/hr: {fmt(applicationProgress.manaPerHour)} -
- {design && ( -
- Applying: {design.name} -
- )} -
- ); - } - - return progressSections.length > 0 ? ( -
- {progressSections} -
- ) : null; -} - -CraftingProgress.displayName = "CraftingProgress"; diff --git a/src/components/game/LootInventory/LootInventoryDisplay.tsx b/src/components/game/LootInventory/LootInventoryDisplay.tsx deleted file mode 100644 index 240bd32..0000000 --- a/src/components/game/LootInventory/LootInventoryDisplay.tsx +++ /dev/null @@ -1,310 +0,0 @@ -'use client'; - -import { useState } from 'react'; -import { GameCard } from '@/components/ui/game-card'; -import { Badge } from '@/components/ui/badge'; -import { ActionButton } from '@/components/ui/action-button'; -import { ScrollArea } from '@/components/ui/scroll-area'; -import { Separator } from '@/components/ui/separator'; -import { Input } from '@/components/ui/input'; -import { - Gem, Search, ArrowUpDown, AlertTriangle -} from 'lucide-react'; -import { ElementBadge } from '@/components/ui/element-badge'; -import type { LootInventory as LootInventoryType, EquipmentInstance, ElementState } from '@/lib/game/types'; -import { LOOT_DROPS } from '@/lib/game/data/loot-drops'; -import { EQUIPMENT_TYPES } from '@/lib/game/data/equipment'; -import { ELEMENTS } from '@/lib/game/constants'; -import { useGameToast } from '@/components/game/GameToast'; -import { - AlertDialog, - AlertDialogAction, - AlertDialogCancel, - AlertDialogContent, - AlertDialogDescription, - AlertDialogFooter, - AlertDialogHeader, - AlertDialogTitle, -} from '@/components/ui/alert-dialog'; - -import { type SortMode, type FilterMode, RARITY_ORDER } from './types'; -import { MaterialsSection } from './MaterialItem'; -import { EssenceSection } from './EssenceItem'; -import { BlueprintsSection } from './BlueprintsSection'; -import { EquipmentSection } from './EquipmentItem'; - -interface LootInventoryProps { - inventory: LootInventoryType; - elements?: Record; - equipmentInstances?: Record; - onDeleteMaterial?: (materialId: string, amount: number) => void; - onDeleteEquipment?: (instanceId: string) => void; -} - -export function LootInventoryDisplay({ - inventory, - elements, - equipmentInstances = {}, - onDeleteMaterial, - onDeleteEquipment, -}: LootInventoryProps) { - const showToast = useGameToast(); - const [searchTerm, setSearchTerm] = useState(''); - const [sortMode, setSortMode] = useState('rarity'); - const [filterMode, setFilterMode] = useState('all'); - const [deleteConfirm, setDeleteConfirm] = useState<{ type: 'material' | 'equipment'; id: string; name: string } | null>(null); - - // Count items - const materialCount = Object.values(inventory.materials || {}).reduce((a, b) => a + b, 0); - const essenceCount = elements ? Object.entries(elements).reduce((a, [id, e]) => id === 'transference' ? a : a + e.current, 0) : 0; - const blueprintCount = inventory.blueprints.length; - const equipmentCount = Object.keys(equipmentInstances).length; - const totalItems = materialCount + blueprintCount + equipmentCount; - - // Filter and sort materials - const filteredMaterials = Object.entries(inventory.materials) - .filter(([id, count]) => { - if (count <= 0) return false; - const drop = LOOT_DROPS[id]; - if (!drop) return false; - if (searchTerm && !drop.name.toLowerCase().includes(searchTerm.toLowerCase())) return false; - return true; - }) - .sort(([aId, aCount], [bId, bCount]) => { - const aDrop = LOOT_DROPS[aId]; - const bDrop = LOOT_DROPS[bId]; - if (!aDrop || !bDrop) return 0; - - switch (sortMode) { - case 'name': - return aDrop.name.localeCompare(bDrop.name); - case 'rarity': - return RARITY_ORDER[bDrop.rarity] - RARITY_ORDER[aDrop.rarity]; - case 'count': - return bCount - aCount; - default: - return 0; - } - }); - - // Filter and sort essence - const filteredEssence = elements - ? Object.entries(elements) - .filter(([id, state]) => { - if (!state.unlocked || state.current <= 0) return false; - if (id === 'transference') return false; - if (searchTerm && !ELEMENTS[id]?.name.toLowerCase().includes(searchTerm.toLowerCase())) return false; - return true; - }) - .sort(([aId, aState], [bId, bState]) => { - switch (sortMode) { - case 'name': - return (ELEMENTS[aId]?.name || aId).localeCompare(ELEMENTS[bId]?.name || bId); - case 'count': - return bState.current - aState.current; - default: - return 0; - } - }) - : []; - - // Filter and sort equipment - const filteredEquipment = Object.entries(equipmentInstances) - .filter(([id, instance]) => { - if (searchTerm && !instance.name.toLowerCase().includes(searchTerm.toLowerCase())) return false; - return true; - }) - .sort(([aId, aInst], [bId, bInst]) => { - switch (sortMode) { - case 'name': - return aInst.name.localeCompare(bInst.name); - case 'rarity': - return RARITY_ORDER[bInst.rarity] - RARITY_ORDER[aInst.rarity]; - default: - return 0; - } - }); - - // Check if we have anything to show - const hasItems = totalItems > 0 || essenceCount > 0; - - const handleDeleteMaterial = (materialId: string) => { - const drop = LOOT_DROPS[materialId]; - if (drop) { - setDeleteConfirm({ type: 'material', id: materialId, name: drop.name }); - } - }; - - const handleDeleteEquipment = (instanceId: string) => { - const instance = equipmentInstances[instanceId]; - if (instance) { - setDeleteConfirm({ type: 'equipment', id: instanceId, name: instance.name }); - } - }; - - const confirmDelete = () => { - if (!deleteConfirm) return; - - if (deleteConfirm.type === 'material' && onDeleteMaterial) { - const amount = inventory.materials[deleteConfirm.id] || 0; - onDeleteMaterial(deleteConfirm.id, amount); - showToast('success', 'Material Deleted', `${deleteConfirm.name} removed from inventory`); - } else if (deleteConfirm.type === 'equipment' && onDeleteEquipment) { - onDeleteEquipment(deleteConfirm.id); - showToast('success', 'Item Discarded', `${deleteConfirm.name} has been removed from inventory`); - } - - setDeleteConfirm(null); - }; - - if (!hasItems) { - return ( - -
- -

- Inventory -

-
-
- No items collected yet. Defeat floors and guardians to find loot! -
-
- ); - } - - return ( - <> - -
- -

- Inventory -

- - {totalItems} items - -
- - {/* Search and Filter Controls */} -
-
- - setSearchTerm(e.target.value)} - className="h-7 pl-7 bg-[var(--bg-sunken)] border-[var(--border-subtle)] text-xs text-[var(--text-primary)] placeholder:text-[var(--text-disabled)]" - aria-label="Search inventory" - /> -
- setSortMode(sortMode === 'rarity' ? 'name' : sortMode === 'name' ? 'count' : 'rarity')} - aria-label={`Sort by ${sortMode === 'rarity' ? 'name' : sortMode === 'name' ? 'count' : 'rarity'}`} - > - - -
- - {/* Filter Tabs */} -
- {[ - { mode: 'all' as FilterMode, label: 'All' }, - { mode: 'materials' as FilterMode, label: `Materials (${materialCount})` }, - { mode: 'essence' as FilterMode, label: `Essence (${essenceCount})` }, - { mode: 'blueprints' as FilterMode, label: `Blueprints (${blueprintCount})` }, - { mode: 'equipment' as FilterMode, label: `Equipment (${equipmentCount})` }, - ].map(({ mode, label }) => ( - setFilterMode(mode)} - aria-pressed={filterMode === mode} - aria-label={`Filter by ${label}`} - > - {label} - - ))} -
- - - - -
- {/* Materials */} - {(filterMode === 'all' || filterMode === 'materials') && ( - - )} - - {/* Essence */} - {(filterMode === 'all' || filterMode === 'essence') && ( - - )} - - {/* Blueprints */} - {(filterMode === 'all' || filterMode === 'blueprints') && ( - - )} - - {/* Equipment */} - {(filterMode === 'all' || filterMode === 'equipment') && ( - - )} -
-
-
- - {/* Delete Confirmation Dialog */} - setDeleteConfirm(null)}> - - - - - Delete Item - - - Are you sure you want to delete {deleteConfirm?.name}? - {deleteConfirm?.type === 'material' && ( - - This will delete ALL {inventory.materials[deleteConfirm?.id || ''] || 0} of this material! - - )} - {deleteConfirm?.type === 'equipment' && ( - - This equipment and all its enchantments will be permanently lost! - - )} - - - - - Cancel - - - Delete - - - - - - ); -} - -LootInventoryDisplay.displayName = "LootInventoryDisplay"; diff --git a/src/components/game/LootInventory/index.tsx b/src/components/game/LootInventory/index.tsx deleted file mode 100644 index df0db56..0000000 --- a/src/components/game/LootInventory/index.tsx +++ /dev/null @@ -1,318 +0,0 @@ -'use client'; - -import { useState } from 'react'; -import { GameCard } from '@/components/ui/game-card'; -import { Badge } from '@/components/ui/badge'; -import { ActionButton } from '@/components/ui/action-button'; -import { ScrollArea } from '@/components/ui/scroll-area'; -import { Separator } from '@/components/ui/separator'; -import { Input } from '@/components/ui/input'; -import { - Gem, Search, ArrowUpDown, AlertTriangle -} from 'lucide-react'; -import { ElementBadge } from '@/components/ui/element-badge'; -import type { LootInventory as LootInventoryType, EquipmentInstance, ElementState } from '@/lib/game/types'; -import { LOOT_DROPS, LOOT_RARITY_COLORS } from '@/lib/game/data/loot-drops'; -import { EQUIPMENT_TYPES } from '@/lib/game/data/equipment'; -import { ELEMENTS } from '@/lib/game/constants'; -import { useGameToast } from '@/components/game/GameToast'; -import { - AlertDialog, - AlertDialogAction, - AlertDialogCancel, - AlertDialogContent, - AlertDialogDescription, - AlertDialogFooter, - AlertDialogHeader, - AlertDialogTitle, -} from '@/components/ui/alert-dialog'; - -import { type SortMode, type FilterMode, RARITY_ORDER, RARITY_CSS_VAR, RARITY_GLOW_CSS_VAR } from './types'; -import { MaterialsSection } from './MaterialItem'; -import { EssenceSection } from './EssenceItem'; -import { BlueprintsSection } from './BlueprintsSection'; -import { EquipmentSection } from './EquipmentItem'; - -interface LootInventoryProps { - inventory: LootInventoryType; - elements?: Record; - equipmentInstances?: Record; - onDeleteMaterial?: (materialId: string, amount: number) => void; - onDeleteEquipment?: (instanceId: string) => void; -} - -export function LootInventoryDisplay({ - inventory, - elements, - equipmentInstances = {}, - onDeleteMaterial, - onDeleteEquipment, -}: LootInventoryProps) { - const showToast = useGameToast(); - const [searchTerm, setSearchTerm] = useState(''); - const [sortMode, setSortMode] = useState('rarity'); - const [filterMode, setFilterMode] = useState('all'); - const [deleteConfirm, setDeleteConfirm] = useState<{ type: 'material' | 'equipment'; id: string; name: string } | null>(null); - - // Count items - const materialCount = Object.values(inventory.materials || {}).reduce((a: number, b: number) => a + b, 0); - - // Calculate essence count - let essenceCount = 0; - if (elements) { - essenceCount = Object.entries(elements).reduce((acc: number, [id, state]) => { - if (id === 'transference') return acc; - return acc + (state.current || 0); - }, 0); - } - - const blueprintCount = inventory.blueprints.length; - const equipmentCount = Object.keys(equipmentInstances).length; - const totalItems = materialCount + blueprintCount + equipmentCount; - - // Filter and sort materials - const filteredMaterials = Object.entries(inventory.materials) - .filter(([id, count]) => { - if (count <= 0) return false; - const drop = LOOT_DROPS[id]; - if (!drop) return false; - if (searchTerm && !drop.name.toLowerCase().includes(searchTerm.toLowerCase())) return false; - return true; - }) - .sort(([aId, aCount], [bId, bCount]) => { - const aDrop = LOOT_DROPS[aId]; - const bDrop = LOOT_DROPS[bId]; - if (!aDrop || !bDrop) return 0; - - switch (sortMode) { - case 'name': - return aDrop.name.localeCompare(bDrop.name); - case 'rarity': - return RARITY_ORDER[bDrop.rarity] - RARITY_ORDER[aDrop.rarity]; - case 'count': - return bCount - aCount; - default: - return 0; - } - }); - - // Filter and sort essence - const filteredEssence = elements - ? Object.entries(elements) - .filter(([id, state]) => { - if (!state.unlocked || state.current <= 0) return false; - if (id === 'transference') return false; - if (searchTerm && !ELEMENTS[id]?.name.toLowerCase().includes(searchTerm.toLowerCase())) return false; - return true; - }) - .sort(([aId, aState], [bId, bState]) => { - switch (sortMode) { - case 'name': - return (ELEMENTS[aId]?.name || aId).localeCompare(ELEMENTS[bId]?.name || bId); - case 'count': - return bState.current - aState.current; - default: - return 0; - } - }) - : []; - - // Filter and sort equipment - const filteredEquipment = Object.entries(equipmentInstances) - .filter(([id, instance]) => { - if (searchTerm && !instance.name.toLowerCase().includes(searchTerm.toLowerCase())) return false; - return true; - }) - .sort(([aId, aInst], [bId, bInst]) => { - switch (sortMode) { - case 'name': - return aInst.name.localeCompare(bInst.name); - case 'rarity': - return RARITY_ORDER[bInst.rarity] - RARITY_ORDER[aInst.rarity]; - default: - return 0; - } - }); - - const hasItems = totalItems > 0 || essenceCount > 0; - - const handleDeleteMaterial = (materialId: string) => { - const drop = LOOT_DROPS[materialId]; - if (drop) { - setDeleteConfirm({ type: 'material', id: materialId, name: drop.name }); - } - }; - - const handleDeleteEquipment = (instanceId: string) => { - const instance = equipmentInstances[instanceId]; - if (instance) { - setDeleteConfirm({ type: 'equipment', id: instanceId, name: instance.name }); - } - }; - - const confirmDelete = () => { - if (!deleteConfirm) return; - - if (deleteConfirm.type === 'material' && onDeleteMaterial) { - const amount = inventory.materials[deleteConfirm.id] || 0; - onDeleteMaterial(deleteConfirm.id, amount); - showToast('success', 'Material Deleted', `${deleteConfirm.name} removed from inventory`); - } else if (deleteConfirm.type === 'equipment' && onDeleteEquipment) { - onDeleteEquipment(deleteConfirm.id); - showToast('success', 'Item Discarded', `${deleteConfirm.name} has been removed from inventory`); - } - - setDeleteConfirm(null); - }; - - if (!hasItems) { - return ( - -
- -

- Inventory -

-
-
- No items collected yet. Defeat floors and guardians to find loot! -
-
- ); - } - - return ( - <> - -
- -

- Inventory -

- - {totalItems} items - -
- - {/* Search and Filter Controls */} -
-
- - setSearchTerm(e.target.value)} - className="h-7 pl-7 bg-[var(--bg-sunken)] border-[var(--border-subtle)] text-xs text-[var(--text-primary)] placeholder:text-[var(--text-disabled)]" - aria-label="Search inventory" - /> -
- setSortMode(sortMode === 'rarity' ? 'name' : sortMode === 'name' ? 'count' : 'rarity')} - aria-label={`Sort by ${sortMode === 'rarity' ? 'name' : sortMode === 'name' ? 'count' : 'rarity'}`} - > - - -
- - {/* Filter Tabs */} -
- {[ - { mode: 'all' as FilterMode, label: 'All' }, - { mode: 'materials' as FilterMode, label: `Materials (${materialCount})` }, - { mode: 'essence' as FilterMode, label: `Essence (${essenceCount})` }, - { mode: 'blueprints' as FilterMode, label: `Blueprints (${blueprintCount})` }, - { mode: 'equipment' as FilterMode, label: `Equipment (${equipmentCount})` }, - ].map(({ mode, label }) => ( - setFilterMode(mode)} - aria-pressed={filterMode === mode} - aria-label={`Filter by ${label}`} - > - {label} - - ))} -
- - - - -
- {/* Materials */} - {(filterMode === 'all' || filterMode === 'materials') && ( - - )} - - {/* Essence */} - {(filterMode === 'all' || filterMode === 'essence') && ( - - )} - - {/* Blueprints */} - {(filterMode === 'all' || filterMode === 'blueprints') && ( - - )} - - {/* Equipment */} - {(filterMode === 'all' || filterMode === 'equipment') && ( - - )} -
-
-
- - {/* Delete Confirmation Dialog */} - setDeleteConfirm(null)}> - - - - - Delete Item - - - Are you sure you want to delete {deleteConfirm?.name}? - {deleteConfirm?.type === 'material' && ( - - This will delete ALL {inventory.materials[deleteConfirm?.id || ''] || 0} of this material! - - )} - {deleteConfirm?.type === 'equipment' && ( - - This equipment and all its enchantments will be permanently lost! - - )} - - - - - Cancel - - - Delete - - - - - - ); -} - -LootInventoryDisplay.displayName = "LootInventoryDisplay"; diff --git a/src/components/game/StudyProgress.tsx b/src/components/game/StudyProgress.tsx deleted file mode 100755 index 43f7159..0000000 --- a/src/components/game/StudyProgress.tsx +++ /dev/null @@ -1,59 +0,0 @@ -'use client'; - -import { Button } from '@/components/ui/button'; -import { Progress } from '@/components/ui/progress'; -import { BookOpen, X } from 'lucide-react'; -import { SPELLS_DEF } from '@/lib/game/constants'; -import { formatStudyTime } from '@/lib/game/utils/formatting'; -import type { StudyTarget } from '@/lib/game/types'; - -interface StudyProgressProps { - currentStudyTarget: StudyTarget | null; - skills: Record; - studySpeedMult: number; - cancelStudy: () => void; -} - -export function StudyProgress({ - currentStudyTarget, - skills, - studySpeedMult, - cancelStudy, -}: StudyProgressProps) { - if (!currentStudyTarget) return null; - - const target = currentStudyTarget; - const progressPct = Math.min(100, (target.progress / target.required) * 100); - const isSkill = target.type === 'skill'; - const def = isSkill ? undefined : SPELLS_DEF[target.id]; - const currentLevel = isSkill ? (skills[target.id] || 0) : 0; - - return ( -
-
-
- - - {def?.name ?? target.id} - {isSkill && ` Lv.${currentLevel + 1}`} - -
- -
- -
- {formatStudyTime(target.progress)} / {formatStudyTime(target.required)} - {studySpeedMult.toFixed(1)}x speed -
-
- ); -} - -StudyProgress.displayName = "StudyProgress"; diff --git a/src/components/game/index.ts b/src/components/game/index.ts index d9a58a6..7568ae9 100755 --- a/src/components/game/index.ts +++ b/src/components/game/index.ts @@ -7,9 +7,6 @@ export { StatsTab } from './StatsTab'; // UI components export { ActionButtons } from './ActionButtons'; -export { CalendarDisplay } from './CalendarDisplay'; -export { CraftingProgress } from './CraftingProgress'; -export { StudyProgress } from './StudyProgress'; export { ManaDisplay } from './ManaDisplay'; export { TimeDisplay } from './TimeDisplay'; export { UpgradeDialog } from './UpgradeDialog'; diff --git a/src/components/game/tabs/DisciplinesTab.tsx b/src/components/game/tabs/DisciplinesTab.tsx index 006e6d4..347491e 100644 --- a/src/components/game/tabs/DisciplinesTab.tsx +++ b/src/components/game/tabs/DisciplinesTab.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from 'react'; +import React, { useEffect, useState, useCallback } from 'react'; import { useDisciplineStore } from '@/lib/game/stores/discipline-slice'; import type { DisciplineDefinition } from '@/lib/game/types/disciplines'; import { baseDisciplines } from '@/lib/game/data/disciplines/base'; @@ -33,6 +33,10 @@ interface DisciplineCardProps { drainBase: number; difficultyFactor: number; scalingFactor: number; + xp: number; + paused: boolean; + concurrentLimit: number; + onToggle: (id: string, paused: boolean) => void; } const DisciplineCard: React.FC = ({ @@ -47,14 +51,14 @@ const DisciplineCard: React.FC = ({ drainBase, difficultyFactor, scalingFactor, + xp, + paused, + concurrentLimit, + onToggle, }) => { - const activeIds = useDisciplineStore((s) => s.activeIds); - const concurrentLimit = useDisciplineStore((s) => s.concurrentLimit); - const currentDisc = useDisciplineStore((s) => s.disciplines[id] ?? { xp: 0, paused: true }); - - const displayXp = currentDisc.xp; + const displayXp = xp; const progressPercent = Math.min(displayXp / Math.max(1, concurrentLimit * 100), 100); - const isPaused = currentDisc.paused; + const isPaused = paused; const activeStatBonus = calculateStatBonus(baseValue, displayXp, scalingFactor); const estimatedDrain = calculateManaDrain(drainBase, displayXp, difficultyFactor); @@ -73,11 +77,7 @@ const DisciplineCard: React.FC = ({ }, []); const toggleAction = () => { - if (isPaused) { - useDisciplineStore.getState().activate(id); - } else { - useDisciplineStore.getState().deactivate(id); - } + onToggle(id, isPaused); }; return ( @@ -135,7 +135,11 @@ const DisciplineCard: React.FC = ({ }; export const DisciplinesTab: React.FC = () => { - const { activeIds, concurrentLimit } = useDisciplineStore(); + const activeIds = useDisciplineStore((s) => s.activeIds); + const concurrentLimit = useDisciplineStore((s) => s.concurrentLimit); + const disciplines = useDisciplineStore((s) => s.disciplines); + const activate = useDisciplineStore((s) => s.activate); + const deactivate = useDisciplineStore((s) => s.deactivate); const [mounted, setMounted] = useState(false); const [activeAttunement, setActiveAttunement] = useState('base'); @@ -144,6 +148,14 @@ export const DisciplinesTab: React.FC = () => { setMounted(true); }, []); + const handleToggle = useCallback((id: string, paused: boolean) => { + if (paused) { + activate(id); + } else { + deactivate(id); + } + }, [activate, deactivate]); + if (!mounted) { return (
@@ -177,22 +189,29 @@ export const DisciplinesTab: React.FC = () => { {/* Discipline cards — only render active tab */}
- {activeTab?.items.map((disc) => ( - p.threshold)} - perkValues={disc.perks?.map((p) => p.value)} - perkTypes={disc.perks?.map((p) => p.type)} - statBonus={disc.statBonus.stat} - baseValue={disc.statBonus.baseValue} - drainBase={disc.drainBase} - difficultyFactor={disc.difficultyFactor} - scalingFactor={disc.scalingFactor} - /> - ))} + {activeTab?.items.map((disc) => { + const discState = disciplines[disc.id] ?? { xp: 0, paused: true }; + return ( + p.threshold)} + perkValues={disc.perks?.map((p) => p.value)} + perkTypes={disc.perks?.map((p) => p.type)} + statBonus={disc.statBonus.stat} + baseValue={disc.statBonus.baseValue} + drainBase={disc.drainBase} + difficultyFactor={disc.difficultyFactor} + scalingFactor={disc.scalingFactor} + xp={discState.xp} + paused={discState.paused} + concurrentLimit={concurrentLimit} + onToggle={handleToggle} + /> + ); + })}
{/* Summary info */} diff --git a/src/lib/game/crafting-actions/equipment-actions.ts b/src/lib/game/crafting-actions/equipment-actions.ts index 358879e..ea0fd02 100644 --- a/src/lib/game/crafting-actions/equipment-actions.ts +++ b/src/lib/game/crafting-actions/equipment-actions.ts @@ -45,7 +45,7 @@ export function equipItem( const instance = state.equipmentInstances[instanceId]; if (!instance) return false; - if (!CraftingUtils.canEquipInSlot(instance, slot, state.equippedInstances)) { + if (!CraftingUtils.canEquipInSlot(instance, slot, state.equippedInstances, state.equipmentInstances)) { return false; } diff --git a/src/lib/game/crafting-utils.ts b/src/lib/game/crafting-utils.ts index 876bdee..4f0cf54 100644 --- a/src/lib/game/crafting-utils.ts +++ b/src/lib/game/crafting-utils.ts @@ -123,7 +123,8 @@ export function refundCraftMaterials(recipe: CraftingRecipe, refundRate: number export function canEquipInSlot( instance: EquipmentInstance, slot: EquipmentSlot, - currentlyEquipped: Record + currentlyEquipped: Record, + instances: Record = {}, ): boolean { const type = EQUIPMENT_TYPES[instance.typeId]; if (!type) return false; @@ -145,7 +146,8 @@ export function canEquipInSlot( } if (slot === 'offHand' && currentlyEquipped.mainHand) { - const mainHandType = EQUIPMENT_TYPES[currentlyEquipped.mainHand]; + const mainHandInstance = instances[currentlyEquipped.mainHand]; + const mainHandType = mainHandInstance ? EQUIPMENT_TYPES[mainHandInstance.typeId] : undefined; if (mainHandType?.twoHanded) { return false; } diff --git a/src/lib/game/data/disciplines/base-disciplines.ts b/src/lib/game/data/disciplines/base-disciplines.ts deleted file mode 100644 index 4e87c8f..0000000 --- a/src/lib/game/data/disciplines/base-disciplines.ts +++ /dev/null @@ -1,34 +0,0 @@ -type DisciplineDefinition = { -name: string; -attunement: DisciplinesAttunementType; -manaType: ManaType; -baseCost: number; -description: string; -requires?: DisciplineDefinition[]; -}; - -enum DisciplinesAttunementType { -base, -enchanter, -fabricator, -invoker -}; - -export const baseDisciplines: DisciplineDefinition[] = [ -{ -name: "Embercraft", -attunement: DisciplinesAttunementType.base, -manaType: "fire", -baseCost: 10, -description: "Basic flame projection with autocrit on combustion explosion", -requires: [] -}, -{ -name: "Earthbind", -attunement: DisciplinesAttunementType.base, -manaType: "earth", -baseCost: 12, -description: "Basic mana chains with passive ground stability", -requires: [] -} -]; \ No newline at end of file diff --git a/src/lib/game/data/disciplines/enchanter-disciplines.ts b/src/lib/game/data/disciplines/enchanter-disciplines.ts deleted file mode 100644 index 032bed0..0000000 --- a/src/lib/game/data/disciplines/enchanter-disciplines.ts +++ /dev/null @@ -1,34 +0,0 @@ -type DisciplineDefinition = { -name: string; -attunement: DisciplinesAttunementType; -manaType: ManaType; -baseCost: number; -description: string; -requires?: DisciplineDefinition[]; -}; - -enum DisciplinesAttunementType { -base, -enchanter, -fabricator, -invoker -}; - -export const enchanterDisciplines: DisciplineDefinition[] = [ -{ -name: "Soulforge", -attunement: DisciplinesAttunementType.enchanter, -manaType: "light", -baseCost: 25, -description: "Mana chains that create permanent elemental storage nodes", -requires: [{name: "Embercraft"}] -}, -{ -name: "Mana Prism", -attunement: DisciplinesAttunementType.enchanter, -manaType: "light", -baseCost: 30, -description: "Prismatic mana focusing that reflexes attacks as fixed ratio", -requires: [{name: "Soulforge"}] -} -]; \ No newline at end of file diff --git a/src/lib/game/data/disciplines/fabricator-disciplines.ts b/src/lib/game/data/disciplines/fabricator-disciplines.ts deleted file mode 100644 index 8418354..0000000 --- a/src/lib/game/data/disciplines/fabricator-disciplines.ts +++ /dev/null @@ -1,21 +0,0 @@ -import type { DisciplineDefinition } from '../../types/disciplines'; - -const fabricatorDisciplines: DisciplineDefinition[] = [ - { - name: 'Metalworking', - attunement: 'fabricator', - manaType: 'metal', - baseCosts: { mana: 28, time: 7 }, - description: 'Increase metal equipment crafting speed', - thresholds: { xp: 140, interval: 70 } - }, - { - name: 'Crystal Shaping', - attunement: 'fabricator', - manaType: 'crystal', - baseCosts: { mana: 30, time: 8 }, - description: 'Increase crystal equipment durability', - thresholds: { xp: 160, interval: 80 } - } -]; -export default fabricatorDisciplines; \ No newline at end of file diff --git a/src/lib/game/data/disciplines/invoker-disciplines.ts b/src/lib/game/data/disciplines/invoker-disciplines.ts deleted file mode 100644 index 2fc37ee..0000000 --- a/src/lib/game/data/disciplines/invoker-disciplines.ts +++ /dev/null @@ -1,21 +0,0 @@ -import type { DisciplineDefinition } from '../../types/disciplines'; - -const invokerDisciplines: DisciplineDefinition[] = [ - { - name: 'Lightning Surge', - attunement: 'invoker', - manaType: 'lightning', - baseCost: 30, - description: 'Boost lightning spell damage', - thresholds: { xp: 150, interval: 75 } - }, - { - name: 'Void Echo', - attunement: 'invoker', - manaType: 'void', - baseCost: 35, - description: 'Increase void spell cast speed', - thresholds: { xp: 180, interval: 90 } - } -]; -export default invokerDisciplines; diff --git a/src/lib/game/data/enchantments/index.ts b/src/lib/game/data/enchantments/index.ts index 7e27d67..ca14ec4 100644 --- a/src/lib/game/data/enchantments/index.ts +++ b/src/lib/game/data/enchantments/index.ts @@ -3,6 +3,7 @@ // Import types import type { EnchantmentEffectCategory, EnchantmentEffectDef } from '../enchantment-types' +import type { EquipmentCategory } from '../equipment' // Import all category-specific effect collections import { SPELL_EFFECTS } from './spell-effects' diff --git a/src/lib/game/effects/upgrade-effects.types.ts b/src/lib/game/effects/upgrade-effects.types.ts index ac2ee20..86b076b 100644 --- a/src/lib/game/effects/upgrade-effects.types.ts +++ b/src/lib/game/effects/upgrade-effects.types.ts @@ -1,13 +1,11 @@ // ─── Upgrade Effect Types ──────────────────────────────────────────────────── // Type interfaces for upgrade effects -import type { SkillUpgradeChoice, SkillUpgradeEffect } from './types'; - export interface ActiveUpgradeEffect { upgradeId: string; skillId: string; milestone: 5 | 10; - effect: SkillUpgradeEffect; + effect: string; name: string; desc: string; } @@ -23,7 +21,7 @@ export interface ComputedEffects { meditationEfficiency: number; spellCostMultiplier: number; conversionEfficiency: number; - + // Combat effects baseDamageMultiplier: number; baseDamageBonus: number; @@ -31,33 +29,33 @@ export interface ComputedEffects { critChanceBonus: number; critDamageMultiplier: number; elementalDamageMultiplier: number; - + // Study effects studySpeedMultiplier: number; studyCostMultiplier: number; progressRetention: number; instantStudyChance: number; freeStudyChance: number; - + // Element effects elementCapMultiplier: number; elementCapBonus: number; perElementCapBonus: Record; conversionCostMultiplier: number; doubleCraftChance: number; - + // Special values permanentRegenBonus: number; - + // Special effect flags specials: Set; - + // All active upgrades for display activeUpgrades: ActiveUpgradeEffect[]; - + // DEEP_UNDERSTANDING: +10% bonus from all skill levels skillLevelMultiplier: number; - + // Enchantment Power enchantmentPowerMultiplier: number; } diff --git a/src/lib/game/stores/combat-actions.ts b/src/lib/game/stores/combat-actions.ts index 5b461d3..0752ac1 100644 --- a/src/lib/game/stores/combat-actions.ts +++ b/src/lib/game/stores/combat-actions.ts @@ -51,8 +51,6 @@ export function processCombatTick( const afterCost = deductSpellCost(spellDef.cost, rawMana, elements); rawMana = afterCost.rawMana; elements = afterCost.elements; - totalManaGathered += spellDef.cost.amount; - // Calculate base damage const floorElement = getFloorElement(currentFloor); const damage = calcDamage( @@ -107,8 +105,6 @@ export function processCombatTick( const eAfterCost = deductSpellCost(eSpellDef.cost, rawMana, elements); rawMana = eAfterCost.rawMana; elements = eAfterCost.elements; - totalManaGathered += eSpellDef.cost.amount; - // Calculate damage const eFloorElement = getFloorElement(currentFloor); const eDamage = calcDamage( diff --git a/src/lib/game/stores/combatStore.ts b/src/lib/game/stores/combatStore.ts index b1c59bc..7738242 100755 --- a/src/lib/game/stores/combatStore.ts +++ b/src/lib/game/stores/combatStore.ts @@ -274,7 +274,7 @@ export const useCombatStore = create()( currentFloor: state.currentFloor, maxFloorReached: state.maxFloorReached, spells: state.spells, - activeSpell: state.activeAction, + activeSpell: state.activeSpell, }), } ) diff --git a/src/lib/game/stores/discipline-slice.ts b/src/lib/game/stores/discipline-slice.ts index 6cd1477..2615111 100644 --- a/src/lib/game/stores/discipline-slice.ts +++ b/src/lib/game/stores/discipline-slice.ts @@ -80,9 +80,10 @@ export const useDisciplineStore = create()( let rawMana = mana.rawMana; const elements = { ...mana.elements }; let newXP = s.totalXP; + const newDisciplines = { ...s.disciplines }; for (const id of s.activeIds) { - const disc = s.disciplines[id]; + const disc = newDisciplines[id]; if (!disc) continue; if (disc.paused) continue; @@ -94,7 +95,7 @@ export const useDisciplineStore = create()( const available = def.manaType === 'raw' ? rawMana : element?.current; if (!available || available < drain) { - disc.paused = true; + newDisciplines[id] = { ...disc, paused: true }; continue; } @@ -104,7 +105,7 @@ export const useDisciplineStore = create()( elements[def.manaType].current -= drain; } - disc.xp += 1; + newDisciplines[id] = { ...disc, xp: disc.xp + 1 }; newXP += 1; } @@ -114,7 +115,7 @@ export const useDisciplineStore = create()( ); set({ - disciplines: s.disciplines, + disciplines: newDisciplines, totalXP: newXP, concurrentLimit: Math.max(s.concurrentLimit, newLimit), }); diff --git a/src/lib/game/stores/gameActions.ts b/src/lib/game/stores/gameActions.ts index d894254..09041cb 100644 --- a/src/lib/game/stores/gameActions.ts +++ b/src/lib/game/stores/gameActions.ts @@ -18,7 +18,7 @@ export const createResetGame = (set: (state: any) => void, initialState: any) => const startFloor = 1; - useUIStore.getState().resetUI(); + useUIStore.getState().reset(); usePrestigeStore.getState().resetPrestige(); useManaStore.getState().resetMana({}, {}, {}, {}); useCombatStore.getState().resetCombat(startFloor); diff --git a/src/lib/game/stores/gameStore.ts b/src/lib/game/stores/gameStore.ts index c4775f4..6f3710e 100755 --- a/src/lib/game/stores/gameStore.ts +++ b/src/lib/game/stores/gameStore.ts @@ -206,7 +206,6 @@ export const useGameStore = create()( // Combat - delegate to combatStore if (combatState.currentAction === 'climb') { const combatResult = useCombatStore.getState().processCombatTick( - {}, rawMana, elements, maxMana, diff --git a/src/lib/game/stores/uiStore.ts b/src/lib/game/stores/uiStore.ts index dc4a3bd..5be6aea 100755 --- a/src/lib/game/stores/uiStore.ts +++ b/src/lib/game/stores/uiStore.ts @@ -13,7 +13,7 @@ export interface UIState { paused: boolean; gameOver: boolean; victory: boolean; - + // Actions addLog: (message: string) => void; clearLogs: () => void; @@ -21,7 +21,6 @@ export interface UIState { setPaused: (paused: boolean) => void; setGameOver: (gameOver: boolean, victory?: boolean) => void; reset: () => void; - resetUI: () => void; } const MAX_LOGS = 50; @@ -31,29 +30,29 @@ export const useUIStore = create((set) => ({ paused: false, gameOver: false, victory: false, - + addLog: (message: string) => { set((state) => ({ logs: [message, ...state.logs.slice(0, MAX_LOGS - 1)], })); }, - + clearLogs: () => { set({ logs: [] }); }, - + togglePause: () => { set((state) => ({ paused: !state.paused })); }, - + setPaused: (paused: boolean) => { set({ paused }); }, - + setGameOver: (gameOver: boolean, victory: boolean = false) => { set({ gameOver, victory }); }, - + reset: () => { set({ logs: ['✨ The loop begins. You start with Mana Bolt. Gather your strength, mage.'], @@ -62,13 +61,4 @@ export const useUIStore = create((set) => ({ victory: false, }); }, - - resetUI: () => { - set({ - logs: ['✨ The loop begins. You start with Mana Bolt. Gather your strength, mage.'], - paused: false, - gameOver: false, - victory: false, - }); - }, })); diff --git a/src/lib/game/types/elements.ts b/src/lib/game/types/elements.ts index ba917a8..2c1b279 100644 --- a/src/lib/game/types/elements.ts +++ b/src/lib/game/types/elements.ts @@ -2,6 +2,8 @@ export type ElementCategory = 'base' | 'utility' | 'composite' | 'exotic'; +export type ManaType = 'raw' | 'fire' | 'water' | 'air' | 'earth' | 'light' | 'dark' | 'death' | 'transference' | 'metal' | 'sand' | 'lightning' | 'crystal' | 'stellar' | 'void'; + export interface ElementDef { name: string; sym: string; diff --git a/src/lib/game/types/game.ts b/src/lib/game/types/game.ts index 38556b3..94c6572 100644 --- a/src/lib/game/types/game.ts +++ b/src/lib/game/types/game.ts @@ -43,7 +43,6 @@ export interface EnemyState { maxHP: number; armor: number; // Damage reduction (0-1) dodgeChance: number; // For speed rooms (0-1) - healthRegen?: number; // HP regenerated per tick (0-1 as percentage of max HP) barrier?: number; // Shield that absorbs damage before HP (0-1 as percentage of max HP) element: string; } @@ -262,6 +261,24 @@ export interface GameState { // ─── Action Types for Store ───────────────────────────────────────────── +export interface PrestigeDef { + name: string; + desc: string; + max: number; + cost: number; +} + +export interface LootDrop { + id: string; + name: string; + rarity: string; + type: string; + minFloor: number; + dropChance: number; + guardianOnly?: boolean; + amount?: { min: number; max: number }; +} + export type GameActionType = | { type: 'TICK' } | { type: 'GATHER_MANA' } diff --git a/src/lib/game/types/index.ts b/src/lib/game/types/index.ts index e9da396..c188794 100644 --- a/src/lib/game/types/index.ts +++ b/src/lib/game/types/index.ts @@ -3,7 +3,7 @@ // Re-export all types from domain-specific files // Element types -export type { ElementCategory, ElementDef, ElementState } from './elements'; +export type { ElementCategory, ElementDef, ElementState, ManaType } from './elements'; // Attunement types export type { AttunementSlot, AttunementDef, AttunementState, GuardianBoon, GuardianDef } from './attunements'; @@ -46,4 +46,6 @@ export type { GameActionType, ActivityEventType, ActivityLogEntry, + PrestigeDef, + LootDrop, } from './game'; diff --git a/src/lib/game/types/spells.ts b/src/lib/game/types/spells.ts index d7ec35d..080d1c7 100644 --- a/src/lib/game/types/spells.ts +++ b/src/lib/game/types/spells.ts @@ -21,6 +21,7 @@ export interface SpellDef { isAoe?: boolean; // AOE spell that hits multiple enemies aoeTargets?: number; // Number of enemies hit by AOE isWeaponEnchant?: boolean; // Can be used as weapon enchantment (magic swords) + grimoire?: boolean; // Whether this spell appears in the grimoire } export interface SpellEffect { diff --git a/src/lib/game/utils/room-utils.ts b/src/lib/game/utils/room-utils.ts index 7c434ef..5609510 100644 --- a/src/lib/game/utils/room-utils.ts +++ b/src/lib/game/utils/room-utils.ts @@ -61,20 +61,6 @@ export function getDodgeChance(floor: number): number { ); } -// Get health regen for an enemy (0-1 as percentage of max HP per tick) -export function getEnemyHealthRegen(floor: number, element: string): number { - // Higher floors have a chance for enemies with health regen - if (floor < 15) return 0; - - // Health regen becomes more common on higher floors - const regenChance = Math.min(0.3, (floor - 15) * 0.005); // Max 30% chance - if (Math.random() > regenChance) return 0; - - // Scale regen with floor (0.5% to 3% of max HP per tick) - const floorProgress = Math.min(1, (floor - 15) / 85); - return 0.005 + floorProgress * 0.025; -} - // Get barrier for an enemy (0-1 as percentage of max HP) export function getEnemyBarrier(floor: number, element: string): number { // Barrier appears on higher floors, more common with certain elements @@ -110,7 +96,6 @@ export function generateSwarmEnemies(floor: number): EnemyState[] { maxHP: Math.floor(baseHP * SWARM_CONFIG.hpMultiplier), armor: SWARM_CONFIG.armorBase + Math.floor(floor / 10) * SWARM_CONFIG.armorPerFloor, dodgeChance: 0, - healthRegen: getEnemyHealthRegen(floor, element), barrier: getEnemyBarrier(floor, element), element, }); @@ -136,7 +121,6 @@ export function generateFloorState(floor: number): FloorState { maxHP: guardian.hp, armor: guardian.armor || 0, dodgeChance: 0, - healthRegen: 0.01, // Guardians have 1% HP regen per tick barrier: 0, element: guardian.element, }], @@ -159,7 +143,6 @@ export function generateFloorState(floor: number): FloorState { maxHP: baseHP, armor: getFloorArmor(floor), dodgeChance: getDodgeChance(floor), - healthRegen: getEnemyHealthRegen(floor, element), barrier: getEnemyBarrier(floor, element), element, }], @@ -192,7 +175,6 @@ export function generateFloorState(floor: number): FloorState { maxHP: baseHP, armor: getFloorArmor(floor), dodgeChance: 0, - healthRegen: getEnemyHealthRegen(floor, element), barrier: getEnemyBarrier(floor, element), element, }],