Files
Mana-Loop/AGENTS.md
2026-03-28 06:07:39 +00:00

12 KiB
Executable File

Mana Loop - Project Architecture Guide

This document provides a comprehensive overview of the project architecture for AI agents working on this codebase.


🔑 Git Credentials (SAVE THESE)

Repository: git@gitea.tailf367e3.ts.net:Anexim/Mana-Loop.git

HTTPS URL with credentials:

https://zhipu:5LlnutmdsC2WirDwWgnZuRH7@gitea.tailf367e3.ts.net/Anexim/Mana-Loop.git

Credentials:

To configure git:

git config --global user.name "zhipu"
git config --global user.email "zhipu@local.local"

⚠️ MANDATORY GIT WORKFLOW - MUST BE FOLLOWED

Before starting ANY work, you MUST:

  1. Pull the latest changes:

    cd /home/z/my-project && git pull origin master
    
  2. Do your task - Make all necessary code changes

  3. Before finishing, commit and push:

    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()
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.

// 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:
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 }
}
  1. Add stat mapping in effects.ts (if new stat):
// In computeEquipmentEffects()
if (effect.stat === 'myNewStat') {
  bonuses.myNewStat = (bonuses.myNewStat || 0) + effect.value;
}
  1. Apply in game logic:
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):
// 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
      });
    },
  };
}
  1. Add to main store (store.ts):
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)