feat: add prestige system and skill upgrades with comprehensive documentation
Build and Publish Mana Loop Docker Image / build-and-publish (push) Failing after 5m57s
Build and Publish Mana Loop Docker Image / build-and-publish (push) Failing after 5m57s
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
# Context: src/app/page.tsx
|
||||
|
||||
## Total Line Count
|
||||
492 lines
|
||||
|
||||
## Top-Level Exports
|
||||
|
||||
### 1. `ManaLoopGame` (default export)
|
||||
- **Line Range:** 45–485
|
||||
- **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:** 42–43
|
||||
- **Description:** A simple loading placeholder component shown while lazy-loaded tab components are being fetched.
|
||||
|
||||
### 3. `canCastSpell` (inline helper)
|
||||
- **Line Range:** 141–144
|
||||
- **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.
|
||||
@@ -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` | ~44–47 | `interface` | Props interface for the SkillsTab component, containing the `store` (GameStore). |
|
||||
| `hasMilestoneUpgrade` | ~50–81 | `function` | Determines whether a skill has milestone upgrades (level 5/10) available given current level, tiers, and upgrades; returns milestone info or null. |
|
||||
| `SkillsTab` | ~83–398 | `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 ~50–81)
|
||||
- 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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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`
|
||||
Reference in New Issue
Block a user