diff --git a/.zscripts/dev.out.log b/.zscripts/dev.out.log index 53bf133..70d2d90 100755 --- a/.zscripts/dev.out.log +++ b/.zscripts/dev.out.log @@ -1225,3 +1225,25 @@ Checked 846 installs across 915 packages (no changes) [60.00ms] $ 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 +========================================== +[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 diff --git a/.zscripts/dev.pid b/.zscripts/dev.pid index e5a0177..615d0e0 100755 --- a/.zscripts/dev.pid +++ b/.zscripts/dev.pid @@ -1 +1 @@ -488 +486 diff --git a/src/app/layout.tsx b/src/app/layout.tsx index e9179cf..6792fac 100755 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -2,6 +2,7 @@ import type { Metadata } from "next"; import { Geist, Geist_Mono } from "next/font/google"; import "./globals.css"; import { Toaster } from "@/components/ui/toaster"; +import { DebugProvider } from "@/lib/game/debug-context"; const geistSans = Geist({ variable: "--font-geist-sans", @@ -14,25 +15,10 @@ const geistMono = Geist_Mono({ }); export const metadata: Metadata = { - title: "Z.ai Code Scaffold - AI-Powered Development", - description: "Modern Next.js scaffold optimized for AI-powered development with Z.ai. Built with TypeScript, Tailwind CSS, and shadcn/ui.", - keywords: ["Z.ai", "Next.js", "TypeScript", "Tailwind CSS", "shadcn/ui", "AI development", "React"], - authors: [{ name: "Z.ai 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", - }, + title: "Mana Loop", + description: "A time-loop incremental game where you climb the Spire and sign pacts with guardians.", + keywords: ["Mana Loop", "incremental game", "idle game", "time loop"], + authors: [{ name: "Mana Loop Team" }], }; export default function RootLayout({ @@ -45,7 +31,9 @@ export default function RootLayout({ - {children} + + {children} + diff --git a/src/app/page.tsx b/src/app/page.tsx index 3122dd2..3aeaba4 100755 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -17,6 +17,7 @@ import { CraftingTab, SpireTab, SpellsTab, LabTab, SkillsTab, StatsTab, Equipmen import { ActionButtons, CalendarDisplay, ManaDisplay, TimeDisplay } from '@/components/game'; import { LootInventoryDisplay } from '@/components/game/LootInventory'; import { AchievementsDisplay } from '@/components/game/AchievementsDisplay'; +import { DebugName } from '@/lib/game/debug-context'; export default function ManaLoopGame() { const [activeTab, setActiveTab] = useState('spire'); @@ -175,56 +176,66 @@ export default function ManaLoopGame() { {/* Left Panel - Mana & Actions */}
{/* Mana Display */} - - + + + + {/* Action Buttons */} - - + + + + {/* Calendar */} - - + + + + {/* Loot Inventory */} - - + + + + {/* Achievements */} - + + +
{/* Right Panel - Tabs */} @@ -242,57 +253,77 @@ export default function ManaLoopGame() { 🔧 Debug 📖 Grimoire - + - + + + - + - + + + - + - + + + - + - + + + - + - + + + - + - + + + - + + + - + - + + + - + - {renderGrimoireTab()} + + {renderGrimoireTab()} + - + - + + + diff --git a/src/components/game/ManaDisplay.tsx b/src/components/game/ManaDisplay.tsx index ed0dc67..9f9b520 100755 --- a/src/components/game/ManaDisplay.tsx +++ b/src/components/game/ManaDisplay.tsx @@ -33,9 +33,9 @@ export function ManaDisplay({ }: ManaDisplayProps) { 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) - .filter(([, state]) => state.unlocked) + .filter(([, state]) => state.unlocked && state.current > 0) .sort((a, b) => b[1].current - a[1].current); return ( diff --git a/src/components/game/tabs/CraftingTab.tsx b/src/components/game/tabs/CraftingTab.tsx index ff5642a..aeb8182 100755 --- a/src/components/game/tabs/CraftingTab.tsx +++ b/src/components/game/tabs/CraftingTab.tsx @@ -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) const getAvailableEffects = () => { if (!selectedEquipmentType) return []; @@ -188,15 +168,15 @@ export function CraftingTab({ store }: CraftingTabProps) { {designProgress ? (
-
Designing for: {EQUIPMENT_TYPES[selectedEquipmentType || '']?.name}
+
+ Designing for: {EQUIPMENT_TYPES[designProgress.equipmentType]?.name} +
+
{designProgress.name}
{designProgress.progress.toFixed(1)}h / {designProgress.required.toFixed(1)}h
- {designProgress.progress >= designProgress.required && ( - - )}
) : ( @@ -235,7 +215,7 @@ export function CraftingTab({ store }: CraftingTabProps) { ) : designProgress ? (
Design in progress...
- {selectedEffects.map(eff => { + {designProgress.effects.map(eff => { const def = ENCHANTMENT_EFFECTS[eff.effectId]; return (
diff --git a/src/components/game/tabs/DebugTab.tsx b/src/components/game/tabs/DebugTab.tsx index 9856402..8d00583 100755 --- a/src/components/game/tabs/DebugTab.tsx +++ b/src/components/game/tabs/DebugTab.tsx @@ -5,14 +5,17 @@ import { Button } from '@/components/ui/button'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Badge } from '@/components/ui/badge'; import { Separator } from '@/components/ui/separator'; +import { Switch } from '@/components/ui/switch'; +import { Label } from '@/components/ui/label'; import { RotateCcw, Bug, Plus, Minus, Lock, Unlock, Zap, - Clock, Star, AlertTriangle, Sparkles, Settings + Clock, Star, AlertTriangle, Sparkles, Settings, Eye } from 'lucide-react'; import type { GameStore } from '@/lib/game/types'; import { ATTUNEMENTS_DEF } from '@/lib/game/data/attunements'; import { ELEMENTS } from '@/lib/game/constants'; import { fmt } from '@/lib/game/store'; +import { useDebug } from '@/lib/game/debug-context'; interface DebugTabProps { store: GameStore; @@ -20,6 +23,7 @@ interface DebugTabProps { export function DebugTab({ store }: DebugTabProps) { const [confirmReset, setConfirmReset] = useState(false); + const { showComponentNames, toggleComponentNames } = useDebug(); const handleReset = () => { if (confirmReset) { @@ -86,6 +90,31 @@ export function DebugTab({ store }: DebugTabProps) { + {/* Display Options */} + + + + + Display Options + + + +
+
+ +

+ Display component names at the top of each component for debugging +

+
+ +
+
+
+
{/* Game Reset */} @@ -371,6 +400,18 @@ export function DebugTab({ store }: DebugTabProps) { > Skip to Floor 100 +
diff --git a/src/components/game/tabs/LabTab.tsx b/src/components/game/tabs/LabTab.tsx index cd17c0d..38e1e49 100755 --- a/src/components/game/tabs/LabTab.tsx +++ b/src/components/game/tabs/LabTab.tsx @@ -14,11 +14,11 @@ interface LabTabProps { } export function LabTab({ store }: LabTabProps) { - // Render elemental mana grid + // Render elemental mana grid - only show elements with current > 0 const renderElementsGrid = () => (
{Object.entries(store.elements) - .filter(([, state]) => state.unlocked) + .filter(([, state]) => state.unlocked && state.current > 0) .map(([id, state]) => { const def = ELEMENTS[id]; return ( @@ -82,15 +82,15 @@ export function LabTab({ store }: LabTabProps) { ); }; - // Check if there are any unlocked elements - const hasUnlockedElements = Object.values(store.elements).some(e => e.unlocked); + // Check if there are any unlocked elements with current > 0 + const hasUnlockedElements = Object.values(store.elements).some(e => e.unlocked && e.current > 0); if (!hasUnlockedElements) { return (
- No elemental mana available. Elements are unlocked through gameplay. + No elemental mana available. Gather or convert mana to see elemental pools.
diff --git a/src/lib/game/crafting-slice.ts b/src/lib/game/crafting-slice.ts index ac95d1e..fb36753 100755 --- a/src/lib/game/crafting-slice.ts +++ b/src/lib/game/crafting-slice.ts @@ -289,24 +289,20 @@ export function createCraftingSlice( const efficiencyBonus = (state.skills.efficientEnchant || 0) * 0.05; const totalCapacityCost = calculateDesignCapacityCost(effects, efficiencyBonus); - // Create design + // Create design ID + const designId = `design_${Date.now()}`; 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(() => ({ currentAction: 'design', designProgress: { - designId: design.id, + designId, progress: 0, required: designTime, + name, + equipmentType: equipmentTypeId, + effects, }, })); @@ -616,11 +612,27 @@ export function processCraftingTick( if (state.currentAction === 'design' && state.designProgress) { const progress = state.designProgress.progress + 0.04; // HOURS_PER_TICK if (progress >= state.designProgress.required) { - // Design complete - but we need the design data to save it - // This will be handled by the UI calling saveDesign + // Design complete - auto-save the design using stored data + 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, - log: ['✅ Enchantment design complete!', ...log], + designProgress: null, + currentAction: 'meditate', + enchantmentDesigns: [...state.enchantmentDesigns, completedDesign], + log: [`✅ Enchantment design "${dp.name}" complete!`, ...log], }; } else { updates = { diff --git a/src/lib/game/debug-context.tsx b/src/lib/game/debug-context.tsx new file mode 100644 index 0000000..ef3c0b7 --- /dev/null +++ b/src/lib/game/debug-context.tsx @@ -0,0 +1,67 @@ +'use client'; + +import { createContext, useContext, useState, useEffect, type ReactNode } from 'react'; + +interface DebugContextType { + showComponentNames: boolean; + toggleComponentNames: () => void; +} + +const DebugContext = createContext(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 ( + + {children} + + ); +} + +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 ( +
+
+ {name} +
+ {children} +
+ ); +} diff --git a/src/lib/game/navigation-slice.ts b/src/lib/game/navigation-slice.ts index dc254f2..0b5fd77 100755 --- a/src/lib/game/navigation-slice.ts +++ b/src/lib/game/navigation-slice.ts @@ -10,6 +10,7 @@ export interface NavigationActions { // Floor Navigation setClimbDirection: (direction: 'up' | 'down') => void; changeFloor: (direction: 'up' | 'down') => void; + resetFloorHP: () => void; } // ─── Navigation Slice Factory ───────────────────────────────────────────────── @@ -59,5 +60,16 @@ export function createNavigationSlice( 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)], + }); + }, }; } diff --git a/src/lib/game/store.ts b/src/lib/game/store.ts index e7caea9..01dbfaa 100755 --- a/src/lib/game/store.ts +++ b/src/lib/game/store.ts @@ -578,6 +578,7 @@ interface GameStore extends GameState, CraftingActions { debugSetTime: (day: number, hour: number) => void; debugAddAttunementXP: (attunementId: string, amount: number) => void; debugSetFloor: (floor: number) => void; + resetFloorHP: () => void; // Computed getters getMaxMana: () => number; @@ -1463,26 +1464,26 @@ export const useGameStore = create()( 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 let designTime = 1; for (const eff of effects) { designTime += 0.5 * eff.stacks; } - // Store pending design in designProgress + // Create design ID + const designId = `design_${Date.now()}`; + + // Store design data in progress set(() => ({ currentAction: 'design', designProgress: { - designId: `design_${Date.now()}`, + designId, progress: 0, required: designTime, + name, + equipmentType: equipmentTypeId, + effects, }, - // Store design data temporarily log: [`🔮 Designing enchantment: ${name}...`, ...state.log.slice(0, 49)], })); @@ -1816,6 +1817,16 @@ export const useGameStore = create()( 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', diff --git a/src/lib/game/types.ts b/src/lib/game/types.ts index 8b4e6fd..9201fc6 100755 --- a/src/lib/game/types.ts +++ b/src/lib/game/types.ts @@ -213,6 +213,10 @@ export interface DesignProgress { designId: string; progress: number; // Hours spent designing required: number; // Total hours needed + // Design data stored during progress + name: string; + equipmentType: string; + effects: DesignEffect[]; } export interface PreparationProgress {