Files
Mana-Loop/src/lib/game/stores/attunementStore.ts
T
n8n-gitea 573130cdb1
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m24s
fix: attunement system spec-vs-code discrepancies (issue #331)
- Fix conversion rate level scaling from linear (1+level*0.5) to exponential (1.5^(level-1)) in conversion-rates.ts
- Fix getAttunementLevelMultiplier formula to match spec §4.3
- Add level-up logging in attunementStore.ts via combat store addActivityLog
- Clarify getAttunementConversionRate returns flat base rate (level scaling applied separately)
- Update spec §8 to describe time-based puzzle room system matching code implementation
- Add 17 regression tests verifying exponential scaling, base rate behavior, and spec table values
2026-06-08 22:08:17 +02:00

111 lines
3.1 KiB
TypeScript

// Attunement Store
// Handles attunements, XP, leveling, and related actions
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
import { createSafeStorage } from '../utils/safe-persist';
import type { AttunementState } from '../types';
import { getAttunementXPForLevel, MAX_ATTUNEMENT_LEVEL, ATTUNEMENTS_DEF } from '../data/attunements';
import { useCombatStore } from './combatStore';
export interface AttunementStoreState {
attunements: Record<string, AttunementState>;
// Actions
addAttunementXP: (attunementId: string, amount: number) => void;
debugUnlockAttunement: (attunementId: string) => void;
setAttunements: (attunements: Record<string, AttunementState>) => void;
// Reset
resetAttunements: () => void;
}
const initialState = {
attunements: {
enchanter: { id: 'enchanter', active: true, level: 1, experience: 0 } as AttunementState,
},
};
export const useAttunementStore = create<AttunementStoreState>()(
persist(
(set, _get) => ({
...initialState,
addAttunementXP: (attunementId: string, amount: number) => {
set((state) => {
const attState = state.attunements?.[attunementId];
if (!attState) return state;
let newXP = attState.experience + amount;
let newLevel = attState.level;
// Level up if enough XP
while (newLevel < MAX_ATTUNEMENT_LEVEL) {
const xpNeeded = getAttunementXPForLevel(newLevel + 1);
if (newXP >= xpNeeded) {
newXP -= xpNeeded;
newLevel++;
} else {
break;
}
}
// Log level-up (outside set to avoid nested state updates)
if (newLevel > attState.level) {
const def = ATTUNEMENTS_DEF[attunementId];
const name = def?.name || attunementId;
// Use setTimeout to log after state update completes
setTimeout(() => {
useCombatStore.getState().addActivityLog(
'attunement',
`${name} reached level ${newLevel}!`
);
}, 0);
}
return {
attunements: {
...state.attunements,
[attunementId]: {
...attState,
level: newLevel,
experience: newXP,
},
},
};
});
},
debugUnlockAttunement: (attunementId: string) => {
set((state) => ({
attunements: {
...state.attunements,
[attunementId]: {
id: attunementId,
active: true,
level: 1,
experience: 0,
} as AttunementState,
},
}));
},
setAttunements: (attunements: Record<string, AttunementState>) => {
set({ attunements });
},
resetAttunements: () => {
set(initialState);
},
}),
{
storage: createSafeStorage(),
name: 'mana-loop-attunements',
version: 1,
partialize: (state) => ({
attunements: state.attunements,
}),
}
)
);