fix(spire): reset currentAction to meditate on spire exit; fix(crafting): wire enchanting state hooks to EnchantmentDesigner/Preparer/Applier
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m53s

This commit is contained in:
2026-05-10 21:28:46 +02:00
parent cad72fe88c
commit ae0bf3e38d
4 changed files with 88 additions and 15 deletions
+1
View File
@@ -397,6 +397,7 @@ Mana-Loop/
│ │ │ │ ├── regen.test.ts │ │ │ │ ├── regen.test.ts
│ │ │ │ ├── skill.test.ts │ │ │ │ ├── skill.test.ts
│ │ │ │ ├── spell-cost.test.ts │ │ │ │ ├── spell-cost.test.ts
│ │ │ │ ├── spire-exit-action.test.ts
│ │ │ │ └── spire-tab-refresh.test.ts │ │ │ │ └── spire-tab-refresh.test.ts
│ │ │ ├── attunementStore.ts │ │ │ ├── attunementStore.ts
│ │ │ ├── combat-actions.ts │ │ │ ├── combat-actions.ts
+22 -14
View File
@@ -16,6 +16,7 @@ import {
import { useCombatStore, useCraftingStore } from '@/lib/game/stores'; import { useCombatStore, useCraftingStore } from '@/lib/game/stores';
import { DebugName } from '@/lib/game/debug-context'; import { DebugName } from '@/lib/game/debug-context';
import { useGameToast } from '@/components/game/GameToast'; import { useGameToast } from '@/components/game/GameToast';
import type { DesignEffect } from '@/lib/game/types';
export function CraftingTab() { export function CraftingTab() {
const showToast = useGameToast(); const showToast = useGameToast();
@@ -30,6 +31,13 @@ export function CraftingTab() {
const [activeTab, setActiveTab] = useState<'fabricate' | 'enchant'>('fabricate'); const [activeTab, setActiveTab] = useState<'fabricate' | 'enchant'>('fabricate');
const [enchantStage, setEnchantStage] = useState<'design' | 'prepare' | 'apply'>('design'); const [enchantStage, setEnchantStage] = useState<'design' | 'prepare' | 'apply'>('design');
// Enchant state
const [selectedEquipmentType, setSelectedEquipmentType] = useState<string | null>(null);
const [selectedEffects, setSelectedEffects] = useState<DesignEffect[]>([]);
const [designName, setDesignName] = useState('');
const [selectedDesign, setSelectedDesign] = useState<string | null>(null);
const [selectedEquipmentInstance, setSelectedEquipmentInstance] = useState<string | null>(null);
// Safe toFixed helper // Safe toFixed helper
const safeToFixed = (value: number | undefined, decimals: number = 0): string => { const safeToFixed = (value: number | undefined, decimals: number = 0): string => {
if (value === undefined || isNaN(value)) return '0'; if (value === undefined || isNaN(value)) return '0';
@@ -121,28 +129,28 @@ export function CraftingTab() {
{/* Enchant Stage Content */} {/* Enchant Stage Content */}
{enchantStage === 'design' && ( {enchantStage === 'design' && (
<EnchantmentDesigner <EnchantmentDesigner
selectedEquipmentType={null} selectedEquipmentType={selectedEquipmentType}
setSelectedEquipmentType={() => {}} setSelectedEquipmentType={setSelectedEquipmentType}
selectedEffects={[]} selectedEffects={selectedEffects}
setSelectedEffects={() => {}} setSelectedEffects={setSelectedEffects}
designName={''} designName={designName}
setDesignName={() => {}} setDesignName={setDesignName}
selectedDesign={null} selectedDesign={selectedDesign}
setSelectedDesign={() => {}} setSelectedDesign={setSelectedDesign}
/> />
)} )}
{enchantStage === 'prepare' && ( {enchantStage === 'prepare' && (
<EnchantmentPreparer <EnchantmentPreparer
selectedEquipmentInstance={null} selectedEquipmentInstance={selectedEquipmentInstance}
setSelectedEquipmentInstance={() => {}} setSelectedEquipmentInstance={setSelectedEquipmentInstance}
/> />
)} )}
{enchantStage === 'apply' && ( {enchantStage === 'apply' && (
<EnchantmentApplier <EnchantmentApplier
selectedEquipmentInstance={null} selectedEquipmentInstance={selectedEquipmentInstance}
setSelectedEquipmentInstance={() => {}} setSelectedEquipmentInstance={setSelectedEquipmentInstance}
selectedDesign={null} selectedDesign={selectedDesign}
setSelectedDesign={() => {}} setSelectedDesign={setSelectedDesign}
onEnchantmentApplied={handleEnchantmentApplied} onEnchantmentApplied={handleEnchantmentApplied}
onCapacityExceeded={handleCapacityExceeded} onCapacityExceeded={handleCapacityExceeded}
/> />
@@ -0,0 +1,64 @@
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { useCombatStore } from '../combatStore';
import { useUIStore } from '../uiStore';
describe('Spire Exit Action — Bug 1 Fix', () => {
beforeEach(() => {
// Reset combat store to a known state
useCombatStore.setState({
currentFloor: 5,
floorHP: 100,
floorMaxHP: 100,
maxFloorReached: 5,
activeSpell: 'manaBolt',
currentAction: 'climb' as const,
castProgress: 0,
spireMode: true,
climbDirection: 'down' as const,
isDescending: true,
});
});
afterEach(() => {
useCombatStore.setState({
currentFloor: 1,
floorHP: 100,
floorMaxHP: 100,
maxFloorReached: 1,
activeSpell: 'manaBolt',
currentAction: 'meditate',
castProgress: 0,
spireMode: false,
climbDirection: null,
isDescending: false,
});
useUIStore.setState({ logs: [] });
});
it('should reset currentAction to "meditate" when exitSpireMode is called', () => {
// Pre-condition: action is "climb" while in spire
expect(useCombatStore.getState().currentAction).toBe('climb');
expect(useCombatStore.getState().spireMode).toBe(true);
// Exit spire mode
useCombatStore.getState().exitSpireMode();
// Post-condition: currentAction must be "meditate"
expect(useCombatStore.getState().currentAction).toBe('meditate');
});
it('should set spireMode to false when exitSpireMode is called', () => {
useCombatStore.getState().exitSpireMode();
expect(useCombatStore.getState().spireMode).toBe(false);
});
it('should clear climbDirection when exitSpireMode is called', () => {
useCombatStore.getState().exitSpireMode();
expect(useCombatStore.getState().climbDirection).toBeNull();
});
it('should clear isDescending when exitSpireMode is called', () => {
useCombatStore.getState().exitSpireMode();
expect(useCombatStore.getState().isDescending).toBe(false);
});
});
+1 -1
View File
@@ -253,7 +253,7 @@ export const useCombatStore = create<CombatState>()(
}, },
exitSpireMode: () => { exitSpireMode: () => {
set({ spireMode: false, climbDirection: null, isDescending: false }); set({ spireMode: false, currentAction: 'meditate', climbDirection: null, isDescending: false });
}, },
startClimbUp: () => set({ climbDirection: 'up', currentAction: 'climb' }), startClimbUp: () => set({ climbDirection: 'up', currentAction: 'climb' }),