cba42e01ff
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m18s
- Delete store.ts (355 LOC monolithic store, zero imports) - Delete crafting-slice.ts (379 LOC legacy crafting module) - Inline createStartingEquipment() into craftingStore.ts - Remove legacy equipment/inventory fields from GameState - Remove EquipmentDef from game.ts imports (unused) - Fix duplicate EquipmentSpellState export in types.ts - Fix bluePrintId typo in craftingStore.ts - Update stores/index.ts to import CraftingState/CraftingActions from craftingStore.types - Update EquipmentTab.test.ts to test store state instead of deleted module - Clean up stale comments referencing crafting-slice.ts - Reduce TS errors from 83 to 72 by removing conflicting legacy types
172 lines
5.2 KiB
TypeScript
172 lines
5.2 KiB
TypeScript
// ─── Attunement System ──────────────────────────────────────────────────────
|
|
// Attunement system functions
|
|
|
|
import type { AttunementState } from './types';
|
|
import { calculateEnchantingXP, getAttunementXPForLevel, MAX_ATTUNEMENT_LEVEL } from './data/attunements';
|
|
|
|
// ─── Enchanter Attunement ──────────────────────────────────────────────────
|
|
|
|
export function initEnchanterAttunement(): AttunementState {
|
|
return {
|
|
id: 'enchanter',
|
|
active: true,
|
|
level: 1,
|
|
experience: 0,
|
|
title: 'Novice Enchanter',
|
|
};
|
|
}
|
|
|
|
export function isEnchanterActive(attunements: Record<string, AttunementState>): boolean {
|
|
return attunements.enchanter?.active === true;
|
|
}
|
|
|
|
// ─── XP Gain & Leveling ────────────────────────────────────────────────────
|
|
|
|
export function gainEnchantingXP(
|
|
attunements: Record<string, AttunementState>,
|
|
xpAmount: number
|
|
): {
|
|
attunements: Record<string, AttunementState>;
|
|
leveledUp: boolean;
|
|
previousLevel: number;
|
|
newLevel: number;
|
|
} {
|
|
if (!attunements?.enchanter?.active || xpAmount <= 0) {
|
|
return {
|
|
attunements,
|
|
leveledUp: false,
|
|
previousLevel: attunements.enchanter?.level || 0,
|
|
newLevel: attunements.enchanter?.level || 0,
|
|
};
|
|
}
|
|
|
|
const previousLevel = attunements.enchanter.level;
|
|
let newXP = attunements.enchanter.experience + xpAmount;
|
|
let newLevel = attunements.enchanter.level;
|
|
let leveledUp = false;
|
|
|
|
while (newLevel < MAX_ATTUNEMENT_LEVEL) {
|
|
const xpNeeded = getAttunementXPForLevel(newLevel + 1);
|
|
if (newXP >= xpNeeded) {
|
|
newXP -= xpNeeded;
|
|
newLevel++;
|
|
leveledUp = true;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
const updatedAttunements: Record<string, AttunementState> = {
|
|
...attunements,
|
|
enchanter: {
|
|
...attunements.enchanter,
|
|
level: newLevel,
|
|
experience: newXP,
|
|
},
|
|
};
|
|
|
|
return {
|
|
attunements: updatedAttunements,
|
|
leveledUp,
|
|
previousLevel,
|
|
newLevel,
|
|
};
|
|
}
|
|
|
|
export function getXpToNextLevel(currentLevel: number): number {
|
|
if (currentLevel >= MAX_ATTUNEMENT_LEVEL) return 0;
|
|
return getAttunementXPForLevel(currentLevel + 1);
|
|
}
|
|
|
|
export function getLevelProgress(attunements: Record<string, AttunementState>): {
|
|
currentLevel: number;
|
|
currentXP: number;
|
|
xpToNextLevel: number;
|
|
progressPercent: number;
|
|
maxLevel: number;
|
|
} {
|
|
const enchanter = attunements?.enchanter;
|
|
if (!enchanter?.active) {
|
|
return {
|
|
currentLevel: 0,
|
|
currentXP: 0,
|
|
xpToNextLevel: 0,
|
|
progressPercent: 0,
|
|
maxLevel: MAX_ATTUNEMENT_LEVEL,
|
|
};
|
|
}
|
|
|
|
const xpToNext = getXpToNextLevel(enchanter.level);
|
|
return {
|
|
currentLevel: enchanter.level,
|
|
currentXP: enchanter.experience,
|
|
xpToNextLevel: xpToNext,
|
|
progressPercent: xpToNext > 0 ? (enchanter.experience / xpToNext) * 100 : 100,
|
|
maxLevel: MAX_ATTUNEMENT_LEVEL,
|
|
};
|
|
}
|
|
|
|
export function calculateEnchantingXpFromDesignCapacity(capacityCost: number): number {
|
|
return calculateEnchantingXP(capacityCost);
|
|
}
|
|
|
|
export function calculateXpFromEquipmentInstance(
|
|
instance: { enchantments: Array<{ effectId: string; stacks: number; actualCost: number }> }
|
|
): number {
|
|
let totalXp = 0;
|
|
for (const ench of instance.enchantments) {
|
|
totalXp += calculateEnchantingXP(ench.actualCost);
|
|
}
|
|
return totalXp;
|
|
}
|
|
|
|
export function getEnchanterTitle(level: number): string {
|
|
if (level >= 10) return 'Grandmaster Enchanter';
|
|
if (level >= 8) return 'Master Enchanter';
|
|
if (level >= 6) return 'Expert Enchanter';
|
|
if (level >= 4) return 'Journeyman Enchanter';
|
|
if (level >= 2) return 'Apprentice Enchanter';
|
|
return 'Novice Enchanter';
|
|
}
|
|
|
|
export function updateEnchanterTitle(attunements: Record<string, AttunementState>): Record<string, AttunementState> {
|
|
if (!attunements.enchanter?.active) return attunements;
|
|
const title = getEnchanterTitle(attunements.enchanter.level);
|
|
return {
|
|
...attunements,
|
|
enchanter: { ...attunements.enchanter, title },
|
|
};
|
|
}
|
|
|
|
export function resetEnchanterExperience(attunements: Record<string, AttunementState>): Record<string, AttunementState> {
|
|
if (!attunements.enchanter) return attunements;
|
|
return {
|
|
...attunements,
|
|
enchanter: { ...attunements.enchanter, experience: 0, level: 1, title: 'Novice Enchanter' },
|
|
};
|
|
}
|
|
|
|
export function initAttunements(): Record<string, AttunementState> {
|
|
return { enchanter: initEnchanterAttunement() };
|
|
}
|
|
|
|
export function batchGainXP(
|
|
attunements: Record<string, AttunementState>,
|
|
xpAmounts: number[]
|
|
): { attunements: Record<string, AttunementState>; totalXP: number; totalLevelUps: number; finalLevel: number } {
|
|
let result = {
|
|
attunements,
|
|
totalXP: 0,
|
|
totalLevelUps: 0,
|
|
finalLevel: attunements.enchanter?.level || 1,
|
|
};
|
|
for (const xpAmount of xpAmounts) {
|
|
const gain = gainEnchantingXP(result.attunements, xpAmount);
|
|
result.attunements = gain.attunements;
|
|
result.totalXP += xpAmount;
|
|
if (gain.leveledUp) result.totalLevelUps++;
|
|
result.finalLevel = gain.newLevel;
|
|
}
|
|
return result;
|
|
}
|