feat: add prestige system and skill upgrades with comprehensive documentation
Build and Publish Mana Loop Docker Image / build-and-publish (push) Failing after 5m57s

This commit is contained in:
Refactoring Agent
2026-05-01 15:18:09 +02:00
parent 3691aa4acc
commit 03815f27ee
52 changed files with 4056 additions and 873 deletions
+55
View File
@@ -0,0 +1,55 @@
# Context: src/app/page.tsx
## Total Line Count
492 lines
## Top-Level Exports
### 1. `ManaLoopGame` (default export)
- **Line Range:** 45485
- **Description:** The main game component that renders the entire Mana Loop UI, manages tab state, gathering, spire mode, and orchestrates all game systems via the Zustand store.
### 2. `TabLoadingFallback`
- **Line Range:** 4243
- **Description:** A simple loading placeholder component shown while lazy-loaded tab components are being fetched.
### 3. `canCastSpell` (inline helper)
- **Line Range:** 141144
- **Description:** A closure defined inside `ManaLoopGame` that checks whether a given spell can be afforded with current mana and element resources.
## Imports from Other Files in the Repo (relative paths)
### From `@/lib/game/`
- `useGameStore`, `useGameLoop`, `fmt`, `getFloorElement`, `computeMaxMana`, `computeRegen`, `computeClickMana`, `getMeditationBonus`, `getIncursionStrength`, `canAffordSpellCost``@/lib/game/store`
- `ActivityLogEntry``@/lib/game/types`
- `getActiveEquipmentSpells`, `getTotalDPS``@/lib/game/computed-stats`
- `ELEMENTS`, `GUARDIANS`, `SPELLS_DEF`, `PRESTIGE_DEF`, `getStudySpeedMultiplier`, `getStudyCostMultiplier``@/lib/game/constants`
- `getUnifiedEffects`, `hasSpecial`, `SPECIAL_EFFECTS``@/lib/game/effects`
- `DebugName``@/lib/game/debug-context`
### From `@/components/`
- `Button``@/components/ui/button`
- `Tabs`, `TabsContent`, `TabsList`, `TabsTrigger``@/components/ui/tabs`
- `Card`, `CardContent`, `CardHeader`, `CardTitle``@/components/ui/card`
- `Badge``@/components/ui/badge`
- `ScrollArea``@/components/ui/scroll-area`
- `RotateCcw`, `Mountain`, `ChevronDown``lucide-react` (icon pack)
- `TooltipProvider``@/components/ui/tooltip`
- `ActionButtons`, `CalendarDisplay`, `ManaDisplay`, `TimeDisplay``@/components/game`
- Lazy-loaded tab components (all from `@/components/game/tabs`):
- `SpireTab`, `SkillsTab`, `SpellsTab`, `LabTab`, `StatsTab`, `EquipmentTab`, `AttunementsTab`, `DebugTab`, `LootTab`, `AchievementsTab`, `GolemancyTab`, `CraftingTab`
## Assessment: Which exports are safest to extract to a new file
### Safest to extract (stand-alone, reusable, low coupling):
1. **`TabLoadingFallback`** — A presentational component with zero dependencies on game state or side-effects. It could be moved to a shared UI file (e.g., `components/ui/loading.tsx`) with no behavioral impact.
2. **`canCastSpell`** — Although currently nested inside `ManaLoopGame`, it is a pure function of `(spellId, store)` (it reads `SPELLS_DEF` and `canAffordSpellCost`). It could be lifted to `@/lib/game/spells.ts` (or similar) and exported as a named helper. This would reduce closure complexity and make it easily testable.
### Moderate safety (would require small refactors but are useful to share):
- None identified beyond the above two — the only other export is the default `ManaLoopGame`, which is intentionally top-level and orchestrates too many concerns to extract as-is. Splitting it would require significant decomposition (e.g., extracting subcomponents, custom hooks, and game-logic helpers).
### Not recommended to extract as-is:
- **`ManaLoopGame`** — It is the root page component for `/` and tightly integrates routing-lite behavior (spire mode vs normal tabs), game-loop effects, state selectors, and presentation. Extracting it would require first breaking it into smaller pieces (state hooks, subcomponents) rather than moving it wholesale.
+62
View File
@@ -0,0 +1,62 @@
# SkillsTab.tsx — Context File
**File:** `src/components/game/tabs/SkillsTab.tsx`
**Total lines:** 400
---
## Top-level Exports
| Export | Line Range | Type | Description |
|--------|------------|------|-------------|
| `SkillsTabProps` | ~4447 | `interface` | Props interface for the SkillsTab component, containing the `store` (GameStore). |
| `hasMilestoneUpgrade` | ~5081 | `function` | Determines whether a skill has milestone upgrades (level 5/10) available given current level, tiers, and upgrades; returns milestone info or null. |
| `SkillsTab` | ~83398 | `function` (component) | Main SkillsTab component that renders skill categories/cards, study progress, upgrade dialogs, and handles study/upgrade flows using the store. |
---
## Imports from other files in the repo (relative paths only)
- `@/lib/game/constants``SKILLS_DEF`, `SKILL_CATEGORIES`, `getStudySpeedMultiplier`, `getStudyCostMultiplier`
- `@/lib/game/skill-evolution``SKILL_EVOLUTION_PATHS`, `getUpgradesForSkillAtMilestone`, `getNextTierSkill`, `getTierMultiplier`
- `@/lib/game/effects``getUnifiedEffects`
- `@/lib/game/data/attunements``getAvailableSkillCategories`
- `@/lib/game/store``fmt`, `fmtDec`
- `@/lib/game/types``SkillUpgradeChoice`, `GameStore`
- `@/components/ui/button``Button`
- `@/components/ui/card``Card`, `CardContent`, `CardHeader`, `CardTitle`
- `@/components/ui/badge``Badge`
- `@/components/ui/tooltip``Tooltip`, `TooltipContent`, `TooltipProvider`, `TooltipTrigger`
- `./StudyProgress``StudyProgress`
- `./UpgradeDialog``UpgradeDialog`
- `@/components/game/ConfirmDialog``ConfirmDialog`
- `@/components/game/GameToast``useGameToast`
- `@/lib/game/constants` (re-export) — `ELEMENTS`
- `lucide-react``ChevronDown`, `ChevronRight`
- `./SkillRow``SkillRow`
- `@/lib/game/hooks/useSkillUpgradeSelection``useSkillUpgradeSelection`
Note: The file also references a `SPECIAL_EFFECTS` constant in JSX (used in `canParallelStudy` logic) but it is not imported in this file — it may be missing or available from a global/ambient source.
---
## Assessment — Safest exports to extract to a new file
**Best candidates for extraction** (low coupling, high reusability, minimal dependencies on store/ui):
1. **`hasMilestoneUpgrade`** (lines ~5081)
- Pure-ish function that computes milestone eligibility from skill/tier/upgrade state.
- Depends only on `SKILL_EVOLUTION_PATHS`, `getUpgradesForSkillAtMilestone` and primitive arguments.
- No React or store coupling — safest to extract.
2. **`SkillsTabProps`** (interface)
- Type-only export; could be colocated with types or moved to a shared types file if desired.
- Zero runtime cost — safe but typically not worth extracting by itself unless consolidating interfaces.
**Less safe / more complex**:
- **`SkillsTab`** — deeply coupled to store, UI components, hooks, local dialog state, and many domain helpers. Extracting this would require pulling many dependencies and UI coordination; not recommended unless performing a major feature split.
- If extraction goal is to reduce file size, consider extracting smaller helpers used *inside* the component into modules (e.g., category filtering, tier/cost calculation helpers) but those are currently inline.
**Recommendation:**
Extract `hasMilestoneUpgrade` into a helper file (e.g., `src/lib/game/skill-milestones.ts` or similar) and move `SkillsTabProps` into a shared types file if consolidating. Leave `SkillsTab` in place.
+31
View File
@@ -0,0 +1,31 @@
# Context: upgrade-effects.ts
- Total line count: 191
## Top-Level Exports
1. **`getActiveUpgrades`** (lines ~28-51)
- Returns all selected upgrades with full effect definitions from the skill upgrades record
- Builds cache of upgrade definitions on first access, iterates over SKILL_EVOLUTION_PATHS
2. **`computeEffects`** (lines ~54-188)
- Computes all active effects from selected upgrades into a ComputedEffects object
- Applies multipliers, bonuses, and special effects from upgrades; handles DEEP_UNDERSTANDING and MANA_THRESHOLD
3. **`upgradeDefinitionsById`** (line ~15)
- Cache Map for quick lookup of upgrade definitions by ID
4. **`buildUpgradeCache`** (lines ~18-25)
- Initializes the upgrade definition cache from SKILL_EVOLUTION_PATHS
## Imports
- `SkillUpgradeChoice`, `SkillUpgradeEffect` from './types'
- `getUpgradesForSkillAtMilestone`, `SKILL_EVOLUTION_PATHS` from './skill-evolution'
- `ActiveUpgradeEffect`, `ComputedEffects` from './upgrade-effects.types'
- `SPECIAL_EFFECTS`, `hasSpecial` from './special-effects'
- `computeDynamicRegen`, `computeDynamicClickMana`, `computeDynamicDamage` from './dynamic-compute'
## Assessment
This file is already **191 lines** (well under 400). No refactoring needed. The exports are cleanly separated - `getActiveUpgrades` handles data gathering, `computeEffects` handles computation. The module is focused on a single concern (upgrade effects) and is not a candidate for splitting.
+49
View File
@@ -0,0 +1,49 @@
# Refactor Plan: page.tsx
## Current State
- **File:** `src/app/page.tsx`
- **Lines:** 650
- **Target:** ≤400 lines (reduce by ~250 lines)
## Proposed File Structure
### 1. `src/components/game/tabs/PrestigeTab.tsx` (NEW, ≈60 lines)
**Contains:**
- `PrestigeTab` component (extracted from `renderGrimoireTab`)
- Grimoire/Prestige tab UI
**Rationale:** Self-contained tab component; only depends on store, PRESTIGE_DEF, GUARDIANS.
### 2. `src/app/page.tsx` (≈300 lines)
**Keeps:**
- Main `ManaLoopGame` component shell
- Tabs definition and lazy loading
- Core game loop integration
- Most tab content (other tabs remain via lazy loading)
**Rationale:** Reduces main page by extracting only the Grimoire tab which is standalone.
## Circular Import Risks
**LOW RISK:**
- `PrestigeTab` depends on store, PRESTIGE_DEF, GUARDIANS - all stable dependencies.
- No circular dependency with `page.tsx`.
**MITIGATION:**
- Import store and constants normally in new component.
## Extraction Order
**1 → 2**
1. Create `PrestigeTab.tsx`, move `renderGrimoireTab` content, adapt to be standalone (receive store via hook internally), export component.
2. Update `page.tsx`: replace `renderGrimoireTab` call with `<PrestigeTab />`, remove extracted code.
3. Verify typecheck, verify ≤400 lines.
## Notes
This addresses the main line bloat by only ~60 lines (renderGrimoireTab). To get page.tsx under 400 lines fully would require more extensive splitting (extracting each tab into separate route files, extracting sidebar, etc.). The plan here is conservative - if page.tsx is still >400 after extracting PrestigeTab, we can consider:
- Extracting StatsTab/LabTab content further
- Extracting activity log rendering
- Extracting action buttons
But per phase instructions we must get ALL THREE target files under 400 lines, so we must be more aggressive if needed.
+65
View File
@@ -0,0 +1,65 @@
# Refactor Plan: SkillsTab.tsx
## Current State
- **File:** `src/components/game/tabs/SkillsTab.tsx`
- **Lines:** 434
- **Target:** ≤400 lines (reduce by ~34 lines)
## Proposed File Structure
### 1. `src/lib/game/utils.ts` (NEW, ≈20 lines)
**Contains:**
- `formatStudyTime` function
**Rationale:** Simple pure utility function, zero-risk extraction.
### 2. `src/components/game/tabs/SkillUpgradeDialog.tsx` (NEW, ≈80 lines)
**Contains:**
- `SkillUpgradeDialog` component (extracted from `renderUpgradeDialog`)
- Dialog UI and selection state handlers
**Rationale:** Isolates the upgrade dialog UI (~100 lines worth from parent), simplifies SkillsTab significantly. Uses callback props.
### 3. `src/components/game/SkillRow.tsx` (NEW, ≈100 lines)
**Contains:**
- `SkillRow` component for individual skill rows
- Level dots, buttons, study toggle, tier-up, milestone badges
**Rationale:** Encapsulates per-skill UI (~150 lines worth from parent), reusable, simplifies SkillsTab.
### 4. `src/lib/game/hooks/useSkillUpgradeSelection.ts` (NEW, ≈40 lines)
**Contains:**
- `useSkillUpgradeSelection` custom hook
- Selection state and mutation logic for milestone upgrades
**Rationale:** Encapsulates upgrade selection logic previously inline in SkillsTab.
### 5. `src/components/game/tabs/SkillsTab.tsx` (≈150 lines)
**Keeps:**
- Core `SkillsTab` component shell
- Category-level layout
- Main store hook calls
- Tab switching
**Rationale:** Maintains coordination role while delegating details to extracted components/hooks.
## Circular Import Risks
**MEDIUM RISK:**
- `SkillRow` needs access to many store selectors and actions. May accept callbacks as props to avoid direct store access (kept in parent).
- `SkillUpgradeDialog` needs data from store; will receive computed values as props.
- `useSkillUpgradeSelection` needs `SKILL_EVOLUTION_PATHS` and store commits - safe.
**MITIGATION:**
- Keep store interactions in `SkillsTab`, pass data down via props.
- Accept slight prop drilling vs. direct store access in extracted components for cleaner boundaries.
## Extraction Order
**1 → 4 → 3 → 2 → 5**
1. Create `utils.ts`, move `formatStudyTime`, update SkillsTab import.
2. Create `useSkillUpgradeSelection` hook, move selection logic, update SkillsTab.
3. Create `SkillRow`, move per-skill UI (pass callbacks from parent), update SkillsTab.
4. Create `SkillUpgradeDialog`, move dialog UI, update SkillsTab.
5. Delete old extracted sections from SkillsTab, verify ≤400 lines.
+69
View File
@@ -0,0 +1,69 @@
# Refactor Plan: upgrade-effects.ts
## Current State
- **File:** `src/lib/game/upgrade-effects.ts`
- **Lines:** 466
- **Target:** ≤400 lines (reduce by ~66 lines)
## Proposed File Structure
### 1. `src/lib/game/upgrade-effects.ts` (≈220 lines)
**Keeps:**
- `upgradeDefinitionsById` (cache)
- `buildUpgradeCache` function
- `getActiveUpgrades` function
- `computeEffects` function (core orchestrator)
- Evolution path dependency (`SKILL_EVOLUTION_PATHS`, `getUpgradesForSkillAtMilestone`)
**Rationale:** This remains the core orchestration module that ties together evolution paths with upgrade computation.
### 2. `src/lib/game/upgrade-effects.types.ts` (≈60 lines)
**Contains:**
- `ActiveUpgradeEffect` interface
- `ComputedEffects` interface
- Re-exports for type consumers
**Rationale:** Pure type definitions separated for clarity.
### 3. `src/lib/game/special-effects.ts` (≈80 lines)
**Contains:**
- `SPECIAL_EFFECTS` constant record
- `hasSpecial` function
**Rationale:** Isolates special effect keys and the simple predicate function.
### 4. `src/lib/game/dynamic-compute.ts` (≈100 lines)
**Contains:**
- `computeDynamicRegen` function
- `computeDynamicClickMana` function
- `computeDynamicDamage` function
**Rationale:** Groups the three dynamic computation functions that all depend on `SPECIAL_EFFECTS` and share similar patterns.
## Circular Import Risks
**LOW RISK:**
- `upgrade-effects.ts` depends on `skill-evolution` - one-way dependency.
- New files import types from `upgrade-effects.types.ts` and `special-effects.ts`.
- `dynamic-compute.ts` depends on `special-effects.ts` and types - safe.
**MITIGATION:**
- Keep type re-exports clean.
- If `computeEffects` needs dynamic functions, import them from `dynamic-compute.ts`.
## Extraction Order
**1 → 2 → 3 → 4**
1. Create `upgrade-effects.types.ts`, move type interfaces, update imports.
2. Create `special-effects.ts`, move `SPECIAL_EFFECTS` + `hasSpecial`, update imports.
3. Create `dynamic-compute.ts`, move the three `computeDynamic*` functions, update imports.
4. Trim `upgrade-effects.ts` - remove moved items, update internal imports.
## Import Updates Required
Files importing from `upgrade-effects.ts` need updates:
- Types → `upgrade-effects.types.ts`
- Special effects → `special-effects.ts`
- Dynamic compute → `dynamic-compute.ts`
- Core functions → `upgrade-effects.ts`