From ae7fb8f6fc34608fdc11aca6a3bcd7cc934ca805 Mon Sep 17 00:00:00 2001 From: zhipu Date: Fri, 27 Mar 2026 17:05:04 +0000 Subject: [PATCH] feat: Add Attunements tab UI and filter skills by attunement access - Create AttunementsTab component with visual display of all 3 attunements - Show primary mana type, regen stats, and capabilities for each attunement - Add visual effects for active attunements (color, glow) - Filter skill categories based on active attunements in SkillsTab - Add attunement tab navigation in main page - Display available skill categories summary in AttunementsTab --- src/app/page.tsx | 9 +- src/components/game/tabs/AttunementsTab.tsx | 235 ++++++++++++++++++++ src/components/game/tabs/SkillsTab.tsx | 18 +- src/components/game/tabs/index.ts | 1 + worklog.md | 28 +++ 5 files changed, 284 insertions(+), 7 deletions(-) create mode 100644 src/components/game/tabs/AttunementsTab.tsx diff --git a/src/app/page.tsx b/src/app/page.tsx index 37ddc89..c2fd217 100755 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -12,7 +12,7 @@ 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 { RotateCcw } from 'lucide-react'; -import { CraftingTab, SpireTab, SpellsTab, LabTab, SkillsTab, StatsTab, EquipmentTab } from '@/components/game/tabs'; +import { CraftingTab, SpireTab, SpellsTab, LabTab, SkillsTab, StatsTab, EquipmentTab, AttunementsTab } from '@/components/game/tabs'; import { ComboMeter, ActionButtons, CalendarDisplay, ManaDisplay, TimeDisplay } from '@/components/game'; import { LootInventoryDisplay } from '@/components/game/LootInventory'; import { AchievementsDisplay } from '@/components/game/AchievementsDisplay'; @@ -230,8 +230,9 @@ export default function ManaLoopGame() { ⚔️ Spire + ✨ Attune 📚 Skills - ✨ Spells + 🔮 Spells 🛡️ Gear 🔧 Craft 🔬 Lab @@ -243,6 +244,10 @@ export default function ManaLoopGame() { + + + + diff --git a/src/components/game/tabs/AttunementsTab.tsx b/src/components/game/tabs/AttunementsTab.tsx new file mode 100644 index 0000000..6797495 --- /dev/null +++ b/src/components/game/tabs/AttunementsTab.tsx @@ -0,0 +1,235 @@ +'use client'; + +import { ATTUNEMENTS_DEF, ATTUNEMENT_SLOT_NAMES, getTotalAttunementRegen, getAvailableSkillCategories } from '@/lib/game/data/attunements'; +import { ELEMENTS } from '@/lib/game/constants'; +import type { GameStore, AttunementState } from '@/lib/game/types'; +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'; +import { Lock, Sparkles } from 'lucide-react'; + +export interface AttunementsTabProps { + store: GameStore; +} + +export function AttunementsTab({ store }: AttunementsTabProps) { + const attunements = store.attunements || {}; + + // Get active attunements + const activeAttunements = Object.entries(attunements) + .filter(([, state]) => state.active) + .map(([id]) => ATTUNEMENTS_DEF[id]) + .filter(Boolean); + + // Calculate total regen from attunements + const totalAttunementRegen = getTotalAttunementRegen(attunements); + + // Get available skill categories + const availableCategories = getAvailableSkillCategories(attunements); + + return ( +
+ {/* Overview Card */} + + + Your Attunements + + +

+ Attunements are magical bonds tied to specific body locations. Each attunement grants unique capabilities, + mana regeneration, and access to specialized skills. +

+
+ + +{totalAttunementRegen.toFixed(1)} raw mana/hr + + + {activeAttunements.length} active attunement{activeAttunements.length !== 1 ? 's' : ''} + +
+
+
+ + {/* Attunement Slots */} +
+ {Object.entries(ATTUNEMENTS_DEF).map(([id, def]) => { + const state = attunements[id]; + const isActive = state?.active; + const isUnlocked = state?.active || def.unlocked; + + // Get primary mana element info + const primaryElem = def.primaryManaType ? ELEMENTS[def.primaryManaType] : null; + + // Get current mana for this attunement's type + const currentMana = def.primaryManaType ? store.elements[def.primaryManaType]?.current || 0 : 0; + const maxMana = def.primaryManaType ? store.elements[def.primaryManaType]?.max || 50 : 50; + + return ( + + +
+
+ {def.icon} +
+ + {def.name} + +
+ {ATTUNEMENT_SLOT_NAMES[def.slot]} +
+
+
+ {!isUnlocked && ( + + )} + {isActive && ( + + Active + + )} +
+
+ +

{def.desc}

+ + {/* Mana Type */} +
+
+ Primary Mana + {primaryElem ? ( + + {primaryElem.sym} {primaryElem.name} + + ) : ( + From Pacts + )} +
+ + {/* Mana bar (only for attunements with primary type) */} + {primaryElem && isActive && ( +
+ +
+ {currentMana.toFixed(1)} + /{maxMana} +
+
+ )} +
+ + {/* Stats */} +
+
+
Raw Regen
+
+{def.rawManaRegen}/hr
+
+
+
Conversion
+
+ {def.conversionRate > 0 ? `${def.conversionRate}/hr` : '—'} +
+
+
+ + {/* Capabilities */} +
+
Capabilities
+
+ {def.capabilities.map(cap => ( + + {cap === 'enchanting' && '✨ Enchanting'} + {cap === 'disenchanting' && '🔄 Disenchant'} + {cap === 'scrollCrafting' && '📜 Scrolls'} + {cap === 'pacts' && '🤝 Pacts'} + {cap === 'guardianPowers' && '💜 Guardian Powers'} + {cap === 'elementalMastery' && '🌟 Elem. Mastery'} + {cap === 'golemCrafting' && '🗿 Golems'} + {cap === 'gearCrafting' && '⚒️ Gear'} + {cap === 'earthShaping' && '⛰️ Earth Shaping'} + {!['enchanting', 'disenchanting', 'scrollCrafting', 'pacts', 'guardianPowers', + 'elementalMastery', 'golemCrafting', 'gearCrafting', 'earthShaping'].includes(cap) && cap} + + ))} +
+
+ + {/* Unlock condition for locked attunements */} + {!isUnlocked && def.unlockCondition && ( +
+ 🔒 {def.unlockCondition} +
+ )} + + {/* Level for unlocked attunements */} + {isUnlocked && state && ( +
+ Level {state.level} • {state.experience} XP +
+ )} +
+
+ ); + })} +
+ + {/* Available Skills Summary */} + + + Available Skill Categories + + +

+ Your attunements grant access to specialized skill categories: +

+
+ {availableCategories.map(cat => { + const attunement = Object.values(ATTUNEMENTS_DEF).find(a => + a.skillCategories.includes(cat) && attunements[a.id]?.active + ); + return ( + + {cat === 'mana' && '💧 Mana'} + {cat === 'study' && '📚 Study'} + {cat === 'research' && '🔮 Research'} + {cat === 'ascension' && '⭐ Ascension'} + {cat === 'enchant' && '✨ Enchanting'} + {cat === 'effectResearch' && '🔬 Effect Research'} + {cat === 'invocation' && '💜 Invocation'} + {cat === 'pact' && '🤝 Pact Mastery'} + {cat === 'fabrication' && '⚒️ Fabrication'} + {cat === 'golemancy' && '🗿 Golemancy'} + {!['mana', 'study', 'research', 'ascension', 'enchant', 'effectResearch', + 'invocation', 'pact', 'fabrication', 'golemancy'].includes(cat) && cat} + + ); + })} +
+
+
+
+ ); +} diff --git a/src/components/game/tabs/SkillsTab.tsx b/src/components/game/tabs/SkillsTab.tsx index ad86a25..24b038e 100644 --- a/src/components/game/tabs/SkillsTab.tsx +++ b/src/components/game/tabs/SkillsTab.tsx @@ -4,6 +4,7 @@ import { useState } from 'react'; import { SKILLS_DEF, SKILL_CATEGORIES, getStudySpeedMultiplier, getStudyCostMultiplier } from '@/lib/game/constants'; import { SKILL_EVOLUTION_PATHS, getUpgradesForSkillAtMilestone, getNextTierSkill, getTierMultiplier } from '@/lib/game/skill-evolution'; import { getUnifiedEffects, hasSpecial, SPECIAL_EFFECTS } from '@/lib/game/effects'; +import { getAvailableSkillCategories } from '@/lib/game/data/attunements'; import { fmt, fmtDec } from '@/lib/game/store'; import { formatStudyTime } from '@/lib/game/formatting'; import type { SkillUpgradeChoice, GameStore } from '@/lib/game/types'; @@ -127,11 +128,17 @@ export function SkillsTab({ store }: SkillsTabProps) { )} - {SKILL_CATEGORIES.map((cat) => { - const skillsInCat = Object.entries(SKILLS_DEF).filter(([, def]) => def.cat === cat.id); - if (skillsInCat.length === 0) return null; + {/* Get available skill categories based on attunements */} + {(() => { + const availableCategories = getAvailableSkillCategories(store.attunements || {}); - return ( + return SKILL_CATEGORIES + .filter(cat => availableCategories.includes(cat.id)) + .map((cat) => { + const skillsInCat = Object.entries(SKILLS_DEF).filter(([, def]) => def.cat === cat.id); + if (skillsInCat.length === 0) return null; + + return ( @@ -332,7 +339,8 @@ export function SkillsTab({ store }: SkillsTabProps) { ); - })} + }); + })()} ); } diff --git a/src/components/game/tabs/index.ts b/src/components/game/tabs/index.ts index bca3380..02e6281 100644 --- a/src/components/game/tabs/index.ts +++ b/src/components/game/tabs/index.ts @@ -8,3 +8,4 @@ export { LabTab } from './LabTab'; export { SkillsTab } from './SkillsTab'; export { StatsTab } from './StatsTab'; export { EquipmentTab } from './EquipmentTab'; +export { AttunementsTab } from './AttunementsTab'; diff --git a/worklog.md b/worklog.md index 798a625..f08e886 100755 --- a/worklog.md +++ b/worklog.md @@ -233,3 +233,31 @@ Stage Summary: - Attunements provide raw mana regen and convert to primary mana types - Skills are now organized by attunement (foundation for skill tab overhaul) - Lint checks pass, ready for UI implementation + +--- +Task ID: 10 +Agent: Main +Task: Implement Attunement System - UI Overhaul + +Work Log: +- **Created AttunementsTab component** (src/components/game/tabs/AttunementsTab.tsx): + - Displays all 3 attunements with their status (active/locked) + - Shows primary mana type and current mana for active attunements + - Displays raw mana regen and conversion rate stats + - Shows capabilities unlocked by each attunement + - Displays available skill categories based on active attunements + - Uses color coding and visual effects for active attunements +- **Updated page.tsx**: + - Added AttunementsTab import + - Added "✨ Attune" tab between Spire and Skills + - Added TabsContent for attunements +- **Updated SkillsTab.tsx**: + - Added import for getAvailableSkillCategories + - Modified skill rendering to filter categories by attunement access + - Skills now only show if the player has the appropriate attunement + +Stage Summary: +- New Attunements tab shows all attunement details +- Skills are filtered based on active attunements +- Player can see exactly which skill categories they have access to +- Visual feedback shows active vs locked attunements