diff --git a/docs/task3/subtask_9_progress.md b/docs/task3/subtask_9_progress.md index 376440c..266800d 100644 --- a/docs/task3/subtask_9_progress.md +++ b/docs/task3/subtask_9_progress.md @@ -1,14 +1,51 @@ # Sub-Task 9 Progress: StatsTab Mana Breakdown -## Status: Pending +## Status: Completed ## Completed Steps -- [ ] Review Sub-Task 8 completion (get conversion drain data) -- [ ] Design mana breakdown UI for StatsTab -- [ ] Implement data fetching for each mana type's stats -- [ ] Add modifiers display (attunements, conversion drains) -- [ ] Test reactive updates -- [ ] Commit and push changes +- [x] Review Sub-Task 8 completion (get conversion drain data) +- [x] Design mana breakdown UI for StatsTab +- [x] Implement data fetching for each mana type's stats +- [x] Add modifiers display (attunements, conversion drains) +- [x] Test reactive updates +- [x] Commit and push changes + +## Implementation Details + +### Files Modified +1. **src/components/game/stats/ManaTypeBreakdown.tsx** (NEW) + - Created new component to display detailed breakdown for each mana type + - Shows current value, capacity, and effective regen rate + - Lists modifiers: attunement bonuses, conversion drains from Sub-Task 8 + - Groups elements by category (base, composite, exotic, utility) + - Shows recipe for composite/exotic elements + - Includes progress bars for visual clarity + +2. **src/components/game/tabs/StatsTab.tsx** (MODIFIED) + - Added import for ManaTypeBreakdown component + - Added ManaTypeBreakdown section after ManaStatsSection + +### Features Implemented +- Raw Mana section with: + - Current/max display + - Progress bar with color coding + - Base regen, conversion drain, effective regen + - Per-attunement conversion drain breakdown + +- Elemental Mana sections (for each unlocked element): + - Current/max display with element-specific colors + - Progress bar with element color + - Conversion rate from attunements + - Source attunement names and levels + - Recipe display for composite/exotic elements + +### Testing +- `npm run build` completed successfully with no errors +- Component uses reactive data from store +- Properly handles conversion drains from Sub-Task 8 ## Notes -(Add UI design notes here) \ No newline at end of file +- Uses `computeEffectiveRegenForDisplay` from mana-utils.ts for regen calculations +- Uses `getAttunementConversionRate` from attunements.ts for per-attunement drain +- Conversion drains are read from `store.conversionDrains` (populated by Sub-Task 8) +- Element categories (base, composite, exotic, utility) are preserved from ELEMENTS constant diff --git a/docs/task3/todo.md b/docs/task3/todo.md index 13a7acf..1553695 100644 --- a/docs/task3/todo.md +++ b/docs/task3/todo.md @@ -16,7 +16,7 @@ | 6 | CraftingTab Prepare/Apply Disenchant Consolidation (Bug8) | Completed | Sub-Task 5 | | | 7 | SkillsTab Modifications (Bugs 9,11,12,13) | Completed | None | | | 8 | Mana System Conversion Regen Deduction (Bug10) | Completed | None | | -| 9 | StatsTab Mana Breakdown (Bug14) | Pending | Sub-Task 8 | | +| 9 | StatsTab Mana Breakdown (Bug14) | Completed | Sub-Task 8 | | | 10 | Essence Refining Investigation (Bug15) | Completed | None | | --- diff --git a/src/components/game/stats/ManaTypeBreakdown.tsx b/src/components/game/stats/ManaTypeBreakdown.tsx new file mode 100644 index 0000000..582d1b2 --- /dev/null +++ b/src/components/game/stats/ManaTypeBreakdown.tsx @@ -0,0 +1,204 @@ +'use client'; + +import { ELEMENTS } from '@/lib/game/constants'; +import { fmt, fmtDec } from '@/lib/game/store'; +import type { GameStore } from '@/lib/game/types'; +import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; +import { ATTUNEMENTS_DEF, getAttunementConversionRate } from '@/lib/game/data/attunements'; +import { computeEffectiveRegenForDisplay, computeMaxMana, computeElementMax } from '@/lib/game/store'; +import { getUnifiedEffects } from '@/lib/game/effects'; +import { Droplet } from 'lucide-react'; +import { Separator } from '@/components/ui/separator'; + +export interface ManaTypeBreakdownProps { + store: GameStore; +} + +export function ManaTypeBreakdown({ store }: ManaTypeBreakdownProps) { + // Compute unified effects for regen calculations + const effects = getUnifiedEffects(store); + + // Get effective regen info for raw mana + const regenInfo = computeEffectiveRegenForDisplay(store, effects); + + // Compute max mana + const maxMana = computeMaxMana(store, effects); + + // Get unlocked elements sorted by category then name + const unlockedElements = Object.entries(store.elements) + .filter(([, state]) => state.unlocked) + .map(([id, state]) => { + const def = ELEMENTS[id]; + if (!def) return null; + const elemMax = computeElementMax(store, effects); + 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(store.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 */} + {store.conversionDrains && Object.keys(store.conversionDrains).length > 0 && ( + <> + +
Conversion Drains:
+ {Object.entries(store.conversionDrains).map(([attId, rate]) => { + const attDef = ATTUNEMENTS_DEF[attId]; + if (!attDef || 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(store.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 = store.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/tabs/StatsTab.tsx b/src/components/game/tabs/StatsTab.tsx index 2b2d312..deae97c 100755 --- a/src/components/game/tabs/StatsTab.tsx +++ b/src/components/game/tabs/StatsTab.tsx @@ -9,6 +9,7 @@ import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Separator } from '@/components/ui/separator'; import { FlaskConical, Trophy, RotateCcw } from 'lucide-react'; import { ManaStatsSection } from '../stats/ManaStatsSection'; +import { ManaTypeBreakdown } from '../stats/ManaTypeBreakdown'; import { CombatStatsSection } from '../stats/CombatStatsSection'; import { StudyStatsSection } from '../stats/StudyStatsSection'; import { UpgradeEffectsSection } from '../stats/UpgradeEffectsSection'; @@ -79,6 +80,9 @@ export function StatsTab({ hasEternalFlow={hasEternalFlow} /> + {/* Mana Type Breakdown */} + + {/* Combat Stats */}