feat: implement attunement leveling and debug functions
All checks were successful
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 2m4s

- Add attunement XP system with level-scaled progression (100 * 3^(level-2) XP per level)
- Add addAttunementXP function with automatic level-up handling
- Add debug functions: debugUnlockAttunement, debugAddElementalMana, debugSetTime, debugAddAttunementXP, debugSetFloor
- Update attunement conversion to use level-scaled conversion rate
- Import getAttunementConversionRate, getAttunementXPForLevel, MAX_ATTUNEMENT_LEVEL in store
This commit is contained in:
2026-03-27 18:55:22 +00:00
parent a1b15cea74
commit 4748b81fe6

View File

@@ -39,7 +39,7 @@ import {
} from './crafting-slice'; } from './crafting-slice';
import { EQUIPMENT_TYPES } from './data/equipment'; import { EQUIPMENT_TYPES } from './data/equipment';
import { ENCHANTMENT_EFFECTS, calculateEffectCapacityCost } from './data/enchantment-effects'; import { ENCHANTMENT_EFFECTS, calculateEffectCapacityCost } from './data/enchantment-effects';
import { ATTUNEMENTS_DEF, getTotalAttunementRegen } from './data/attunements'; import { ATTUNEMENTS_DEF, getTotalAttunementRegen, getAttunementConversionRate, getAttunementXPForLevel, MAX_ATTUNEMENT_LEVEL } from './data/attunements';
// Default empty effects for when effects aren't provided // Default empty effects for when effects aren't provided
const DEFAULT_EFFECTS: ComputedEffects = { const DEFAULT_EFFECTS: ComputedEffects = {
@@ -568,6 +568,16 @@ interface GameStore extends GameState, CraftingActions {
commitSkillUpgrades: (skillId: string, upgradeIds: string[]) => void; commitSkillUpgrades: (skillId: string, upgradeIds: string[]) => void;
tierUpSkill: (skillId: string) => void; tierUpSkill: (skillId: string) => void;
// Attunement XP and leveling
addAttunementXP: (attunementId: string, amount: number) => void;
// Debug functions
debugUnlockAttunement: (attunementId: string) => void;
debugAddElementalMana: (element: string, amount: number) => void;
debugSetTime: (day: number, hour: number) => void;
debugAddAttunementXP: (attunementId: string, amount: number) => void;
debugSetFloor: (floor: number) => void;
// Computed getters // Computed getters
getMaxMana: () => number; getMaxMana: () => number;
getRegen: () => number; getRegen: () => number;
@@ -679,8 +689,11 @@ export const useGameStore = create<GameStore>()(
const elem = elements[attDef.primaryManaType]; const elem = elements[attDef.primaryManaType];
if (!elem || !elem.unlocked) return; if (!elem || !elem.unlocked) return;
// Get level-scaled conversion rate
const scaledConversionRate = getAttunementConversionRate(attId, attState.level || 1);
// Convert raw mana to primary type // Convert raw mana to primary type
const conversionAmount = attDef.conversionRate * HOURS_PER_TICK; const conversionAmount = scaledConversionRate * HOURS_PER_TICK;
const actualConversion = Math.min(conversionAmount, rawMana, elem.max - elem.current); const actualConversion = Math.min(conversionAmount, rawMana, elem.max - elem.current);
if (actualConversion > 0) { if (actualConversion > 0) {
@@ -1668,6 +1681,140 @@ export const useGameStore = create<GameStore>()(
if (!instance) return 0; if (!instance) return 0;
return instance.totalCapacity - instance.usedCapacity; return instance.totalCapacity - instance.usedCapacity;
}, },
// Attunement XP and leveling
addAttunementXP: (attunementId: string, amount: number) => {
const state = get();
const attState = state.attunements[attunementId];
if (!attState?.active) return;
let newXP = attState.experience + amount;
let newLevel = attState.level;
// Check for level ups
while (newLevel < MAX_ATTUNEMENT_LEVEL) {
const xpNeeded = getAttunementXPForLevel(newLevel + 1);
if (newXP >= xpNeeded) {
newXP -= xpNeeded;
newLevel++;
} else {
break;
}
}
// Cap XP at max level
if (newLevel >= MAX_ATTUNEMENT_LEVEL) {
newXP = 0;
}
set({
attunements: {
...state.attunements,
[attunementId]: {
...attState,
level: newLevel,
experience: newXP,
},
},
log: newLevel > attState.level
? [`🌟 ${attunementId} attunement reached Level ${newLevel}!`, ...state.log.slice(0, 49)]
: state.log,
});
},
// Debug functions
debugUnlockAttunement: (attunementId: string) => {
const state = get();
const def = ATTUNEMENTS_DEF[attunementId];
if (!def) return;
set({
attunements: {
...state.attunements,
[attunementId]: {
id: attunementId,
active: true,
level: 1,
experience: 0,
},
},
// Unlock the primary mana type if applicable
elements: def.primaryManaType && state.elements[def.primaryManaType]
? {
...state.elements,
[def.primaryManaType]: {
...state.elements[def.primaryManaType],
unlocked: true,
},
}
: state.elements,
log: [`🔓 Debug: Unlocked ${def.name} attunement!`, ...state.log.slice(0, 49)],
});
},
debugAddElementalMana: (element: string, amount: number) => {
const state = get();
const elem = state.elements[element];
if (!elem?.unlocked) return;
set({
elements: {
...state.elements,
[element]: {
...elem,
current: Math.min(elem.current + amount, elem.max * 10), // Allow overflow
},
},
});
},
debugSetTime: (day: number, hour: number) => {
set({
day,
hour,
incursionStrength: getIncursionStrength(day, hour),
});
},
debugAddAttunementXP: (attunementId: string, amount: number) => {
const state = get();
const attState = state.attunements[attunementId];
if (!attState) return;
let newXP = attState.experience + amount;
let newLevel = attState.level;
while (newLevel < MAX_ATTUNEMENT_LEVEL) {
const xpNeeded = getAttunementXPForLevel(newLevel + 1);
if (newXP >= xpNeeded) {
newXP -= xpNeeded;
newLevel++;
} else {
break;
}
}
set({
attunements: {
...state.attunements,
[attunementId]: {
...attState,
level: newLevel,
experience: newXP,
},
},
});
},
debugSetFloor: (floor: number) => {
const state = get();
set({
currentFloor: floor,
floorHP: getFloorMaxHP(floor),
floorMaxHP: getFloorMaxHP(floor),
maxFloorReached: Math.max(state.maxFloorReached, floor),
});
},
}), }),
{ {
name: 'mana-loop-storage', name: 'mana-loop-storage',