fix: migrate equipment and enchantment state to modular stores, fix EnchantmentDesigner
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m24s
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m24s
This commit is contained in:
@@ -184,7 +184,7 @@ export function updateEnchanterAttunement(
|
||||
let newXP = enchanterState.experience + xpGained;
|
||||
let newLevel = enchanterState.level;
|
||||
|
||||
const { getAttunementXPForLevel, MAX_ATTUNEMENT_LEVEL } = require('./data/attunements');
|
||||
import { getAttunementXPForLevel, MAX_ATTUNEMENT_LEVEL } from './data/attunements.js';
|
||||
|
||||
while (newLevel < MAX_ATTUNEMENT_LEVEL) {
|
||||
const xpNeeded = getAttunementXPForLevel(newLevel + 1);
|
||||
|
||||
@@ -112,7 +112,7 @@ describe('Combat Calculations', () => {
|
||||
describe('getFloorMaxHP', () => {
|
||||
it('should return guardian HP for guardian floors', () => {
|
||||
// Import GUARDIANS from constants
|
||||
const { GUARDIANS } = require('../../constants');
|
||||
import { GUARDIANS } from '../../constants';
|
||||
expect(getFloorMaxHP(10)).toBe(GUARDIANS[10].hp);
|
||||
expect(getFloorMaxHP(100)).toBe(GUARDIANS[100].hp);
|
||||
});
|
||||
|
||||
@@ -4,7 +4,22 @@
|
||||
|
||||
import { create } from 'zustand';
|
||||
import { persist } from 'zustand/middleware';
|
||||
import type { DesignProgress, PreparationProgress, ApplicationProgress, EquipmentCraftingProgress } from '../types';
|
||||
import type { DesignProgress, PreparationProgress, ApplicationProgress, EquipmentCraftingProgress, EnchantmentDesign, EquipmentInstance, DesignEffect } from '../types';
|
||||
|
||||
// Import crafting modules for action logic
|
||||
import * as CraftingUtils from '../crafting-utils';
|
||||
import * as CraftingDesign from '../crafting-design';
|
||||
import { computeEffects } from '../upgrade-effects';
|
||||
import { hasSpecial, SPECIAL_EFFECTS } from '../special-effects';
|
||||
|
||||
// Import other stores to access required state
|
||||
import { useSkillStore } from './skillStore';
|
||||
import { useGameStore } from './gameStore';
|
||||
|
||||
// Import action modules
|
||||
import * as ApplicationActions from '../crafting-actions/application-actions';
|
||||
import * as CraftingApply from '../crafting-apply';
|
||||
import * as PreparationActions from '../crafting-actions/preparation-actions';
|
||||
|
||||
export interface CraftingState {
|
||||
// Crafting progress
|
||||
@@ -13,6 +28,15 @@ export interface CraftingState {
|
||||
preparationProgress: PreparationProgress | null;
|
||||
applicationProgress: ApplicationProgress | null;
|
||||
equipmentCraftingProgress: EquipmentCraftingProgress | null;
|
||||
|
||||
// Enchantment designs
|
||||
enchantmentDesigns: EnchantmentDesign[];
|
||||
// Unlocked enchantment effects
|
||||
unlockedEffects: string[];
|
||||
// Equipment instances (instanceId -> instance)
|
||||
equipmentInstances: Record<string, EquipmentInstance>;
|
||||
// Equipped instances (slot -> instanceId or null)
|
||||
equippedInstances: Record<string, string | null>;
|
||||
}
|
||||
|
||||
export interface CraftingActions {
|
||||
@@ -28,19 +52,39 @@ export interface CraftingActions {
|
||||
|
||||
// Actions for equipment crafting progress
|
||||
setEquipmentCraftingProgress: (progress: EquipmentCraftingProgress | null) => void;
|
||||
|
||||
// Enchantment design actions
|
||||
startDesigningEnchantment: (name: string, equipmentTypeId: string, effects: DesignEffect[]) => boolean;
|
||||
cancelDesign: () => void;
|
||||
saveDesign: (design: EnchantmentDesign) => void;
|
||||
deleteDesign: (designId: string) => void;
|
||||
|
||||
// Enchantment application actions
|
||||
startApplying: (equipmentInstanceId: string, designId: string) => boolean;
|
||||
pauseApplication: () => void;
|
||||
resumeApplication: () => void;
|
||||
cancelApplication: () => void;
|
||||
|
||||
// Enchantment preparation actions
|
||||
startPreparing: (equipmentInstanceId: string) => boolean;
|
||||
cancelPreparation: () => void;
|
||||
}
|
||||
|
||||
export type CraftingStore = CraftingState & CraftingActions;
|
||||
|
||||
export const useCraftingStore = create<CraftingStore>()(
|
||||
persist(
|
||||
(set) => ({
|
||||
(set, get) => ({
|
||||
// Initial state
|
||||
designProgress: null,
|
||||
designProgress2: null,
|
||||
preparationProgress: null,
|
||||
applicationProgress: null,
|
||||
equipmentCraftingProgress: null,
|
||||
enchantmentDesigns: [],
|
||||
unlockedEffects: [],
|
||||
equipmentInstances: {},
|
||||
equippedInstances: {},
|
||||
|
||||
// Actions
|
||||
setDesignProgress: (progress) => set({ designProgress: progress }),
|
||||
@@ -48,6 +92,135 @@ export const useCraftingStore = create<CraftingStore>()(
|
||||
setPreparationProgress: (progress) => set({ preparationProgress: progress }),
|
||||
setApplicationProgress: (progress) => set({ applicationProgress: progress }),
|
||||
setEquipmentCraftingProgress: (progress) => set({ equipmentCraftingProgress: progress }),
|
||||
|
||||
// Enchantment design actions
|
||||
startDesigningEnchantment: (name, equipmentTypeId, effects) => {
|
||||
// Get state from other stores
|
||||
const skillState = useSkillStore.getState();
|
||||
const state = get(); // crafting state
|
||||
|
||||
const enchantingLevel = skillState.skills?.enchanting || 0;
|
||||
const validation = CraftingDesign.validateDesignEffects(effects, equipmentTypeId, enchantingLevel);
|
||||
if (!validation.valid) return false;
|
||||
|
||||
const equipType = CraftingUtils.getEquipmentType(equipmentTypeId);
|
||||
if (!equipType) return false;
|
||||
|
||||
const efficiencyBonus = (skillState.skillUpgrades?.['efficientEnchant'] || []).length * 0.05 || 0;
|
||||
const totalCapacityCost = CraftingDesign.calculateDesignCapacityCost(effects, efficiencyBonus);
|
||||
|
||||
if (totalCapacityCost > equipType.baseCapacity) return false;
|
||||
|
||||
const computedEffects = computeEffects(skillState.skillUpgrades || {}, skillState.skillTiers || {});
|
||||
const hasEnchantMastery = hasSpecial(computedEffects, SPECIAL_EFFECTS.ENCHANT_MASTERY);
|
||||
|
||||
let updates: Partial<CraftingState> = {};
|
||||
|
||||
if (!state.designProgress) {
|
||||
updates = {
|
||||
designProgress: {
|
||||
designId: CraftingUtils.generateDesignId(),
|
||||
progress: 0,
|
||||
required: CraftingDesign.calculateDesignTime(effects),
|
||||
name,
|
||||
equipmentType: equipmentTypeId,
|
||||
effects,
|
||||
},
|
||||
};
|
||||
// Update currentAction in gameStore
|
||||
useGameStore.setState({ currentAction: 'design' });
|
||||
} else if (hasEnchantMastery && !state.designProgress2) {
|
||||
updates = {
|
||||
designProgress2: {
|
||||
designId: CraftingUtils.generateDesignId(),
|
||||
progress: 0,
|
||||
required: CraftingDesign.calculateDesignTime(effects),
|
||||
name,
|
||||
equipmentType: equipmentTypeId,
|
||||
effects,
|
||||
},
|
||||
};
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
set(updates);
|
||||
return true;
|
||||
},
|
||||
|
||||
cancelDesign: () => {
|
||||
const state = get();
|
||||
if (state.designProgress2 && !state.designProgress) {
|
||||
set({ designProgress2: null });
|
||||
} else {
|
||||
set({ designProgress: null });
|
||||
useGameStore.setState({ currentAction: 'meditate' });
|
||||
}
|
||||
},
|
||||
|
||||
deleteDesign: (designId) => {
|
||||
set((state) => ({
|
||||
enchantmentDesigns: state.enchantmentDesigns.filter(d => d.id !== designId),
|
||||
}));
|
||||
},
|
||||
|
||||
// Enchantment design save
|
||||
saveDesign: (design) => {
|
||||
const state = get();
|
||||
if (state.designProgress2 && state.designProgress2.designId === design.id) {
|
||||
set((s) => ({
|
||||
enchantmentDesigns: [...s.enchantmentDesigns, design],
|
||||
designProgress2: null,
|
||||
}));
|
||||
} else {
|
||||
set((s) => ({
|
||||
enchantmentDesigns: [...s.enchantmentDesigns, design],
|
||||
designProgress: null,
|
||||
currentAction: 'meditate' as const,
|
||||
}));
|
||||
}
|
||||
},
|
||||
|
||||
// Enchantment application actions
|
||||
startApplying: (equipmentInstanceId, designId) => {
|
||||
return ApplicationActions.startApplying(
|
||||
equipmentInstanceId,
|
||||
designId,
|
||||
get,
|
||||
set
|
||||
);
|
||||
},
|
||||
|
||||
pauseApplication: () => {
|
||||
ApplicationActions.pauseApplication(get, set);
|
||||
},
|
||||
|
||||
resumeApplication: () => {
|
||||
ApplicationActions.resumeApplication(get, set);
|
||||
},
|
||||
|
||||
cancelApplication: () => {
|
||||
ApplicationActions.cancelApplication(set);
|
||||
useGameStore.setState({ currentAction: 'meditate' });
|
||||
},
|
||||
|
||||
// Preparation actions
|
||||
startPreparing: (equipmentInstanceId) => {
|
||||
// Get rawMana from manaStore
|
||||
const rawMana = useManaStore.getState().rawMana;
|
||||
// Temporary state to pass to preparation action
|
||||
const tempState = { ...get(), rawMana } as any;
|
||||
return PreparationActions.startPreparing(
|
||||
equipmentInstanceId,
|
||||
() => tempState,
|
||||
set
|
||||
);
|
||||
},
|
||||
|
||||
cancelPreparation: () => {
|
||||
PreparationActions.cancelPreparation(set);
|
||||
useGameStore.setState({ currentAction: 'meditate' });
|
||||
},
|
||||
}),
|
||||
{
|
||||
name: 'mana-loop-crafting',
|
||||
@@ -57,6 +230,10 @@ export const useCraftingStore = create<CraftingStore>()(
|
||||
preparationProgress: state.preparationProgress,
|
||||
applicationProgress: state.applicationProgress,
|
||||
equipmentCraftingProgress: state.equipmentCraftingProgress,
|
||||
enchantmentDesigns: state.enchantmentDesigns,
|
||||
unlockedEffects: state.unlockedEffects,
|
||||
equipmentInstances: state.equipmentInstances,
|
||||
equippedInstances: state.equippedInstances,
|
||||
}),
|
||||
}
|
||||
)
|
||||
|
||||
@@ -5,6 +5,7 @@ import { useSkillStore } from './skillStore';
|
||||
import { usePrestigeStore } from './prestigeStore';
|
||||
import { useCombatStore } from './combatStore';
|
||||
import { useUIStore } from './uiStore';
|
||||
import { useCraftingStore } from './craftingStore';
|
||||
import { getUnifiedEffects } from '../effects';
|
||||
import {
|
||||
computeMaxMana,
|
||||
@@ -32,8 +33,8 @@ export function useGameLoop() {
|
||||
export function useUnifiedEffects() {
|
||||
const skillUpgrades = useSkillStore((s) => s.skillUpgrades);
|
||||
const skillTiers = useSkillStore((s) => s.skillTiers);
|
||||
const equippedInstances = useGameStore((s) => s.equippedInstances);
|
||||
const equipmentInstances = useGameStore((s) => s.equipmentInstances);
|
||||
const equippedInstances = useCraftingStore((s) => s.equippedInstances);
|
||||
const equipmentInstances = useCraftingStore((s) => s.equipmentInstances);
|
||||
|
||||
return getUnifiedEffects({
|
||||
skillUpgrades,
|
||||
@@ -54,12 +55,14 @@ export function useManaStats() {
|
||||
const meditateTicks = useManaStore((s) => s.meditateTicks);
|
||||
const day = useGameStore((s) => s.day);
|
||||
const hour = useGameStore((s) => s.hour);
|
||||
const equippedInstances = useCraftingStore((s) => s.equippedInstances);
|
||||
const equipmentInstances = useCraftingStore((s) => s.equipmentInstances);
|
||||
|
||||
const upgradeEffects = getUnifiedEffects({
|
||||
skillUpgrades,
|
||||
skillTiers,
|
||||
equippedInstances: {},
|
||||
equipmentInstances: {},
|
||||
equippedInstances,
|
||||
equipmentInstances,
|
||||
});
|
||||
|
||||
const maxMana = computeMaxMana(
|
||||
@@ -85,12 +88,12 @@ export function useManaStats() {
|
||||
|
||||
// Mana Cascade bonus
|
||||
const manaCascadeBonus = upgradeEffects.specials.has('mana_cascade')
|
||||
? Math.floor(maxMana / 100) * 0.1
|
||||
? Math.floor(maxMana /100) * 0.1
|
||||
: 0;
|
||||
|
||||
// Mana Waterfall bonus
|
||||
const manaWaterfallBonus = upgradeEffects.specials.has('mana_waterfall')
|
||||
? Math.floor(maxMana / 100) * 0.25
|
||||
? Math.floor(maxMana /100) * 0.25
|
||||
: 0;
|
||||
|
||||
const effectiveRegen = (effectiveRegenWithSpecials + manaCascadeBonus + manaWaterfallBonus) * meditationMultiplier;
|
||||
@@ -114,8 +117,8 @@ export function useManaStats() {
|
||||
export function useCombatStats() {
|
||||
const skills = useSkillStore((s) => s.skills);
|
||||
const signedPacts = usePrestigeStore((s) => s.signedPacts);
|
||||
const equippedInstances = useGameStore((s) => s.equippedInstances);
|
||||
const equipmentInstances = useGameStore((s) => s.equipmentInstances);
|
||||
const equippedInstances = useCraftingStore((s) => s.equippedInstances);
|
||||
const equipmentInstances = useCraftingStore((s) => s.equipmentInstances);
|
||||
const skillUpgrades = useSkillStore((s) => s.skillUpgrades);
|
||||
const skillTiers = useSkillStore((s) => s.skillTiers);
|
||||
|
||||
@@ -139,8 +142,8 @@ export function useCombatStats() {
|
||||
* Get equipment-related derived state
|
||||
*/
|
||||
export function useEquipmentState() {
|
||||
const equippedInstances = useGameStore((s) => s.equippedInstances);
|
||||
const equipmentInstances = useGameStore((s) => s.equipmentInstances);
|
||||
const equippedInstances = useCraftingStore((s) => s.equippedInstances);
|
||||
const equipmentInstances = useCraftingStore((s) => s.equipmentInstances);
|
||||
const elements = useManaStore((s) => s.elements);
|
||||
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user