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
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
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
488
|
486
|
||||||
|
|||||||
@@ -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`}
|
||||||
>
|
>
|
||||||
|
<DebugProvider>
|
||||||
{children}
|
{children}
|
||||||
|
</DebugProvider>
|
||||||
<Toaster />
|
<Toaster />
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -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,6 +176,7 @@ 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 */}
|
||||||
|
<DebugName name="ManaDisplay">
|
||||||
<ManaDisplay
|
<ManaDisplay
|
||||||
rawMana={store.rawMana}
|
rawMana={store.rawMana}
|
||||||
maxMana={maxMana}
|
maxMana={maxMana}
|
||||||
@@ -186,8 +188,10 @@ export default function ManaLoopGame() {
|
|||||||
onGatherEnd={handleGatherEnd}
|
onGatherEnd={handleGatherEnd}
|
||||||
elements={store.elements}
|
elements={store.elements}
|
||||||
/>
|
/>
|
||||||
|
</DebugName>
|
||||||
|
|
||||||
{/* Action Buttons */}
|
{/* Action Buttons */}
|
||||||
|
<DebugName name="ActionButtons">
|
||||||
<ActionButtons
|
<ActionButtons
|
||||||
currentAction={store.currentAction}
|
currentAction={store.currentAction}
|
||||||
designProgress={store.designProgress}
|
designProgress={store.designProgress}
|
||||||
@@ -195,15 +199,19 @@ export default function ManaLoopGame() {
|
|||||||
applicationProgress={store.applicationProgress}
|
applicationProgress={store.applicationProgress}
|
||||||
setAction={store.setAction}
|
setAction={store.setAction}
|
||||||
/>
|
/>
|
||||||
|
</DebugName>
|
||||||
|
|
||||||
{/* Calendar */}
|
{/* Calendar */}
|
||||||
|
<DebugName name="CalendarDisplay">
|
||||||
<CalendarDisplay
|
<CalendarDisplay
|
||||||
day={store.day}
|
day={store.day}
|
||||||
hour={store.hour}
|
hour={store.hour}
|
||||||
incursionStrength={incursionStrength}
|
incursionStrength={incursionStrength}
|
||||||
/>
|
/>
|
||||||
|
</DebugName>
|
||||||
|
|
||||||
{/* Loot Inventory */}
|
{/* Loot Inventory */}
|
||||||
|
<DebugName name="LootInventoryDisplay">
|
||||||
<LootInventoryDisplay
|
<LootInventoryDisplay
|
||||||
inventory={store.lootInventory}
|
inventory={store.lootInventory}
|
||||||
elements={store.elements}
|
elements={store.elements}
|
||||||
@@ -211,8 +219,10 @@ export default function ManaLoopGame() {
|
|||||||
onDeleteMaterial={store.deleteMaterial}
|
onDeleteMaterial={store.deleteMaterial}
|
||||||
onDeleteEquipment={store.deleteEquipmentInstance}
|
onDeleteEquipment={store.deleteEquipmentInstance}
|
||||||
/>
|
/>
|
||||||
|
</DebugName>
|
||||||
|
|
||||||
{/* Achievements */}
|
{/* Achievements */}
|
||||||
|
<DebugName name="AchievementsDisplay">
|
||||||
<AchievementsDisplay
|
<AchievementsDisplay
|
||||||
achievements={store.achievements}
|
achievements={store.achievements}
|
||||||
gameState={{
|
gameState={{
|
||||||
@@ -225,6 +235,7 @@ export default function ManaLoopGame() {
|
|||||||
combo: store.combo,
|
combo: store.combo,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
</DebugName>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Right Panel - Tabs */}
|
{/* Right Panel - Tabs */}
|
||||||
@@ -244,34 +255,49 @@ export default function ManaLoopGame() {
|
|||||||
</TabsList>
|
</TabsList>
|
||||||
|
|
||||||
<TabsContent value="spire">
|
<TabsContent value="spire">
|
||||||
|
<DebugName name="SpireTab">
|
||||||
<SpireTab store={store} />
|
<SpireTab store={store} />
|
||||||
|
</DebugName>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|
||||||
<TabsContent value="attunements">
|
<TabsContent value="attunements">
|
||||||
|
<DebugName name="AttunementsTab">
|
||||||
<AttunementsTab store={store} />
|
<AttunementsTab store={store} />
|
||||||
|
</DebugName>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|
||||||
<TabsContent value="skills">
|
<TabsContent value="skills">
|
||||||
|
<DebugName name="SkillsTab">
|
||||||
<SkillsTab store={store} />
|
<SkillsTab store={store} />
|
||||||
|
</DebugName>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|
||||||
<TabsContent value="spells">
|
<TabsContent value="spells">
|
||||||
|
<DebugName name="SpellsTab">
|
||||||
<SpellsTab store={store} />
|
<SpellsTab store={store} />
|
||||||
|
</DebugName>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|
||||||
<TabsContent value="equipment">
|
<TabsContent value="equipment">
|
||||||
|
<DebugName name="EquipmentTab">
|
||||||
<EquipmentTab store={store} />
|
<EquipmentTab store={store} />
|
||||||
|
</DebugName>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|
||||||
<TabsContent value="crafting">
|
<TabsContent value="crafting">
|
||||||
|
<DebugName name="CraftingTab">
|
||||||
<CraftingTab store={store} />
|
<CraftingTab store={store} />
|
||||||
|
</DebugName>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|
||||||
<TabsContent value="lab">
|
<TabsContent value="lab">
|
||||||
|
<DebugName name="LabTab">
|
||||||
<LabTab store={store} />
|
<LabTab store={store} />
|
||||||
|
</DebugName>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|
||||||
<TabsContent value="stats">
|
<TabsContent value="stats">
|
||||||
|
<DebugName name="StatsTab">
|
||||||
<StatsTab
|
<StatsTab
|
||||||
store={store}
|
store={store}
|
||||||
upgradeEffects={upgradeEffects}
|
upgradeEffects={upgradeEffects}
|
||||||
@@ -285,14 +311,19 @@ export default function ManaLoopGame() {
|
|||||||
studySpeedMult={studySpeedMult}
|
studySpeedMult={studySpeedMult}
|
||||||
studyCostMult={studyCostMult}
|
studyCostMult={studyCostMult}
|
||||||
/>
|
/>
|
||||||
|
</DebugName>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|
||||||
<TabsContent value="grimoire">
|
<TabsContent value="grimoire">
|
||||||
|
<DebugName name="GrimoireTab">
|
||||||
{renderGrimoireTab()}
|
{renderGrimoireTab()}
|
||||||
|
</DebugName>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|
||||||
<TabsContent value="debug">
|
<TabsContent value="debug">
|
||||||
|
<DebugName name="DebugTab">
|
||||||
<DebugTab store={store} />
|
<DebugTab store={store} />
|
||||||
|
</DebugName>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -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 (
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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 = {
|
||||||
|
|||||||
67
src/lib/game/debug-context.tsx
Normal file
67
src/lib/game/debug-context.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -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)],
|
||||||
|
});
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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',
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user