Documentation: - Add comprehensive README.md with project overview - Update AGENTS.md with new file structure and slice pattern - Add AUDIT_REPORT.md documenting unimplemented effects Refactoring (page.tsx: 1695 → 434 lines, 74% reduction): - Extract SkillsTab.tsx component - Extract StatsTab.tsx component - Extract UpgradeDialog.tsx component - Move getDamageBreakdown and getTotalDPS to computed-stats.ts - Move ELEMENT_ICON_NAMES to constants.ts All lint checks pass, functionality preserved.
346 lines
12 KiB
Markdown
346 lines
12 KiB
Markdown
# Mana Loop - Project Architecture Guide
|
|
|
|
This document provides a comprehensive overview of the project architecture for AI agents working on this codebase.
|
|
|
|
---
|
|
|
|
## ⚠️ MANDATORY GIT WORKFLOW - MUST BE FOLLOWED
|
|
|
|
**Before starting ANY work, you MUST:**
|
|
|
|
1. **Pull the latest changes:**
|
|
```bash
|
|
cd /home/z/my-project && git pull origin master
|
|
```
|
|
|
|
2. **Do your task** - Make all necessary code changes
|
|
|
|
3. **Before finishing, commit and push:**
|
|
```bash
|
|
cd /home/z/my-project
|
|
git add -A
|
|
git commit -m "descriptive message about changes"
|
|
git push origin master
|
|
```
|
|
|
|
**This workflow is ENFORCED and NON-NEGOTIABLE.** Every agent session must:
|
|
- Start with `git pull`
|
|
- End with `git add`, `git commit`, `git push`
|
|
|
|
**Git Remote:** `git@gitea.tailf367e3.ts.net:Anexim/Mana-Loop.git`
|
|
|
|
---
|
|
|
|
## Project Overview
|
|
|
|
**Mana Loop** is an incremental/idle game built with:
|
|
- **Framework**: Next.js 16 with App Router
|
|
- **Language**: TypeScript 5
|
|
- **Styling**: Tailwind CSS 4 with shadcn/ui components
|
|
- **State Management**: Zustand with persist middleware
|
|
- **Database**: Prisma ORM with SQLite (for persistence features)
|
|
|
|
## Core Game Loop
|
|
|
|
1. **Mana Gathering**: Click or auto-generate mana over time
|
|
2. **Studying**: Spend mana to learn skills and spells
|
|
3. **Combat**: Climb the Spire, defeat guardians, sign pacts
|
|
4. **Crafting**: Enchant equipment with spell effects
|
|
5. **Prestige**: Reset progress for permanent bonuses (Insight)
|
|
|
|
## Directory Structure
|
|
|
|
```
|
|
src/
|
|
├── app/
|
|
│ ├── page.tsx # Main game UI (~1700 lines, single page application)
|
|
│ ├── layout.tsx # Root layout with providers
|
|
│ └── api/ # API routes (minimal use)
|
|
├── components/
|
|
│ ├── ui/ # shadcn/ui components (auto-generated)
|
|
│ └── game/
|
|
│ ├── index.ts # Barrel exports
|
|
│ ├── ActionButtons.tsx # Main action buttons (Meditate, Climb, Study, etc.)
|
|
│ ├── CalendarDisplay.tsx # Day calendar with incursion indicators
|
|
│ ├── CraftingProgress.tsx # Design/preparation/application progress bars
|
|
│ ├── StudyProgress.tsx # Current study progress with cancel button
|
|
│ ├── ManaDisplay.tsx # Mana/gathering section with progress bar
|
|
│ ├── TimeDisplay.tsx # Day/hour display with pause toggle
|
|
│ └── tabs/ # Tab-specific components
|
|
│ ├── index.ts # Tab component exports
|
|
│ ├── CraftingTab.tsx # Enchantment crafting UI
|
|
│ ├── LabTab.tsx # Skill upgrade and lab features
|
|
│ ├── SpellsTab.tsx # Spell management and equipment spells
|
|
│ └── SpireTab.tsx # Combat and spire climbing
|
|
└── lib/
|
|
├── game/
|
|
│ ├── store.ts # Zustand store (~1650 lines, main state + tick logic)
|
|
│ ├── computed-stats.ts # Computed stats functions (extracted utilities)
|
|
│ ├── navigation-slice.ts # Floor navigation actions (setClimbDirection, changeFloor)
|
|
│ ├── study-slice.ts # Study system actions (startStudying*, cancelStudy)
|
|
│ ├── crafting-slice.ts # Equipment/enchantment logic
|
|
│ ├── familiar-slice.ts # Familiar system actions
|
|
│ ├── effects.ts # Unified effect computation
|
|
│ ├── upgrade-effects.ts # Skill upgrade effect definitions
|
|
│ ├── constants.ts # Game definitions (spells, skills, etc.)
|
|
│ ├── skill-evolution.ts # Skill tier progression paths
|
|
│ ├── types.ts # TypeScript interfaces
|
|
│ ├── formatting.ts # Display formatters
|
|
│ ├── utils.ts # Utility functions
|
|
│ └── data/
|
|
│ ├── equipment.ts # Equipment type definitions
|
|
│ └── enchantment-effects.ts # Enchantment effect catalog
|
|
└── utils.ts # General utilities (cn function)
|
|
```
|
|
|
|
## Key Systems
|
|
|
|
### 1. State Management (`store.ts`)
|
|
|
|
The game uses a Zustand store organized with **slice pattern** for better maintainability:
|
|
|
|
#### Store Slices
|
|
- **Main Store** (`store.ts`): Core state, tick logic, and main actions
|
|
- **Navigation Slice** (`navigation-slice.ts`): Floor navigation (setClimbDirection, changeFloor)
|
|
- **Study Slice** (`study-slice.ts`): Study system (startStudyingSkill, startStudyingSpell, cancelStudy)
|
|
- **Crafting Slice** (`crafting-slice.ts`): Equipment/enchantment (createEquipmentInstance, startDesigningEnchantment)
|
|
- **Familiar Slice** (`familiar-slice.ts`): Familiar system (addFamiliar, removeFamiliar)
|
|
|
|
#### Computed Stats (`computed-stats.ts`)
|
|
Extracted utility functions for stat calculations:
|
|
- `computeMaxMana()`, `computeRegen()`, `computeEffectiveRegen()`
|
|
- `calcDamage()`, `calcInsight()`, `getElementalBonus()`
|
|
- `getFloorMaxHP()`, `getFloorElement()`, `getMeditationBonus()`
|
|
- `canAffordSpellCost()`, `deductSpellCost()`
|
|
|
|
```typescript
|
|
interface GameState {
|
|
// Time
|
|
day: number;
|
|
hour: number;
|
|
paused: boolean;
|
|
|
|
// Mana
|
|
rawMana: number;
|
|
elements: Record<string, ElementState>;
|
|
|
|
// Combat
|
|
currentFloor: number;
|
|
floorHP: number;
|
|
activeSpell: string;
|
|
castProgress: number;
|
|
|
|
// Progression
|
|
skills: Record<string, number>;
|
|
spells: Record<string, SpellState>;
|
|
skillUpgrades: Record<string, string[]>;
|
|
skillTiers: Record<string, number>;
|
|
|
|
// Equipment
|
|
equipmentInstances: Record<string, EquipmentInstance>;
|
|
equippedInstances: Record<string, string | null>;
|
|
enchantmentDesigns: EnchantmentDesign[];
|
|
|
|
// Prestige
|
|
insight: number;
|
|
prestigeUpgrades: Record<string, number>;
|
|
signedPacts: number[];
|
|
}
|
|
```
|
|
|
|
### 2. Effect System (`effects.ts`)
|
|
|
|
**CRITICAL**: All stat modifications flow through the unified effect system.
|
|
|
|
```typescript
|
|
// Effects come from two sources:
|
|
// 1. Skill Upgrades (milestone bonuses)
|
|
// 2. Equipment Enchantments (crafted bonuses)
|
|
|
|
getUnifiedEffects(state) => UnifiedEffects {
|
|
maxManaBonus, maxManaMultiplier,
|
|
regenBonus, regenMultiplier,
|
|
clickManaBonus, clickManaMultiplier,
|
|
baseDamageBonus, baseDamageMultiplier,
|
|
attackSpeedMultiplier,
|
|
critChanceBonus, critDamageMultiplier,
|
|
studySpeedMultiplier,
|
|
specials: Set<string>, // Special effect IDs
|
|
}
|
|
```
|
|
|
|
**When adding new stats**:
|
|
1. Add to `ComputedEffects` interface in `upgrade-effects.ts`
|
|
2. Add mapping in `computeEquipmentEffects()` in `effects.ts`
|
|
3. Apply in the relevant game logic (tick, damage calc, etc.)
|
|
|
|
### 3. Combat System
|
|
|
|
Combat uses a **cast speed** system:
|
|
- Each spell has `castSpeed` (casts per hour)
|
|
- Cast progress accumulates: `progress += castSpeed * attackSpeedMultiplier * HOURS_PER_TICK`
|
|
- When `progress >= 1`, spell is cast (cost deducted, damage dealt)
|
|
- DPS = `damagePerCast * castsPerSecond`
|
|
|
|
Damage calculation order:
|
|
1. Base spell damage
|
|
2. Skill bonuses (combatTrain, arcaneFury, etc.)
|
|
3. Upgrade effects (multipliers, bonuses)
|
|
4. Special effects (Overpower, Berserker, etc.)
|
|
5. Elemental modifiers (same element +25%, super effective +50%)
|
|
|
|
### 4. Crafting/Enchantment System
|
|
|
|
Three-stage process:
|
|
1. **Design**: Select effects, takes time based on complexity
|
|
2. **Prepare**: Pay mana to prepare equipment, takes time
|
|
3. **Apply**: Apply design to equipment, costs mana per hour
|
|
|
|
Equipment has **capacity** that limits total enchantment power.
|
|
|
|
### 5. Skill Evolution System
|
|
|
|
Skills have 5 tiers of evolution:
|
|
- At level 5: Choose 2 of 4 milestone upgrades
|
|
- At level 10: Choose 2 more upgrades, then tier up
|
|
- Each tier multiplies the skill's base effect by 10x
|
|
|
|
## Important Patterns
|
|
|
|
### Adding a New Effect
|
|
|
|
1. **Define in `enchantment-effects.ts`**:
|
|
```typescript
|
|
my_new_effect: {
|
|
id: 'my_new_effect',
|
|
name: 'Effect Name',
|
|
description: '+10% something',
|
|
category: 'combat',
|
|
baseCapacityCost: 30,
|
|
maxStacks: 3,
|
|
allowedEquipmentCategories: ['caster', 'hands'],
|
|
effect: { type: 'multiplier', stat: 'attackSpeed', value: 1.10 }
|
|
}
|
|
```
|
|
|
|
2. **Add stat mapping in `effects.ts`** (if new stat):
|
|
```typescript
|
|
// In computeEquipmentEffects()
|
|
if (effect.stat === 'myNewStat') {
|
|
bonuses.myNewStat = (bonuses.myNewStat || 0) + effect.value;
|
|
}
|
|
```
|
|
|
|
3. **Apply in game logic**:
|
|
```typescript
|
|
const effects = getUnifiedEffects(state);
|
|
damage *= effects.myNewStatMultiplier;
|
|
```
|
|
|
|
### Adding a New Skill
|
|
|
|
1. **Define in `constants.ts` SKILLS_DEF**
|
|
2. **Add evolution path in `skill-evolution.ts`**
|
|
3. **Add prerequisite checks in `store.ts`**
|
|
4. **Update UI in `page.tsx`**
|
|
|
|
### Adding a New Spell
|
|
|
|
1. **Define in `constants.ts` SPELLS_DEF**
|
|
2. **Add spell enchantment in `enchantment-effects.ts`**
|
|
3. **Add research skill in `constants.ts`**
|
|
4. **Map research to effect in `EFFECT_RESEARCH_MAPPING`**
|
|
|
|
## Common Pitfalls
|
|
|
|
1. **Forgetting to call `getUnifiedEffects()`**: Always use unified effects for stat calculations
|
|
2. **Direct stat modification**: Never modify stats directly; use effect system
|
|
3. **Missing tier multiplier**: Use `getTierMultiplier(skillId)` for tiered skills
|
|
4. **Ignoring special effects**: Check `hasSpecial(effects, SPECIAL_EFFECTS.X)` for special abilities
|
|
|
|
## Testing Guidelines
|
|
|
|
- Run `bun run lint` after changes
|
|
- Check dev server logs at `/home/z/my-project/dev.log`
|
|
- Test with fresh game state (clear localStorage)
|
|
|
|
## Slice Pattern for Store Organization
|
|
|
|
The store uses a **slice pattern** to organize related actions into separate files. This improves maintainability and makes the codebase more modular.
|
|
|
|
### Creating a New Slice
|
|
|
|
1. **Create the slice file** (e.g., `my-feature-slice.ts`):
|
|
```typescript
|
|
// Define the actions interface
|
|
export interface MyFeatureActions {
|
|
doSomething: (param: string) => void;
|
|
undoSomething: () => void;
|
|
}
|
|
|
|
// Create the slice factory
|
|
export function createMyFeatureSlice(
|
|
set: StoreApi<GameStore>['setState'],
|
|
get: StoreApi<GameStore>['getState']
|
|
): MyFeatureActions {
|
|
return {
|
|
doSomething: (param: string) => {
|
|
set((state) => {
|
|
// Update state
|
|
});
|
|
},
|
|
undoSomething: () => {
|
|
set((state) => {
|
|
// Update state
|
|
});
|
|
},
|
|
};
|
|
}
|
|
```
|
|
|
|
2. **Add to main store** (`store.ts`):
|
|
```typescript
|
|
import { createMyFeatureSlice, MyFeatureActions } from './my-feature-slice';
|
|
|
|
// Extend GameStore interface
|
|
interface GameStore extends GameState, MyFeatureActions, /* other slices */ {}
|
|
|
|
// Spread into store creation
|
|
const useGameStore = create<GameStore>()(
|
|
persist(
|
|
(set, get) => ({
|
|
...createMyFeatureSlice(set, get),
|
|
// other slices and state
|
|
}),
|
|
// persist config
|
|
)
|
|
);
|
|
```
|
|
|
|
### Existing Slices
|
|
|
|
| Slice | File | Purpose |
|
|
|-------|------|---------|
|
|
| Navigation | `navigation-slice.ts` | Floor navigation (setClimbDirection, changeFloor) |
|
|
| Study | `study-slice.ts` | Study system (startStudyingSkill, startStudyingSpell, cancelStudy) |
|
|
| Crafting | `crafting-slice.ts` | Equipment/enchantment (createEquipmentInstance, startDesigningEnchantment) |
|
|
| Familiar | `familiar-slice.ts` | Familiar system (addFamiliar, removeFamiliar) |
|
|
|
|
## File Size Guidelines
|
|
|
|
### Current File Sizes (After Refactoring)
|
|
| File | Lines | Notes |
|
|
|------|-------|-------|
|
|
| `store.ts` | ~1650 | Core state + tick logic (reduced from 2138, 23% reduction) |
|
|
| `page.tsx` | ~1695 | Main UI (reduced from 2554, 34% reduction) |
|
|
| `computed-stats.ts` | ~200 | Extracted utility functions |
|
|
| `navigation-slice.ts` | ~50 | Navigation actions |
|
|
| `study-slice.ts` | ~100 | Study system actions |
|
|
|
|
### Guidelines
|
|
- Keep `page.tsx` under 2000 lines by extracting to components (ActionButtons, ManaDisplay, etc.)
|
|
- Keep `store.ts` under 1800 lines by extracting to slices (navigation, study, crafting, familiar)
|
|
- Extract computed stats and utility functions to `computed-stats.ts` when >50 lines
|
|
- Use barrel exports (`index.ts`) for clean imports
|
|
- Follow the slice pattern for store organization (see below)
|