Compare commits
2 Commits
3e915321a9
...
300e43f8be
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
300e43f8be | ||
|
|
6dc301bd7b |
@@ -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
|
||||
|
||||
@@ -1 +1 @@
|
||||
488
|
||||
486
|
||||
|
||||
0
GAME_SYSTEMS_ANALYSIS.md
Normal file → Executable file
0
GAME_SYSTEMS_ANALYSIS.md
Normal file → Executable file
@@ -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({
|
||||
<body
|
||||
className={`${geistSans.variable} ${geistMono.variable} antialiased bg-background text-foreground`}
|
||||
>
|
||||
{children}
|
||||
<DebugProvider>
|
||||
{children}
|
||||
</DebugProvider>
|
||||
<Toaster />
|
||||
</body>
|
||||
</html>
|
||||
|
||||
185
src/app/page.tsx
185
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 */}
|
||||
<div className="md:w-80 space-y-4 flex-shrink-0">
|
||||
{/* Mana Display */}
|
||||
<ManaDisplay
|
||||
rawMana={store.rawMana}
|
||||
maxMana={maxMana}
|
||||
effectiveRegen={effectiveRegen}
|
||||
meditationMultiplier={meditationMultiplier}
|
||||
clickMana={clickMana}
|
||||
isGathering={isGathering}
|
||||
onGatherStart={handleGatherStart}
|
||||
onGatherEnd={handleGatherEnd}
|
||||
elements={store.elements}
|
||||
/>
|
||||
|
||||
<DebugName name="ManaDisplay">
|
||||
<ManaDisplay
|
||||
rawMana={store.rawMana}
|
||||
maxMana={maxMana}
|
||||
effectiveRegen={effectiveRegen}
|
||||
meditationMultiplier={meditationMultiplier}
|
||||
clickMana={clickMana}
|
||||
isGathering={isGathering}
|
||||
onGatherStart={handleGatherStart}
|
||||
onGatherEnd={handleGatherEnd}
|
||||
elements={store.elements}
|
||||
/>
|
||||
</DebugName>
|
||||
|
||||
{/* Action Buttons */}
|
||||
<ActionButtons
|
||||
currentAction={store.currentAction}
|
||||
designProgress={store.designProgress}
|
||||
preparationProgress={store.preparationProgress}
|
||||
applicationProgress={store.applicationProgress}
|
||||
setAction={store.setAction}
|
||||
/>
|
||||
|
||||
<DebugName name="ActionButtons">
|
||||
<ActionButtons
|
||||
currentAction={store.currentAction}
|
||||
designProgress={store.designProgress}
|
||||
preparationProgress={store.preparationProgress}
|
||||
applicationProgress={store.applicationProgress}
|
||||
setAction={store.setAction}
|
||||
/>
|
||||
</DebugName>
|
||||
|
||||
{/* Calendar */}
|
||||
<CalendarDisplay
|
||||
day={store.day}
|
||||
hour={store.hour}
|
||||
incursionStrength={incursionStrength}
|
||||
/>
|
||||
|
||||
<DebugName name="CalendarDisplay">
|
||||
<CalendarDisplay
|
||||
day={store.day}
|
||||
hour={store.hour}
|
||||
incursionStrength={incursionStrength}
|
||||
/>
|
||||
</DebugName>
|
||||
|
||||
{/* Loot Inventory */}
|
||||
<LootInventoryDisplay
|
||||
inventory={store.lootInventory}
|
||||
elements={store.elements}
|
||||
equipmentInstances={store.equipmentInstances}
|
||||
onDeleteMaterial={store.deleteMaterial}
|
||||
onDeleteEquipment={store.deleteEquipmentInstance}
|
||||
/>
|
||||
|
||||
<DebugName name="LootInventoryDisplay">
|
||||
<LootInventoryDisplay
|
||||
inventory={store.lootInventory}
|
||||
elements={store.elements}
|
||||
equipmentInstances={store.equipmentInstances}
|
||||
onDeleteMaterial={store.deleteMaterial}
|
||||
onDeleteEquipment={store.deleteEquipmentInstance}
|
||||
/>
|
||||
</DebugName>
|
||||
|
||||
{/* Achievements */}
|
||||
<AchievementsDisplay
|
||||
achievements={store.achievements}
|
||||
gameState={{
|
||||
maxFloorReached: store.maxFloorReached,
|
||||
totalManaGathered: store.totalManaGathered,
|
||||
signedPacts: store.signedPacts,
|
||||
totalSpellsCast: store.totalSpellsCast,
|
||||
totalDamageDealt: store.totalDamageDealt,
|
||||
totalCraftsCompleted: store.totalCraftsCompleted,
|
||||
combo: store.combo,
|
||||
}}
|
||||
/>
|
||||
<DebugName name="AchievementsDisplay">
|
||||
<AchievementsDisplay
|
||||
achievements={store.achievements}
|
||||
gameState={{
|
||||
maxFloorReached: store.maxFloorReached,
|
||||
totalManaGathered: store.totalManaGathered,
|
||||
signedPacts: store.signedPacts,
|
||||
totalSpellsCast: store.totalSpellsCast,
|
||||
totalDamageDealt: store.totalDamageDealt,
|
||||
totalCraftsCompleted: store.totalCraftsCompleted,
|
||||
combo: store.combo,
|
||||
}}
|
||||
/>
|
||||
</DebugName>
|
||||
</div>
|
||||
|
||||
{/* 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="grimoire" className="text-xs px-2 py-1">📖 Grimoire</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
|
||||
<TabsContent value="spire">
|
||||
<SpireTab store={store} />
|
||||
<DebugName name="SpireTab">
|
||||
<SpireTab store={store} />
|
||||
</DebugName>
|
||||
</TabsContent>
|
||||
|
||||
|
||||
<TabsContent value="attunements">
|
||||
<AttunementsTab store={store} />
|
||||
<DebugName name="AttunementsTab">
|
||||
<AttunementsTab store={store} />
|
||||
</DebugName>
|
||||
</TabsContent>
|
||||
|
||||
|
||||
<TabsContent value="skills">
|
||||
<SkillsTab store={store} />
|
||||
<DebugName name="SkillsTab">
|
||||
<SkillsTab store={store} />
|
||||
</DebugName>
|
||||
</TabsContent>
|
||||
|
||||
|
||||
<TabsContent value="spells">
|
||||
<SpellsTab store={store} />
|
||||
<DebugName name="SpellsTab">
|
||||
<SpellsTab store={store} />
|
||||
</DebugName>
|
||||
</TabsContent>
|
||||
|
||||
|
||||
<TabsContent value="equipment">
|
||||
<EquipmentTab store={store} />
|
||||
<DebugName name="EquipmentTab">
|
||||
<EquipmentTab store={store} />
|
||||
</DebugName>
|
||||
</TabsContent>
|
||||
|
||||
|
||||
<TabsContent value="crafting">
|
||||
<CraftingTab store={store} />
|
||||
<DebugName name="CraftingTab">
|
||||
<CraftingTab store={store} />
|
||||
</DebugName>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="lab">
|
||||
<LabTab store={store} />
|
||||
<DebugName name="LabTab">
|
||||
<LabTab store={store} />
|
||||
</DebugName>
|
||||
</TabsContent>
|
||||
|
||||
|
||||
<TabsContent value="stats">
|
||||
<StatsTab
|
||||
store={store}
|
||||
upgradeEffects={upgradeEffects}
|
||||
maxMana={maxMana}
|
||||
baseRegen={baseRegen}
|
||||
clickMana={clickMana}
|
||||
meditationMultiplier={meditationMultiplier}
|
||||
effectiveRegen={effectiveRegen}
|
||||
incursionStrength={incursionStrength}
|
||||
manaCascadeBonus={manaCascadeBonus}
|
||||
studySpeedMult={studySpeedMult}
|
||||
studyCostMult={studyCostMult}
|
||||
/>
|
||||
<DebugName name="StatsTab">
|
||||
<StatsTab
|
||||
store={store}
|
||||
upgradeEffects={upgradeEffects}
|
||||
maxMana={maxMana}
|
||||
baseRegen={baseRegen}
|
||||
clickMana={clickMana}
|
||||
meditationMultiplier={meditationMultiplier}
|
||||
effectiveRegen={effectiveRegen}
|
||||
incursionStrength={incursionStrength}
|
||||
manaCascadeBonus={manaCascadeBonus}
|
||||
studySpeedMult={studySpeedMult}
|
||||
studyCostMult={studyCostMult}
|
||||
/>
|
||||
</DebugName>
|
||||
</TabsContent>
|
||||
|
||||
|
||||
<TabsContent value="grimoire">
|
||||
{renderGrimoireTab()}
|
||||
<DebugName name="GrimoireTab">
|
||||
{renderGrimoireTab()}
|
||||
</DebugName>
|
||||
</TabsContent>
|
||||
|
||||
|
||||
<TabsContent value="debug">
|
||||
<DebugTab store={store} />
|
||||
<DebugName name="DebugTab">
|
||||
<DebugTab store={store} />
|
||||
</DebugName>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</div>
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -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) {
|
||||
<CardContent>
|
||||
{designProgress ? (
|
||||
<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" />
|
||||
<div className="flex justify-between text-xs text-gray-400">
|
||||
<span>{designProgress.progress.toFixed(1)}h / {designProgress.required.toFixed(1)}h</span>
|
||||
<Button size="sm" variant="outline" onClick={cancelDesign}>Cancel</Button>
|
||||
</div>
|
||||
{designProgress.progress >= designProgress.required && (
|
||||
<Button onClick={handleCompleteDesign} className="w-full">Complete Design</Button>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<ScrollArea className="h-64">
|
||||
@@ -235,7 +215,7 @@ export function CraftingTab({ store }: CraftingTabProps) {
|
||||
) : designProgress ? (
|
||||
<div className="space-y-2">
|
||||
<div className="text-sm text-gray-400">Design in progress...</div>
|
||||
{selectedEffects.map(eff => {
|
||||
{designProgress.effects.map(eff => {
|
||||
const def = ENCHANTMENT_EFFECTS[eff.effectId];
|
||||
return (
|
||||
<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 { 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) {
|
||||
</CardContent>
|
||||
</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">
|
||||
{/* Game Reset */}
|
||||
<Card className="bg-gray-900/80 border-gray-700">
|
||||
@@ -371,6 +400,18 @@ export function DebugTab({ store }: DebugTabProps) {
|
||||
>
|
||||
Skip to Floor 100
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
onClick={() => {
|
||||
// Reset floor HP
|
||||
if (store.resetFloorHP) {
|
||||
store.resetFloorHP();
|
||||
}
|
||||
}}
|
||||
>
|
||||
Reset Floor HP
|
||||
</Button>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
@@ -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 = () => (
|
||||
<div className="grid grid-cols-3 sm:grid-cols-4 md:grid-cols-6 lg:grid-cols-8 gap-2">
|
||||
{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 (
|
||||
<Card className="bg-gray-900/80 border-gray-700">
|
||||
<CardContent className="pt-6">
|
||||
<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>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
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
|
||||
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)],
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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<GameStore>()(
|
||||
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<GameStore>()(
|
||||
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',
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user