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
+312
View File
@@ -0,0 +1,312 @@
# 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`)
```typescript
export interface PrestigeDef {
name: string;
desc: string;
max: number;
cost: number;
}
```
### Current Prestige Upgrades (relevant to mana/elements)
```typescript
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`
```typescript
export interface ElementState {
current: number;
max: number;
unlocked: boolean;
}
```
### Elements Storage in GameState
**File:** `src/lib/game/types/game.ts`
```typescript
export interface GameState {
// ...
elements: Record<string, ElementState>;
// ...
}
```
### Base Unlocked Elements
**File:** `src/lib/game/constants/elements.ts`
```typescript
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)
```typescript
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)
```typescript
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)
```typescript
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)
### GameState Prestige-Related Fields
**File:** `src/lib/game/types/game.ts`
```typescript
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)
```typescript
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)
```typescript
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