Remove all skill system files - preparing for fresh design phase
Build and Publish Mana Loop Docker Image / build-and-publish (push) Failing after 32s
Build and Publish Mana Loop Docker Image / build-and-publish (push) Failing after 32s
This commit is contained in:
@@ -1,343 +0,0 @@
|
||||
// ─── Skill Store ──────────────────────────────────────────────────────────────
|
||||
// Handles skills, upgrades, tiers, and study progress
|
||||
|
||||
import { create } from 'zustand';
|
||||
import { persist } from 'zustand/middleware';
|
||||
import { SKILLS_DEF, getStudySpeedMultiplier, getStudyCostMultiplier } from '../constants';
|
||||
import type { StudyTarget, SkillUpgradeChoice } from '../types';
|
||||
import { SKILL_EVOLUTION_PATHS, getBaseSkillId } from '../skill-evolution';
|
||||
import { useCombatStore } from './combatStore';
|
||||
import { useManaStore } from './manaStore';
|
||||
|
||||
export interface SkillState {
|
||||
// Skills
|
||||
skills: Record<string, number>;
|
||||
skillProgress: Record<string, number>; // Saved study progress for skills
|
||||
skillUpgrades: Record<string, string[]>; // Selected upgrade IDs per skill
|
||||
skillTiers: Record<string, number>; // Current tier for each base skill
|
||||
paidStudySkills: Record<string, number>; // skillId -> level that was paid for
|
||||
|
||||
// Study
|
||||
currentStudyTarget: StudyTarget | null;
|
||||
parallelStudyTarget: StudyTarget | null;
|
||||
|
||||
// Actions - Skills
|
||||
setSkillLevel: (skillId: string, level: number) => void;
|
||||
incrementSkillLevel: (skillId: string) => void;
|
||||
clearPaidStudySkill: (skillId: string) => void;
|
||||
setPaidStudySkill: (skillId: string, level: number) => void;
|
||||
|
||||
// Actions - Study
|
||||
startStudyingSkill: (skillId: string, rawMana: number) => { started: boolean; cost: number };
|
||||
startStudyingSpell: (spellId: string, rawMana: number, studyTime: number) => { started: boolean; cost: number };
|
||||
updateStudyProgress: (progressGain: number) => { completed: boolean; target: StudyTarget | null };
|
||||
cancelStudy: (retentionBonus: number) => void;
|
||||
setStudyTarget: (target: StudyTarget | null) => void;
|
||||
setCurrentStudyTarget: (target: StudyTarget | null) => void;
|
||||
|
||||
// Actions - Upgrades
|
||||
selectSkillUpgrade: (skillId: string, upgradeId: string) => void;
|
||||
deselectSkillUpgrade: (skillId: string, upgradeId: string) => void;
|
||||
commitSkillUpgrades: (skillId: string, upgradeIds: string[]) => void;
|
||||
tierUpSkill: (skillId: string) => void;
|
||||
|
||||
// Computed
|
||||
getSkillUpgradeChoices: (skillId: string, milestone: 5 | 10) => { available: SkillUpgradeChoice[]; selected: string[] };
|
||||
|
||||
// Reset
|
||||
resetSkills: (
|
||||
skills?: Record<string, number>,
|
||||
skillUpgrades?: Record<string, string[]>,
|
||||
skillTiers?: Record<string, number>
|
||||
) => void;
|
||||
}
|
||||
|
||||
export const useSkillStore = create<SkillState>()(
|
||||
persist(
|
||||
(set, get) => ({
|
||||
skills: {},
|
||||
skillProgress: {},
|
||||
skillUpgrades: {},
|
||||
skillTiers: {},
|
||||
paidStudySkills: {},
|
||||
currentStudyTarget: null,
|
||||
parallelStudyTarget: null,
|
||||
|
||||
setSkillLevel: (skillId: string, level: number) => {
|
||||
set((state) => ({
|
||||
skills: { ...state.skills, [skillId]: level },
|
||||
}));
|
||||
},
|
||||
|
||||
incrementSkillLevel: (skillId: string) => {
|
||||
set((state) => ({
|
||||
skills: { ...state.skills, [skillId]: (state.skills[skillId] || 0) + 1 },
|
||||
skillProgress: { ...state.skillProgress, [skillId]: 0 },
|
||||
}));
|
||||
},
|
||||
|
||||
clearPaidStudySkill: (skillId: string) => {
|
||||
set((state) => {
|
||||
const { [skillId]: _, ...remaining } = state.paidStudySkills;
|
||||
return { paidStudySkills: remaining };
|
||||
});
|
||||
},
|
||||
|
||||
setPaidStudySkill: (skillId: string, level: number) => {
|
||||
set((state) => ({
|
||||
paidStudySkills: { ...state.paidStudySkills, [skillId]: level },
|
||||
}));
|
||||
},
|
||||
|
||||
startStudyingSkill: (skillId: string, rawMana: number) => {
|
||||
const state = get();
|
||||
const sk = SKILLS_DEF[skillId];
|
||||
if (!sk) return { started: false, cost: 0 };
|
||||
|
||||
const currentLevel = state.skills[skillId] || 0;
|
||||
if (currentLevel >= sk.max) return { started: false, cost: 0 };
|
||||
|
||||
// Check prerequisites
|
||||
if (sk.req) {
|
||||
for (const [r, rl] of Object.entries(sk.req)) {
|
||||
if ((state.skills[r] || 0) < rl) return { started: false, cost: 0 };
|
||||
}
|
||||
}
|
||||
|
||||
// Check if already paid for this level
|
||||
const paidForLevel = state.paidStudySkills[skillId];
|
||||
const isAlreadyPaid = paidForLevel === currentLevel;
|
||||
|
||||
// Calculate cost
|
||||
const costMult = getStudyCostMultiplier(state.skills);
|
||||
const cost = Math.floor(sk.base * (currentLevel + 1) * costMult);
|
||||
|
||||
if (!isAlreadyPaid && rawMana < cost) return { started: false, cost };
|
||||
|
||||
// Get saved progress
|
||||
const savedProgress = state.skillProgress[skillId] || 0;
|
||||
|
||||
// Mark as paid (this is done here so resume works for free)
|
||||
const newPaidSkills = isAlreadyPaid
|
||||
? state.paidStudySkills
|
||||
: { ...state.paidStudySkills, [skillId]: currentLevel };
|
||||
|
||||
// Start studying
|
||||
set({
|
||||
paidStudySkills: newPaidSkills,
|
||||
currentStudyTarget: {
|
||||
type: 'skill',
|
||||
id: skillId,
|
||||
progress: savedProgress,
|
||||
required: sk.studyTime,
|
||||
},
|
||||
});
|
||||
|
||||
useCombatStore.getState().setAction('study');
|
||||
|
||||
if (!isAlreadyPaid && cost > 0) {
|
||||
useManaStore.getState().spendRawMana(cost);
|
||||
}
|
||||
|
||||
return { started: true, cost: isAlreadyPaid ? 0 : cost };
|
||||
},
|
||||
|
||||
startStudyingSpell: (spellId: string, rawMana: number, studyTime: number) => {
|
||||
const state = get();
|
||||
|
||||
// Start studying the spell
|
||||
set({
|
||||
currentStudyTarget: {
|
||||
type: 'spell',
|
||||
id: spellId,
|
||||
progress: 0,
|
||||
required: studyTime,
|
||||
},
|
||||
});
|
||||
|
||||
useCombatStore.getState().setAction('study');
|
||||
|
||||
// Spell study has no mana cost upfront - cost is paid via study time
|
||||
return { started: true, cost: 0 };
|
||||
},
|
||||
|
||||
updateStudyProgress: (progressGain: number) => {
|
||||
const state = get();
|
||||
if (!state.currentStudyTarget) return { completed: false, target: null };
|
||||
|
||||
const newProgress = state.currentStudyTarget.progress + progressGain;
|
||||
const completed = newProgress >= state.currentStudyTarget.required;
|
||||
|
||||
const newTarget = completed ? null : {
|
||||
...state.currentStudyTarget,
|
||||
progress: newProgress,
|
||||
};
|
||||
|
||||
set({ currentStudyTarget: newTarget });
|
||||
|
||||
return {
|
||||
completed,
|
||||
target: completed ? state.currentStudyTarget : null
|
||||
};
|
||||
},
|
||||
|
||||
cancelStudy: (retentionBonus: number) => {
|
||||
const state = get();
|
||||
if (!state.currentStudyTarget) return;
|
||||
|
||||
// Save progress with retention bonus
|
||||
const savedProgress = Math.min(
|
||||
state.currentStudyTarget.progress,
|
||||
state.currentStudyTarget.required * retentionBonus
|
||||
);
|
||||
|
||||
if (state.currentStudyTarget.type === 'skill') {
|
||||
set({
|
||||
currentStudyTarget: null,
|
||||
skillProgress: {
|
||||
...state.skillProgress,
|
||||
[state.currentStudyTarget.id]: savedProgress,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
set({ currentStudyTarget: null });
|
||||
}
|
||||
},
|
||||
|
||||
setStudyTarget: (target: StudyTarget | null) => {
|
||||
set({ currentStudyTarget: target });
|
||||
},
|
||||
|
||||
setCurrentStudyTarget: (target: StudyTarget | null) => {
|
||||
set({ currentStudyTarget: target });
|
||||
},
|
||||
|
||||
selectSkillUpgrade: (skillId: string, upgradeId: string) => {
|
||||
set((state) => {
|
||||
const current = state.skillUpgrades?.[skillId] || [];
|
||||
if (current.includes(upgradeId)) return state;
|
||||
if (current.length >= 2) return state; // Max 2 upgrades per milestone
|
||||
return {
|
||||
skillUpgrades: {
|
||||
...state.skillUpgrades,
|
||||
[skillId]: [...current, upgradeId],
|
||||
},
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
deselectSkillUpgrade: (skillId: string, upgradeId: string) => {
|
||||
set((state) => {
|
||||
const current = state.skillUpgrades?.[skillId] || [];
|
||||
return {
|
||||
skillUpgrades: {
|
||||
...state.skillUpgrades,
|
||||
[skillId]: current.filter(id => id !== upgradeId),
|
||||
},
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
commitSkillUpgrades: (skillId: string, upgradeIds: string[]) => {
|
||||
set((state) => {
|
||||
// Determine which milestone we're committing
|
||||
const isL5 = upgradeIds.some(id => id.includes('_l5'));
|
||||
const isL10 = upgradeIds.some(id => id.includes('_l10'));
|
||||
|
||||
const existingUpgrades = state.skillUpgrades?.[skillId] || [];
|
||||
|
||||
let preservedUpgrades: string[];
|
||||
if (isL5) {
|
||||
preservedUpgrades = existingUpgrades.filter(id => id.includes('_l10'));
|
||||
} else if (isL10) {
|
||||
preservedUpgrades = existingUpgrades.filter(id => id.includes('_l5'));
|
||||
} else {
|
||||
preservedUpgrades = [];
|
||||
}
|
||||
|
||||
const mergedUpgrades = [...preservedUpgrades, ...upgradeIds];
|
||||
|
||||
return {
|
||||
skillUpgrades: {
|
||||
...state.skillUpgrades,
|
||||
[skillId]: mergedUpgrades,
|
||||
},
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
tierUpSkill: (skillId: string) => {
|
||||
const state = get();
|
||||
const baseSkillId = skillId.includes('_t') ? skillId.split('_t')[0] : skillId;
|
||||
const currentTier = state.skillTiers?.[baseSkillId] || 1;
|
||||
const nextTier = currentTier + 1;
|
||||
|
||||
if (nextTier > 5) return;
|
||||
|
||||
const nextTierSkillId = `${baseSkillId}_t${nextTier}`;
|
||||
const currentLevel = state.skills[skillId] || 0;
|
||||
|
||||
set({
|
||||
skillTiers: {
|
||||
...state.skillTiers,
|
||||
[baseSkillId]: nextTier,
|
||||
},
|
||||
skills: {
|
||||
...state.skills,
|
||||
[nextTierSkillId]: currentLevel,
|
||||
[skillId]: 0,
|
||||
},
|
||||
skillUpgrades: {
|
||||
...state.skillUpgrades,
|
||||
[nextTierSkillId]: [],
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
getSkillUpgradeChoices: (skillId: string, milestone: 5 | 10) => {
|
||||
const state = get();
|
||||
const baseSkillId = getBaseSkillId(skillId);
|
||||
const tier = state.skillTiers?.[baseSkillId] || 1;
|
||||
|
||||
const path = SKILL_EVOLUTION_PATHS[baseSkillId];
|
||||
if (!path) return { available: [], selected: [] };
|
||||
|
||||
const tierDef = path.tiers.find(t => t.tier === tier);
|
||||
if (!tierDef) return { available: [], selected: [] };
|
||||
|
||||
const available = tierDef.upgrades.filter(u => u.milestone === milestone);
|
||||
const selected = state.skillUpgrades?.[skillId]?.filter(id =>
|
||||
available.some(u => u.id === id)
|
||||
) || [];
|
||||
|
||||
return { available, selected };
|
||||
},
|
||||
|
||||
resetSkills: (
|
||||
skills: Record<string, number> = {},
|
||||
skillUpgrades: Record<string, string[]> = {},
|
||||
skillTiers: Record<string, number> = {}
|
||||
) => {
|
||||
set({
|
||||
skills,
|
||||
skillProgress: {},
|
||||
skillUpgrades,
|
||||
skillTiers,
|
||||
paidStudySkills: {},
|
||||
currentStudyTarget: null,
|
||||
parallelStudyTarget: null,
|
||||
});
|
||||
},
|
||||
}),
|
||||
{
|
||||
name: 'mana-loop-skills',
|
||||
partialize: (state) => ({
|
||||
skills: state.skills,
|
||||
skillProgress: state.skillProgress,
|
||||
skillUpgrades: state.skillUpgrades,
|
||||
skillTiers: state.skillTiers,
|
||||
paidStudySkills: state.paidStudySkills,
|
||||
}),
|
||||
}
|
||||
)
|
||||
);
|
||||
Reference in New Issue
Block a user