Compare commits

..

2 Commits

Author SHA1 Message Date
Z User
300e43f8be Fix multiple bugs and add debug features
All checks were successful
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m56s
- Add debug option to show component names at top of each component
- Fix mana lists to hide empty elemental mana types in ManaDisplay and LabTab
- Fix enchantment designing getting stuck at 99% by auto-saving design on completion
- Add resetFloorHP function and debug button to fix stuck floor HP issues
- Store design data in DesignProgress for proper completion handling
2026-03-29 13:15:43 +00:00
Z User
6dc301bd7b Initial commit 2026-03-29 13:15:43 +00:00
14 changed files with 321 additions and 153 deletions

View File

@@ -1225,3 +1225,25 @@ Checked 846 installs across 915 packages (no changes) [60.00ms]
$ prisma db push $ prisma db push
Failed to load config file "/home/z/my-project" as a TypeScript/JavaScript module. Error: Error: ENOTDIR: not a directory, lstat '/home/z/my-project/.config/prisma' Failed to load config file "/home/z/my-project" as a TypeScript/JavaScript module. Error: Error: ENOTDIR: not a directory, lstat '/home/z/my-project/.config/prisma'
error: script "db:push" exited with code 1 error: script "db:push" exited with code 1
==========================================
[2026-03-29 11:16:56] Starting: bun install
==========================================
[BUN] Installing dependencies...
[0.08ms] ".env"
bun install v1.3.10 (30e609e0)
+ next@16.1.3
49 packages installed [2.62s]
==========================================
[2026-03-29 11:16:59] Completed: bun install
[LOG] Step: bun install | Duration: 3s
==========================================
==========================================
[2026-03-29 11:16:59] Starting: bun run db:push
==========================================
[BUN] Setting up database...
$ prisma db push
Failed to load config file "/home/z/my-project" as a TypeScript/JavaScript module. Error: Error: ENOTDIR: not a directory, lstat '/home/z/my-project/.config/prisma'
error: script "db:push" exited with code 1

View File

@@ -1 +1 @@
488 486

0
GAME_SYSTEMS_ANALYSIS.md Normal file → Executable file
View File

View File

@@ -2,6 +2,7 @@ import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google"; import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css"; import "./globals.css";
import { Toaster } from "@/components/ui/toaster"; import { Toaster } from "@/components/ui/toaster";
import { DebugProvider } from "@/lib/game/debug-context";
const geistSans = Geist({ const geistSans = Geist({
variable: "--font-geist-sans", variable: "--font-geist-sans",
@@ -14,25 +15,10 @@ const geistMono = Geist_Mono({
}); });
export const metadata: Metadata = { export const metadata: Metadata = {
title: "Z.ai Code Scaffold - AI-Powered Development", title: "Mana Loop",
description: "Modern Next.js scaffold optimized for AI-powered development with Z.ai. Built with TypeScript, Tailwind CSS, and shadcn/ui.", description: "A time-loop incremental game where you climb the Spire and sign pacts with guardians.",
keywords: ["Z.ai", "Next.js", "TypeScript", "Tailwind CSS", "shadcn/ui", "AI development", "React"], keywords: ["Mana Loop", "incremental game", "idle game", "time loop"],
authors: [{ name: "Z.ai Team" }], authors: [{ name: "Mana Loop Team" }],
icons: {
icon: "https://z-cdn.chatglm.cn/z-ai/static/logo.svg",
},
openGraph: {
title: "Z.ai Code Scaffold",
description: "AI-powered development with modern React stack",
url: "https://chat.z.ai",
siteName: "Z.ai",
type: "website",
},
twitter: {
card: "summary_large_image",
title: "Z.ai Code Scaffold",
description: "AI-powered development with modern React stack",
},
}; };
export default function RootLayout({ export default function RootLayout({
@@ -45,7 +31,9 @@ export default function RootLayout({
<body <body
className={`${geistSans.variable} ${geistMono.variable} antialiased bg-background text-foreground`} className={`${geistSans.variable} ${geistMono.variable} antialiased bg-background text-foreground`}
> >
{children} <DebugProvider>
{children}
</DebugProvider>
<Toaster /> <Toaster />
</body> </body>
</html> </html>

View File

@@ -17,6 +17,7 @@ import { CraftingTab, SpireTab, SpellsTab, LabTab, SkillsTab, StatsTab, Equipmen
import { ActionButtons, CalendarDisplay, ManaDisplay, TimeDisplay } from '@/components/game'; import { ActionButtons, CalendarDisplay, ManaDisplay, TimeDisplay } from '@/components/game';
import { LootInventoryDisplay } from '@/components/game/LootInventory'; import { LootInventoryDisplay } from '@/components/game/LootInventory';
import { AchievementsDisplay } from '@/components/game/AchievementsDisplay'; import { AchievementsDisplay } from '@/components/game/AchievementsDisplay';
import { DebugName } from '@/lib/game/debug-context';
export default function ManaLoopGame() { export default function ManaLoopGame() {
const [activeTab, setActiveTab] = useState('spire'); const [activeTab, setActiveTab] = useState('spire');
@@ -175,56 +176,66 @@ export default function ManaLoopGame() {
{/* Left Panel - Mana & Actions */} {/* Left Panel - Mana & Actions */}
<div className="md:w-80 space-y-4 flex-shrink-0"> <div className="md:w-80 space-y-4 flex-shrink-0">
{/* Mana Display */} {/* Mana Display */}
<ManaDisplay <DebugName name="ManaDisplay">
rawMana={store.rawMana} <ManaDisplay
maxMana={maxMana} rawMana={store.rawMana}
effectiveRegen={effectiveRegen} maxMana={maxMana}
meditationMultiplier={meditationMultiplier} effectiveRegen={effectiveRegen}
clickMana={clickMana} meditationMultiplier={meditationMultiplier}
isGathering={isGathering} clickMana={clickMana}
onGatherStart={handleGatherStart} isGathering={isGathering}
onGatherEnd={handleGatherEnd} onGatherStart={handleGatherStart}
elements={store.elements} onGatherEnd={handleGatherEnd}
/> elements={store.elements}
/>
</DebugName>
{/* Action Buttons */} {/* Action Buttons */}
<ActionButtons <DebugName name="ActionButtons">
currentAction={store.currentAction} <ActionButtons
designProgress={store.designProgress} currentAction={store.currentAction}
preparationProgress={store.preparationProgress} designProgress={store.designProgress}
applicationProgress={store.applicationProgress} preparationProgress={store.preparationProgress}
setAction={store.setAction} applicationProgress={store.applicationProgress}
/> setAction={store.setAction}
/>
</DebugName>
{/* Calendar */} {/* Calendar */}
<CalendarDisplay <DebugName name="CalendarDisplay">
day={store.day} <CalendarDisplay
hour={store.hour} day={store.day}
incursionStrength={incursionStrength} hour={store.hour}
/> incursionStrength={incursionStrength}
/>
</DebugName>
{/* Loot Inventory */} {/* Loot Inventory */}
<LootInventoryDisplay <DebugName name="LootInventoryDisplay">
inventory={store.lootInventory} <LootInventoryDisplay
elements={store.elements} inventory={store.lootInventory}
equipmentInstances={store.equipmentInstances} elements={store.elements}
onDeleteMaterial={store.deleteMaterial} equipmentInstances={store.equipmentInstances}
onDeleteEquipment={store.deleteEquipmentInstance} onDeleteMaterial={store.deleteMaterial}
/> onDeleteEquipment={store.deleteEquipmentInstance}
/>
</DebugName>
{/* Achievements */} {/* Achievements */}
<AchievementsDisplay <DebugName name="AchievementsDisplay">
achievements={store.achievements} <AchievementsDisplay
gameState={{ achievements={store.achievements}
maxFloorReached: store.maxFloorReached, gameState={{
totalManaGathered: store.totalManaGathered, maxFloorReached: store.maxFloorReached,
signedPacts: store.signedPacts, totalManaGathered: store.totalManaGathered,
totalSpellsCast: store.totalSpellsCast, signedPacts: store.signedPacts,
totalDamageDealt: store.totalDamageDealt, totalSpellsCast: store.totalSpellsCast,
totalCraftsCompleted: store.totalCraftsCompleted, totalDamageDealt: store.totalDamageDealt,
combo: store.combo, totalCraftsCompleted: store.totalCraftsCompleted,
}} combo: store.combo,
/> }}
/>
</DebugName>
</div> </div>
{/* Right Panel - Tabs */} {/* Right Panel - Tabs */}
@@ -242,57 +253,77 @@ export default function ManaLoopGame() {
<TabsTrigger value="debug" className="text-xs px-2 py-1">🔧 Debug</TabsTrigger> <TabsTrigger value="debug" className="text-xs px-2 py-1">🔧 Debug</TabsTrigger>
<TabsTrigger value="grimoire" className="text-xs px-2 py-1">📖 Grimoire</TabsTrigger> <TabsTrigger value="grimoire" className="text-xs px-2 py-1">📖 Grimoire</TabsTrigger>
</TabsList> </TabsList>
<TabsContent value="spire"> <TabsContent value="spire">
<SpireTab store={store} /> <DebugName name="SpireTab">
<SpireTab store={store} />
</DebugName>
</TabsContent> </TabsContent>
<TabsContent value="attunements"> <TabsContent value="attunements">
<AttunementsTab store={store} /> <DebugName name="AttunementsTab">
<AttunementsTab store={store} />
</DebugName>
</TabsContent> </TabsContent>
<TabsContent value="skills"> <TabsContent value="skills">
<SkillsTab store={store} /> <DebugName name="SkillsTab">
<SkillsTab store={store} />
</DebugName>
</TabsContent> </TabsContent>
<TabsContent value="spells"> <TabsContent value="spells">
<SpellsTab store={store} /> <DebugName name="SpellsTab">
<SpellsTab store={store} />
</DebugName>
</TabsContent> </TabsContent>
<TabsContent value="equipment"> <TabsContent value="equipment">
<EquipmentTab store={store} /> <DebugName name="EquipmentTab">
<EquipmentTab store={store} />
</DebugName>
</TabsContent> </TabsContent>
<TabsContent value="crafting"> <TabsContent value="crafting">
<CraftingTab store={store} /> <DebugName name="CraftingTab">
<CraftingTab store={store} />
</DebugName>
</TabsContent> </TabsContent>
<TabsContent value="lab"> <TabsContent value="lab">
<LabTab store={store} /> <DebugName name="LabTab">
<LabTab store={store} />
</DebugName>
</TabsContent> </TabsContent>
<TabsContent value="stats"> <TabsContent value="stats">
<StatsTab <DebugName name="StatsTab">
store={store} <StatsTab
upgradeEffects={upgradeEffects} store={store}
maxMana={maxMana} upgradeEffects={upgradeEffects}
baseRegen={baseRegen} maxMana={maxMana}
clickMana={clickMana} baseRegen={baseRegen}
meditationMultiplier={meditationMultiplier} clickMana={clickMana}
effectiveRegen={effectiveRegen} meditationMultiplier={meditationMultiplier}
incursionStrength={incursionStrength} effectiveRegen={effectiveRegen}
manaCascadeBonus={manaCascadeBonus} incursionStrength={incursionStrength}
studySpeedMult={studySpeedMult} manaCascadeBonus={manaCascadeBonus}
studyCostMult={studyCostMult} studySpeedMult={studySpeedMult}
/> studyCostMult={studyCostMult}
/>
</DebugName>
</TabsContent> </TabsContent>
<TabsContent value="grimoire"> <TabsContent value="grimoire">
{renderGrimoireTab()} <DebugName name="GrimoireTab">
{renderGrimoireTab()}
</DebugName>
</TabsContent> </TabsContent>
<TabsContent value="debug"> <TabsContent value="debug">
<DebugTab store={store} /> <DebugName name="DebugTab">
<DebugTab store={store} />
</DebugName>
</TabsContent> </TabsContent>
</Tabs> </Tabs>
</div> </div>

View File

@@ -33,9 +33,9 @@ export function ManaDisplay({
}: ManaDisplayProps) { }: ManaDisplayProps) {
const [expanded, setExpanded] = useState(true); const [expanded, setExpanded] = useState(true);
// Get unlocked elements sorted by current amount // Get unlocked elements with current > 0, sorted by current amount
const unlockedElements = Object.entries(elements) const unlockedElements = Object.entries(elements)
.filter(([, state]) => state.unlocked) .filter(([, state]) => state.unlocked && state.current > 0)
.sort((a, b) => b[1].current - a[1].current); .sort((a, b) => b[1].current - a[1].current);
return ( return (

View File

@@ -144,26 +144,6 @@ export function CraftingTab({ store }: CraftingTabProps) {
} }
}; };
// Complete design after progress
const handleCompleteDesign = () => {
if (!designProgress || !selectedEquipmentType) return;
const design: EnchantmentDesign = {
id: designProgress.designId,
name: designName || 'Untitled Design',
equipmentType: selectedEquipmentType,
effects: selectedEffects,
totalCapacityUsed: designCapacityCost,
designTime,
created: Date.now(),
};
saveDesign(design);
setDesignName('');
setSelectedEquipmentType(null);
setSelectedEffects([]);
};
// Get available effects for selected equipment type (only unlocked ones) // Get available effects for selected equipment type (only unlocked ones)
const getAvailableEffects = () => { const getAvailableEffects = () => {
if (!selectedEquipmentType) return []; if (!selectedEquipmentType) return [];
@@ -188,15 +168,15 @@ export function CraftingTab({ store }: CraftingTabProps) {
<CardContent> <CardContent>
{designProgress ? ( {designProgress ? (
<div className="space-y-3"> <div className="space-y-3">
<div className="text-sm text-gray-400">Designing for: {EQUIPMENT_TYPES[selectedEquipmentType || '']?.name}</div> <div className="text-sm text-gray-400">
Designing for: {EQUIPMENT_TYPES[designProgress.equipmentType]?.name}
</div>
<div className="text-sm font-semibold text-amber-300">{designProgress.name}</div>
<Progress value={(designProgress.progress / designProgress.required) * 100} className="h-3" /> <Progress value={(designProgress.progress / designProgress.required) * 100} className="h-3" />
<div className="flex justify-between text-xs text-gray-400"> <div className="flex justify-between text-xs text-gray-400">
<span>{designProgress.progress.toFixed(1)}h / {designProgress.required.toFixed(1)}h</span> <span>{designProgress.progress.toFixed(1)}h / {designProgress.required.toFixed(1)}h</span>
<Button size="sm" variant="outline" onClick={cancelDesign}>Cancel</Button> <Button size="sm" variant="outline" onClick={cancelDesign}>Cancel</Button>
</div> </div>
{designProgress.progress >= designProgress.required && (
<Button onClick={handleCompleteDesign} className="w-full">Complete Design</Button>
)}
</div> </div>
) : ( ) : (
<ScrollArea className="h-64"> <ScrollArea className="h-64">
@@ -235,7 +215,7 @@ export function CraftingTab({ store }: CraftingTabProps) {
) : designProgress ? ( ) : designProgress ? (
<div className="space-y-2"> <div className="space-y-2">
<div className="text-sm text-gray-400">Design in progress...</div> <div className="text-sm text-gray-400">Design in progress...</div>
{selectedEffects.map(eff => { {designProgress.effects.map(eff => {
const def = ENCHANTMENT_EFFECTS[eff.effectId]; const def = ENCHANTMENT_EFFECTS[eff.effectId];
return ( return (
<div key={eff.effectId} className="flex justify-between text-sm"> <div key={eff.effectId} className="flex justify-between text-sm">

View File

@@ -5,14 +5,17 @@ import { Button } from '@/components/ui/button';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Badge } from '@/components/ui/badge'; import { Badge } from '@/components/ui/badge';
import { Separator } from '@/components/ui/separator'; import { Separator } from '@/components/ui/separator';
import { Switch } from '@/components/ui/switch';
import { Label } from '@/components/ui/label';
import { import {
RotateCcw, Bug, Plus, Minus, Lock, Unlock, Zap, RotateCcw, Bug, Plus, Minus, Lock, Unlock, Zap,
Clock, Star, AlertTriangle, Sparkles, Settings Clock, Star, AlertTriangle, Sparkles, Settings, Eye
} from 'lucide-react'; } from 'lucide-react';
import type { GameStore } from '@/lib/game/types'; import type { GameStore } from '@/lib/game/types';
import { ATTUNEMENTS_DEF } from '@/lib/game/data/attunements'; import { ATTUNEMENTS_DEF } from '@/lib/game/data/attunements';
import { ELEMENTS } from '@/lib/game/constants'; import { ELEMENTS } from '@/lib/game/constants';
import { fmt } from '@/lib/game/store'; import { fmt } from '@/lib/game/store';
import { useDebug } from '@/lib/game/debug-context';
interface DebugTabProps { interface DebugTabProps {
store: GameStore; store: GameStore;
@@ -20,6 +23,7 @@ interface DebugTabProps {
export function DebugTab({ store }: DebugTabProps) { export function DebugTab({ store }: DebugTabProps) {
const [confirmReset, setConfirmReset] = useState(false); const [confirmReset, setConfirmReset] = useState(false);
const { showComponentNames, toggleComponentNames } = useDebug();
const handleReset = () => { const handleReset = () => {
if (confirmReset) { if (confirmReset) {
@@ -86,6 +90,31 @@ export function DebugTab({ store }: DebugTabProps) {
</CardContent> </CardContent>
</Card> </Card>
{/* Display Options */}
<Card className="bg-gray-900/80 border-gray-700">
<CardHeader className="pb-2">
<CardTitle className="text-cyan-400 text-sm flex items-center gap-2">
<Eye className="w-4 h-4" />
Display Options
</CardTitle>
</CardHeader>
<CardContent>
<div className="flex items-center justify-between">
<div className="space-y-0.5">
<Label htmlFor="show-component-names" className="text-sm">Show Component Names</Label>
<p className="text-xs text-gray-400">
Display component names at the top of each component for debugging
</p>
</div>
<Switch
id="show-component-names"
checked={showComponentNames}
onCheckedChange={toggleComponentNames}
/>
</div>
</CardContent>
</Card>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4"> <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{/* Game Reset */} {/* Game Reset */}
<Card className="bg-gray-900/80 border-gray-700"> <Card className="bg-gray-900/80 border-gray-700">
@@ -371,6 +400,18 @@ export function DebugTab({ store }: DebugTabProps) {
> >
Skip to Floor 100 Skip to Floor 100
</Button> </Button>
<Button
size="sm"
variant="outline"
onClick={() => {
// Reset floor HP
if (store.resetFloorHP) {
store.resetFloorHP();
}
}}
>
Reset Floor HP
</Button>
</div> </div>
</CardContent> </CardContent>
</Card> </Card>

View File

@@ -14,11 +14,11 @@ interface LabTabProps {
} }
export function LabTab({ store }: LabTabProps) { export function LabTab({ store }: LabTabProps) {
// Render elemental mana grid // Render elemental mana grid - only show elements with current > 0
const renderElementsGrid = () => ( const renderElementsGrid = () => (
<div className="grid grid-cols-3 sm:grid-cols-4 md:grid-cols-6 lg:grid-cols-8 gap-2"> <div className="grid grid-cols-3 sm:grid-cols-4 md:grid-cols-6 lg:grid-cols-8 gap-2">
{Object.entries(store.elements) {Object.entries(store.elements)
.filter(([, state]) => state.unlocked) .filter(([, state]) => state.unlocked && state.current > 0)
.map(([id, state]) => { .map(([id, state]) => {
const def = ELEMENTS[id]; const def = ELEMENTS[id];
return ( return (
@@ -82,15 +82,15 @@ export function LabTab({ store }: LabTabProps) {
); );
}; };
// Check if there are any unlocked elements // Check if there are any unlocked elements with current > 0
const hasUnlockedElements = Object.values(store.elements).some(e => e.unlocked); const hasUnlockedElements = Object.values(store.elements).some(e => e.unlocked && e.current > 0);
if (!hasUnlockedElements) { if (!hasUnlockedElements) {
return ( return (
<Card className="bg-gray-900/80 border-gray-700"> <Card className="bg-gray-900/80 border-gray-700">
<CardContent className="pt-6"> <CardContent className="pt-6">
<div className="text-center text-gray-500"> <div className="text-center text-gray-500">
No elemental mana available. Elements are unlocked through gameplay. No elemental mana available. Gather or convert mana to see elemental pools.
</div> </div>
</CardContent> </CardContent>
</Card> </Card>

View File

@@ -289,24 +289,20 @@ export function createCraftingSlice(
const efficiencyBonus = (state.skills.efficientEnchant || 0) * 0.05; const efficiencyBonus = (state.skills.efficientEnchant || 0) * 0.05;
const totalCapacityCost = calculateDesignCapacityCost(effects, efficiencyBonus); const totalCapacityCost = calculateDesignCapacityCost(effects, efficiencyBonus);
// Create design // Create design ID
const designId = `design_${Date.now()}`;
const designTime = calculateDesignTime(effects); const designTime = calculateDesignTime(effects);
const design: EnchantmentDesign = {
id: `design_${Date.now()}`,
name,
equipmentType: equipmentTypeId,
effects,
totalCapacityUsed: totalCapacityCost,
designTime,
created: Date.now(),
};
// Store design data in progress
set(() => ({ set(() => ({
currentAction: 'design', currentAction: 'design',
designProgress: { designProgress: {
designId: design.id, designId,
progress: 0, progress: 0,
required: designTime, required: designTime,
name,
equipmentType: equipmentTypeId,
effects,
}, },
})); }));
@@ -616,11 +612,27 @@ export function processCraftingTick(
if (state.currentAction === 'design' && state.designProgress) { if (state.currentAction === 'design' && state.designProgress) {
const progress = state.designProgress.progress + 0.04; // HOURS_PER_TICK const progress = state.designProgress.progress + 0.04; // HOURS_PER_TICK
if (progress >= state.designProgress.required) { if (progress >= state.designProgress.required) {
// Design complete - but we need the design data to save it // Design complete - auto-save the design using stored data
// This will be handled by the UI calling saveDesign const dp = state.designProgress;
const efficiencyBonus = (state.skills.efficientEnchant || 0) * 0.05;
const totalCapacityCost = calculateDesignCapacityCost(dp.effects, efficiencyBonus);
const completedDesign: EnchantmentDesign = {
id: dp.designId,
name: dp.name,
equipmentType: dp.equipmentType,
effects: dp.effects,
totalCapacityUsed: totalCapacityCost,
designTime: dp.required,
created: Date.now(),
};
updates = { updates = {
...updates, ...updates,
log: ['✅ Enchantment design complete!', ...log], designProgress: null,
currentAction: 'meditate',
enchantmentDesigns: [...state.enchantmentDesigns, completedDesign],
log: [`✅ Enchantment design "${dp.name}" complete!`, ...log],
}; };
} else { } else {
updates = { updates = {

View File

@@ -0,0 +1,67 @@
'use client';
import { createContext, useContext, useState, useEffect, type ReactNode } from 'react';
interface DebugContextType {
showComponentNames: boolean;
toggleComponentNames: () => void;
}
const DebugContext = createContext<DebugContextType | null>(null);
export function DebugProvider({ children }: { children: ReactNode }) {
// Initialize from localStorage if available
const [showComponentNames, setShowComponentNames] = useState(() => {
if (typeof window !== 'undefined') {
const saved = localStorage.getItem('debug-show-component-names');
return saved === 'true';
}
return false;
});
const toggleComponentNames = () => {
setShowComponentNames(prev => {
const newValue = !prev;
localStorage.setItem('debug-show-component-names', String(newValue));
return newValue;
});
};
return (
<DebugContext.Provider value={{ showComponentNames, toggleComponentNames }}>
{children}
</DebugContext.Provider>
);
}
export function useDebug() {
const context = useContext(DebugContext);
if (!context) {
// Return default values if used outside provider
return { showComponentNames: false, toggleComponentNames: () => {} };
}
return context;
}
// Wrapper component to show component name in debug mode
interface DebugNameProps {
name: string;
children: ReactNode;
}
export function DebugName({ name, children }: DebugNameProps) {
const { showComponentNames } = useDebug();
if (!showComponentNames) {
return <>{children}</>;
}
return (
<div className="relative">
<div className="absolute -top-5 left-0 text-[10px] font-mono text-yellow-400 bg-yellow-900/50 px-1 rounded z-50">
{name}
</div>
{children}
</div>
);
}

View File

@@ -10,6 +10,7 @@ export interface NavigationActions {
// Floor Navigation // Floor Navigation
setClimbDirection: (direction: 'up' | 'down') => void; setClimbDirection: (direction: 'up' | 'down') => void;
changeFloor: (direction: 'up' | 'down') => void; changeFloor: (direction: 'up' | 'down') => void;
resetFloorHP: () => void;
} }
// ─── Navigation Slice Factory ───────────────────────────────────────────────── // ─── Navigation Slice Factory ─────────────────────────────────────────────────
@@ -59,5 +60,16 @@ export function createNavigationSlice(
log: [`🚶 Moved to floor ${nextFloor}${nextFloorCleared ? ' (respawned)' : ''}.`, ...state.log.slice(0, 49)], log: [`🚶 Moved to floor ${nextFloor}${nextFloorCleared ? ' (respawned)' : ''}.`, ...state.log.slice(0, 49)],
}); });
}, },
// Reset current floor HP to max (useful when floor HP gets stuck)
resetFloorHP: () => {
const state = get();
const maxHP = getFloorMaxHP(state.currentFloor);
set({
floorMaxHP: maxHP,
floorHP: maxHP,
log: [`🔄 Floor ${state.currentFloor} HP reset to full.`, ...state.log.slice(0, 49)],
});
},
}; };
} }

View File

@@ -578,6 +578,7 @@ interface GameStore extends GameState, CraftingActions {
debugSetTime: (day: number, hour: number) => void; debugSetTime: (day: number, hour: number) => void;
debugAddAttunementXP: (attunementId: string, amount: number) => void; debugAddAttunementXP: (attunementId: string, amount: number) => void;
debugSetFloor: (floor: number) => void; debugSetFloor: (floor: number) => void;
resetFloorHP: () => void;
// Computed getters // Computed getters
getMaxMana: () => number; getMaxMana: () => number;
@@ -1463,26 +1464,26 @@ export const useGameStore = create<GameStore>()(
if (eff.stacks > effectDef.maxStacks) return false; if (eff.stacks > effectDef.maxStacks) return false;
} }
// Calculate capacity cost
const efficiencyBonus = (state.skills.efficientEnchant || 0) * 0.05;
const totalCapacityCost = effects.reduce((total, eff) =>
total + calculateEffectCapacityCost(eff.effectId, eff.stacks, efficiencyBonus), 0);
// Calculate design time // Calculate design time
let designTime = 1; let designTime = 1;
for (const eff of effects) { for (const eff of effects) {
designTime += 0.5 * eff.stacks; designTime += 0.5 * eff.stacks;
} }
// Store pending design in designProgress // Create design ID
const designId = `design_${Date.now()}`;
// Store design data in progress
set(() => ({ set(() => ({
currentAction: 'design', currentAction: 'design',
designProgress: { designProgress: {
designId: `design_${Date.now()}`, designId,
progress: 0, progress: 0,
required: designTime, required: designTime,
name,
equipmentType: equipmentTypeId,
effects,
}, },
// Store design data temporarily
log: [`🔮 Designing enchantment: ${name}...`, ...state.log.slice(0, 49)], log: [`🔮 Designing enchantment: ${name}...`, ...state.log.slice(0, 49)],
})); }));
@@ -1816,6 +1817,16 @@ export const useGameStore = create<GameStore>()(
maxFloorReached: Math.max(state.maxFloorReached, floor), maxFloorReached: Math.max(state.maxFloorReached, floor),
}); });
}, },
resetFloorHP: () => {
const state = get();
const maxHP = getFloorMaxHP(state.currentFloor);
set({
floorMaxHP: maxHP,
floorHP: maxHP,
log: [`🔄 Floor ${state.currentFloor} HP reset to full.`, ...state.log.slice(0, 49)],
});
},
}), }),
{ {
name: 'mana-loop-storage', name: 'mana-loop-storage',

View File

@@ -213,6 +213,10 @@ export interface DesignProgress {
designId: string; designId: string;
progress: number; // Hours spent designing progress: number; // Hours spent designing
required: number; // Total hours needed required: number; // Total hours needed
// Design data stored during progress
name: string;
equipmentType: string;
effects: DesignEffect[];
} }
export interface PreparationProgress { export interface PreparationProgress {