Files
Mana-Loop/docs/task6/subtask_1_context.md
T
Refactoring Agent 03815f27ee
Build and Publish Mana Loop Docker Image / build-and-publish (push) Failing after 5m57s
feat: add prestige system and skill upgrades with comprehensive documentation
2026-05-01 15:18:09 +02:00

9.6 KiB
Raw Blame History

Task6-1 Context: Proposal 1 - Unlocked Mana Type Capacity

Overview

Gathered context for implementing Proposal 1: Unlocked Mana Type Capacity. This proposal likely relates to either:

  1. Increasing the mana capacity (max) of unlocked mana types
  2. Increasing the number of mana types that can be unlocked

1. Existing Prestige Upgrade Structure

File: src/lib/game/constants/prestige.ts

PrestigeDef Interface (from src/lib/game/types/skills.ts)

export interface PrestigeDef {
  name: string;
  desc: string;
  max: number;
  cost: number;
}

Current Prestige Upgrades (relevant to mana/elements)

export const PRESTIGE_DEF: Record<string, PrestigeDef> = {
  // ... other upgrades ...
  
  // Existing elemental capacity upgrade
  elementalAttune: { 
    name: "Elemental Attunement", 
    desc: "+25 elemental mana cap", 
    max: 10, 
    cost: 600 
  },
  
  // Starting elemental mana upgrade
  elemStart: { 
    name: "Elem. Start",   
    desc: "Start with 5 of each unlocked element", 
    max: 3, 
    cost: 800 
  },
  
  // ... other upgrades ...
};

Key observations:

  • All prestige upgrades use the same structure: name, description, max level, cost
  • elementalAttune already provides +25 elemental mana cap per level (max 10 levels = +250 cap)
  • elemStart gives starting elemental mana when a new loop begins
  • Upgrades are stored as Record<string, number> where the value is the current level

2. How Mana Type Unlocks Are Tracked

Element State Structure

File: src/lib/game/types/elements.ts

export interface ElementState {
  current: number;
  max: number;
  unlocked: boolean;
}

Elements Storage in GameState

File: src/lib/game/types/game.ts

export interface GameState {
  // ...
  elements: Record<string, ElementState>;
  // ...
}

Base Unlocked Elements

File: src/lib/game/constants/elements.ts

export const BASE_UNLOCKED_ELEMENTS = ['transference'];

Key observations:

  • Only transference starts unlocked by default
  • All other 12 elements (fire, water, air, earth, light, dark, death, metal, sand, lightning, crystal, stellar, void) start locked
  • Elements have a boolean unlocked field - no partial unlocking or limits on number of unlocked types

Unlocking New Elements

File: src/lib/game/store.ts (lines 2176-2195)

unlockElement: (element: string) => {
  const state = get();
  if (state.elements[element]?.unlocked) return;

  const cost = 500;
  if (state.rawMana < cost) return;

  // ELEMENTAL_AFFINITY: New elements start with 10 capacity
  const effects = getUnifiedEffects(state.skillUpgrades, state.skillTiers);
  const newElementMax = hasSpecial(effects, SPECIAL_EFFECTS.ELEMENTAL_AFFINITY) ? 10 : 0;

  set({
    rawMana: state.rawMana - cost,
    elements: {
      ...state.elements,
      [element]: { ...state.elements[element], unlocked: true, max: newElementMax },
    },
    log: [`✨ ${ELEMENTS[element].name} affinity unlocked!`, ...state.log.slice(0, 49)],
  });
},

Key observations:

  • Unlocking an element costs 500 raw mana
  • When unlocked, the element gets unlocked: true
  • New elements start with max: 0 unless ELEMENTAL_AFFINITY special effect is active (then max: 10)
  • No limit on the NUMBER of elements that can be unlocked - players can unlock all 12+ types

Element Definitions

File: src/lib/game/constants/elements.ts

Total element types:

  • Base (7): fire, water, air, earth, light, dark, death
  • Utility (1): transference
  • Composite (3): metal, sand, lightning
  • Exotic (3): crystal, stellar, void

Total: 14 element types (13 unlockable + transference which starts unlocked)


3. How Mana Capacity Is Applied

computeElementMax Function

File: src/lib/game/store.ts (lines 415-432)

export function computeElementMax(
  state: Pick<GameState, 'skills' | 'prestigeUpgrades' | 'skillUpgrades' | 'skillTiers'>,
  effects?: ComputedEffects | UnifiedEffects,
  element?: string
): number {
  const pu = state.prestigeUpgrades;
  const base = 10 + (state.skills.elemAttune || 0) * 50 + (pu.elementalAttune || 0) * 25;
  
  // Apply upgrade effects if provided
  if (effects) {
    let bonus = effects.elementCapBonus; // Global bonus
    
    // Add per-element bonus if element is specified and available
    if (element && (effects as UnifiedEffects).perElementCapBonus) {
      const perElementBonus = (effects as UnifiedEffects).perElementCapBonus[element];
      if (perElementBonus) {
        bonus += perElementBonus;
      }
    }
    
    return Math.floor((base + bonus) * effects.elementCapMultiplier);
  }
  return base;
}

Key observations:

  • Base capacity formula: 10 + (elemAttune skill levels * 50) + (elementalAttune prestige levels * 25)
  • Effects system can modify capacity via:
    • elementCapBonus: Global flat bonus
    • elementCapMultiplier: Global multiplier
    • perElementCapBonus[element]: Per-element flat bonus (from equipment)
  • Currently, all unlocked elements share the SAME max capacity (calculated once and applied to all)
  • The perElementCapBonus exists but is currently only used for equipment effects

Usage in Store Initialization

File: src/lib/game/store.ts (lines 670-710)

function makeInitial(overrides: Partial<GameState> = {}): GameState {
  const pu = overrides.prestigeUpgrades || {};
  // ...
  const elemMax = computeElementMax(
    { skills: overrides.skills || {}, prestigeUpgrades: pu, skillUpgrades: overrides.skillUpgrades, skillTiers: overrides.skillTiers }, 
    effects
  );
  
  const elements: Record<string, { current: number; max: number; unlocked: boolean }> = {};
  Object.keys(ELEMENTS).forEach((k) => {
    const isUnlocked = BASE_UNLOCKED_ELEMENTS.includes(k);
    let startAmount = 0;
    
    // Start with some elemental mana if elemStart upgrade
    if (isUnlocked && pu.elemStart) {
      startAmount = pu.elemStart * 5;
    }
    
    elements[k] = {
      current: overrides.elements?.[k]?.current ?? startAmount,
      max: elemMax,  // <-- Same max for ALL elements
      unlocked: isUnlocked,
    };
  });
  // ...
}

Key observation: The same elemMax is applied to ALL elements regardless of type or unlock status.


4. Save Data Structure (Cross-Loop Persistence)

File: src/lib/game/types/game.ts

export interface GameState {
  // ...
  
  // Prestige
  insight: number;
  totalInsight: number;
  prestigeUpgrades: Record<string, number>;
  memorySlots: number;
  memories: string[];
  
  // Elements
  elements: Record<string, ElementState>;
  
  // ...
}

What Persists Through Loops

File: src/lib/game/store.ts (lines 2255-2290)

startNewLoop: () => {
  const state = get();
  const insightGained = state.loopInsight || calcInsight(state);
  const total = state.insight + insightGained;
  
  // ... (keep some spells through temporal memory)
  
  const newState = makeInitial({
    loopCount: state.loopCount + 1,
    insight: total,
    totalInsight: (state.totalInsight || 0) + insightGained,
    prestigeUpgrades: state.prestigeUpgrades,  // <-- PERSISTS
    memories: state.memories,
    skills: state.skills,
    manaHeartBonus: newHeartBonus,
  });
  
  // ...
  set(newState);
},

Key observations:

  • prestigeUpgrades (all upgrade levels) persist through loops
  • insight and totalInsight persist and accumulate
  • elements are NOT persisted - they are reinitialized in makeInitial()
  • On new loop, element unlocks are lost (player must re-unlock elements)
  • BASE_UNLOCKED_ELEMENTS and pu.elemStart determine which elements start unlocked/have starting mana

Purchasing Prestige Upgrades

File: src/lib/game/store.ts (lines 2235-2250)

doPrestige: (id: string) => {
  // ...
  const lvl = state.prestigeUpgrades[id] || 0;
  if (lvl >= pd.max || state.insight < pd.cost) return;

  const newPU = { ...state.prestigeUpgrades, [id]: lvl + 1 };
  set({
    insight: state.insight - pd.cost,
    prestigeUpgrades: newPU,
    // ...
  });
},

Key observations:

  • Upgrades purchased with insight currency
  • Each level costs the same amount (flat cost model)
  • Max level check prevents over-purchasing
  • New upgrade level is saved to prestigeUpgrades record

Summary of Key Points for Task6-1

  1. Prestige upgrades follow a simple structure: { name, desc, max, cost }
  2. Element capacity is currently global (same for all elements), calculated from:
    • Base: 10
    • Skill: elemAttune levels × 50
    • Prestige: elementalAttune levels × 25
    • Effects: bonuses and multipliers
  3. Element unlocks are per-type (boolean), cost 500 raw mana each
  4. No limit on number of unlocked element types currently exists
  5. Per-element capacity bonuses exist in effects system (perElementCapBonus) but aren't widely used
  6. Cross-loop persistence: prestige upgrades and insight persist; element unlocks do NOT persist
  7. 14 total element types available (13 unlockable + transference)

Questions for Implementation

Depending on what "Unlocked Mana Type Capacity" means:

If it means increasing capacity (max mana) of unlocked types:

  • Could add a new prestige upgrade similar to elementalAttune
  • Could modify the computeElementMax function
  • Could add per-element capacity tracking

If it means increasing the NUMBER of types that can be unlocked:

  • Need to add a limit/cap on unlocked types (currently unlimited)
  • Need to add a prestige upgrade to increase this limit
  • Need to modify unlockElement to check against the limit