fix: update StatsTab, DebugTab and all child components to use modular stores
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m42s
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m42s
- Updated StatsTab to use hooks directly (useSkillStore, usePrestigeStore, etc.) - Updated DebugTab to use hooks directly - Updated all debug child components (GameStateDebug, SkillDebug, AttunementDebug, etc.) - Updated all stats child components (ManaStatsSection, CombatStatsSection, etc.) - Fixed UpgradeEffectsSection.tsx syntax errors - Updated page.tsx to not pass store prop to StatsTab and DebugTab - All components now use modular stores directly instead of receiving store prop
This commit is contained in:
+2
-2
@@ -233,13 +233,13 @@ export default function ManaLoopGame() {
|
|||||||
|
|
||||||
<TabsContent value="stats">
|
<TabsContent value="stats">
|
||||||
<Suspense fallback={<TabLoadingFallback />}>
|
<Suspense fallback={<TabLoadingFallback />}>
|
||||||
<StatsTab store={store} />
|
<StatsTab />
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|
||||||
<TabsContent value="debug">
|
<TabsContent value="debug">
|
||||||
<Suspense fallback={<TabLoadingFallback />}>
|
<Suspense fallback={<TabLoadingFallback />}>
|
||||||
<DebugTab store={store} />
|
<DebugTab />
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|
||||||
|
|||||||
@@ -2,25 +2,22 @@
|
|||||||
|
|
||||||
import { Button } from '@/components/ui/button';
|
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 { ATTUNEMENTS_DEF } from '@/lib/game/data/attunements';
|
|
||||||
import { Sparkles, Unlock } from 'lucide-react';
|
import { Sparkles, Unlock } from 'lucide-react';
|
||||||
import type { GameStore } from '@/lib/game/store';
|
import { ATTUNEMENTS_DEF } from '@/lib/game/data/attunements';
|
||||||
|
import { useGameStore } from '@/lib/game/store';
|
||||||
|
|
||||||
interface AttunementDebugProps {
|
export function AttunementDebug() {
|
||||||
store: GameStore;
|
const store = useGameStore((s) => s);
|
||||||
}
|
|
||||||
|
|
||||||
export function AttunementDebug({ store }: AttunementDebugProps) {
|
|
||||||
const handleUnlockAttunement = (id: string) => {
|
const handleUnlockAttunement = (id: string) => {
|
||||||
// Debug action to unlock attunements
|
if (useGameStore.getState().debugUnlockAttunement) {
|
||||||
if (store.debugUnlockAttunement) {
|
useGameStore.getState().debugUnlockAttunement(id);
|
||||||
store.debugUnlockAttunement(id);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleAddAttunementXP = (id: string, amount: number) => {
|
const handleAddAttunementXP = (id: string, amount: number) => {
|
||||||
if (store.debugAddAttunementXP) {
|
if (useGameStore.getState().debugAddAttunementXP) {
|
||||||
store.debugAddAttunementXP(id, amount);
|
useGameStore.getState().debugAddAttunementXP(id, amount);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -49,34 +46,21 @@ export function AttunementDebug({ store }: AttunementDebugProps) {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex gap-1">
|
<div className="flex gap-2">
|
||||||
{!isActive && (
|
|
||||||
<Button
|
<Button
|
||||||
size="sm"
|
size="sm"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
onClick={() => handleUnlockAttunement(id)}
|
onClick={() => handleUnlockAttunement(id)}
|
||||||
>
|
>
|
||||||
<Unlock className="w-3 h-3" />
|
<Unlock className="w-3 h-3 mr-1" /> Unlock
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
{isActive && (
|
|
||||||
<>
|
|
||||||
<Button
|
|
||||||
size="sm"
|
|
||||||
variant="outline"
|
|
||||||
onClick={() => handleAddAttunementXP(id, 50)}
|
|
||||||
>
|
|
||||||
+50 XP
|
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
size="sm"
|
size="sm"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
onClick={() => handleAddAttunementXP(id, 500)}
|
onClick={() => handleAddAttunementXP(id, 100)}
|
||||||
>
|
>
|
||||||
+500 XP
|
+100 XP
|
||||||
</Button>
|
</Button>
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -2,26 +2,19 @@
|
|||||||
|
|
||||||
import { Button } from '@/components/ui/button';
|
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 { ELEMENTS } from '@/lib/game/constants';
|
|
||||||
import { Star, Lock } from 'lucide-react';
|
import { Star, Lock } from 'lucide-react';
|
||||||
import type { GameStore } from '@/lib/game/store';
|
import { useGameStore } from '@/lib/game/store';
|
||||||
|
|
||||||
interface ElementDebugProps {
|
export function ElementDebug() {
|
||||||
store: GameStore;
|
const store = useGameStore((s) => s);
|
||||||
}
|
|
||||||
|
|
||||||
export function ElementDebug({ store }: ElementDebugProps) {
|
|
||||||
const handleUnlockElement = (element: string) => {
|
const handleUnlockElement = (element: string) => {
|
||||||
store.unlockElement(element);
|
useGameStore.getState().unlockElement(element);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleAddElementalMana = (element: string, amount: number) => {
|
const handleAddElementalMana = (element: string, amount: number) => {
|
||||||
const elem = store.elements[element];
|
if (useGameStore.getState().debugAddElementalMana) {
|
||||||
if (elem?.unlocked) {
|
useGameStore.getState().debugAddElementalMana(element, amount);
|
||||||
// Add directly to element pool - need to implement in store
|
|
||||||
if (store.debugAddElementalMana) {
|
|
||||||
store.debugAddElementalMana(element, amount);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -35,47 +28,41 @@ export function ElementDebug({ store }: ElementDebugProps) {
|
|||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<div className="grid grid-cols-3 sm:grid-cols-4 md:grid-cols-6 gap-2">
|
<div className="grid grid-cols-3 sm:grid-cols-4 md:grid-cols-6 gap-2">
|
||||||
{Object.entries(ELEMENTS).map(([id, def]) => {
|
{Object.entries(store.elements).map(([id, elem]) => {
|
||||||
const elem = store.elements[id];
|
const def = ELEMENTS[id];
|
||||||
const isUnlocked = elem?.unlocked;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={id}
|
key={id}
|
||||||
className={`p-2 rounded border ${
|
className={`p-2 rounded border text-center ${
|
||||||
isUnlocked ? 'border-gray-600' : 'border-gray-800 opacity-60'
|
elem.unlocked ? 'border-gray-600 bg-gray-800/50' : 'border-gray-800 opacity-60'
|
||||||
}`}
|
}`}
|
||||||
style={{
|
style={{
|
||||||
borderColor: isUnlocked ? def.color : undefined
|
borderColor: elem.unlocked ? def?.color : undefined
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="flex items-center justify-between mb-1">
|
<div className="text-lg">{def?.sym}</div>
|
||||||
<span style={{ color: def.color }}>{def.sym}</span>
|
<div className="text-xs text-gray-400">{def?.name}</div>
|
||||||
{!isUnlocked && (
|
<div className="text-xs text-gray-300 mt-1">
|
||||||
|
{elem.current}/{elem.max}
|
||||||
|
</div>
|
||||||
|
{!elem.unlocked && (
|
||||||
<Button
|
<Button
|
||||||
size="sm"
|
size="sm"
|
||||||
variant="ghost"
|
variant="outline"
|
||||||
className="h-5 w-5 p-0"
|
className="mt-2"
|
||||||
onClick={() => handleUnlockElement(id)}
|
onClick={() => handleUnlockElement(id)}
|
||||||
>
|
>
|
||||||
<Lock className="w-3 h-3" />
|
<Unlock className="w-3 h-3 mr-1" /> Unlock
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</div>
|
{elem.unlocked && (
|
||||||
<div className="text-xs" style={{ color: def.color }}>{def.name}</div>
|
|
||||||
{isUnlocked && (
|
|
||||||
<div className="text-xs text-gray-400 mt-1">
|
|
||||||
{elem.current.toFixed(0)}/{elem.max}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{isUnlocked && (
|
|
||||||
<Button
|
<Button
|
||||||
size="sm"
|
size="sm"
|
||||||
variant="ghost"
|
variant="outline"
|
||||||
className="h-5 w-full mt-1 text-xs"
|
className="mt-2"
|
||||||
onClick={() => handleAddElementalMana(id, 100)}
|
onClick={() => handleAddElementalMana(id, 10)}
|
||||||
>
|
>
|
||||||
+100
|
+10
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -8,23 +8,20 @@ import { Switch } from '@/components/ui/switch';
|
|||||||
import { Label } from '@/components/ui/label';
|
import { Label } from '@/components/ui/label';
|
||||||
import {
|
import {
|
||||||
RotateCcw, AlertTriangle, Zap, Clock, Settings, Eye,
|
RotateCcw, AlertTriangle, Zap, Clock, Settings, Eye,
|
||||||
Plus
|
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import type { GameStore } from '@/lib/game/store';
|
|
||||||
import { fmt } from '@/lib/game/store';
|
|
||||||
import { useDebug } from '@/lib/game/debug-context';
|
import { useDebug } from '@/lib/game/debug-context';
|
||||||
|
import { useGameStore } from '@/lib/game/store';
|
||||||
|
|
||||||
interface GameStateDebugProps {
|
export function GameStateDebug() {
|
||||||
store: GameStore;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function GameStateDebug({ store }: GameStateDebugProps) {
|
|
||||||
const [confirmReset, setConfirmReset] = useState(false);
|
const [confirmReset, setConfirmReset] = useState(false);
|
||||||
const { showComponentNames, toggleComponentNames } = useDebug();
|
const { showComponentNames, toggleComponentNames } = useDebug();
|
||||||
|
|
||||||
|
// Get state from store
|
||||||
|
const store = useGameStore((s) => s);
|
||||||
|
|
||||||
const handleReset = () => {
|
const handleReset = () => {
|
||||||
if (confirmReset) {
|
if (confirmReset) {
|
||||||
store.resetGame();
|
useGameStore.getState().resetGame();
|
||||||
setConfirmReset(false);
|
setConfirmReset(false);
|
||||||
} else {
|
} else {
|
||||||
setConfirmReset(true);
|
setConfirmReset(true);
|
||||||
@@ -34,14 +31,16 @@ export function GameStateDebug({ store }: GameStateDebugProps) {
|
|||||||
|
|
||||||
const handleAddMana = (amount: number) => {
|
const handleAddMana = (amount: number) => {
|
||||||
// Use gatherMana multiple times to add mana
|
// Use gatherMana multiple times to add mana
|
||||||
|
const state = useGameStore.getState();
|
||||||
for (let i = 0; i < amount; i++) {
|
for (let i = 0; i < amount; i++) {
|
||||||
store.gatherMana();
|
state.gatherMana();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSetTime = (day: number, hour: number) => {
|
const handleSetTime = (day: number, hour: number) => {
|
||||||
if (store.debugSetTime) {
|
const state = useGameStore.getState();
|
||||||
store.debugSetTime(day, hour);
|
if (state.debugSetTime) {
|
||||||
|
state.debugSetTime(day, hour);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -127,20 +126,20 @@ export function GameStateDebug({ store }: GameStateDebugProps) {
|
|||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="space-y-3">
|
<CardContent className="space-y-3">
|
||||||
<div className="text-xs text-gray-400 mb-2">
|
<div className="text-xs text-gray-400 mb-2">
|
||||||
Current: {fmt(store.rawMana)} / {fmt(store.getMaxMana())}
|
Current: {store.rawMana} / {store.getMaxMana?.() || '?'}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex gap-2 flex-wrap">
|
<div className="flex gap-2 flex-wrap">
|
||||||
<Button size="sm" variant="outline" onClick={() => handleAddMana(10)}>
|
<Button size="sm" variant="outline" onClick={() => handleAddMana(10)}>
|
||||||
<Plus className="w-3 h-3 mr-1" /> +10
|
<Zap className="w-3 h-3 mr-1" /> +10
|
||||||
</Button>
|
</Button>
|
||||||
<Button size="sm" variant="outline" onClick={() => handleAddMana(100)}>
|
<Button size="sm" variant="outline" onClick={() => handleAddMana(100)}>
|
||||||
<Plus className="w-3 h-3 mr-1" /> +100
|
<Zap className="w-3 h-3 mr-1" /> +100
|
||||||
</Button>
|
</Button>
|
||||||
<Button size="sm" variant="outline" onClick={() => handleAddMana(1000)}>
|
<Button size="sm" variant="outline" onClick={() => handleAddMana(1000)}>
|
||||||
<Plus className="w-3 h-3 mr-1" /> +1K
|
<Zap className="w-3 h-3 mr-1" /> +1K
|
||||||
</Button>
|
</Button>
|
||||||
<Button size="sm" variant="outline" onClick={() => handleAddMana(10000)}>
|
<Button size="sm" variant="outline" onClick={() => handleAddMana(10000)}>
|
||||||
<Plus className="w-3 h-3 mr-1" /> +10K
|
<Zap className="w-3 h-3 mr-1" /> +10K
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<Separator className="bg-gray-700" />
|
<Separator className="bg-gray-700" />
|
||||||
@@ -149,7 +148,7 @@ export function GameStateDebug({ store }: GameStateDebugProps) {
|
|||||||
size="sm"
|
size="sm"
|
||||||
className="w-full bg-blue-600 hover:bg-blue-700"
|
className="w-full bg-blue-600 hover:bg-blue-700"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
const max = store.getMaxMana();
|
const max = store.getMaxMana?.() || 100;
|
||||||
const current = store.rawMana;
|
const current = store.rawMana;
|
||||||
for (let i = 0; i < Math.floor(max - current); i++) {
|
for (let i = 0; i < Math.floor(max - current); i++) {
|
||||||
store.gatherMana();
|
store.gatherMana();
|
||||||
@@ -192,7 +191,7 @@ export function GameStateDebug({ store }: GameStateDebugProps) {
|
|||||||
<Button
|
<Button
|
||||||
size="sm"
|
size="sm"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
onClick={() => store.togglePause()}
|
onClick={() => useGameStore.getState().togglePause()}
|
||||||
>
|
>
|
||||||
{store.paused ? '▶ Resume' : '⏸ Pause'}
|
{store.paused ? '▶ Resume' : '⏸ Pause'}
|
||||||
</Button>
|
</Button>
|
||||||
@@ -217,7 +216,7 @@ export function GameStateDebug({ store }: GameStateDebugProps) {
|
|||||||
// Unlock all base elements
|
// Unlock all base elements
|
||||||
['fire', 'water', 'air', 'earth', 'light', 'dark', 'death'].forEach(e => {
|
['fire', 'water', 'air', 'earth', 'light', 'dark', 'death'].forEach(e => {
|
||||||
if (!store.elements[e]?.unlocked) {
|
if (!store.elements[e]?.unlocked) {
|
||||||
store.unlockElement(e);
|
useGameStore.getState().unlockElement(e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
@@ -228,10 +227,10 @@ export function GameStateDebug({ store }: GameStateDebugProps) {
|
|||||||
size="sm"
|
size="sm"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
// Unlock utility elements (only transference remains)
|
// Unlock utility elements
|
||||||
['transference'].forEach(e => {
|
['transference'].forEach(e => {
|
||||||
if (!store.elements[e]?.unlocked) {
|
if (!store.elements[e]?.unlocked) {
|
||||||
store.unlockElement(e);
|
useGameStore.getState().unlockElement(e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
@@ -244,7 +243,7 @@ export function GameStateDebug({ store }: GameStateDebugProps) {
|
|||||||
onClick={() => {
|
onClick={() => {
|
||||||
// Max floor
|
// Max floor
|
||||||
if (store.debugSetFloor) {
|
if (store.debugSetFloor) {
|
||||||
store.debugSetFloor(100);
|
useGameStore.getState().debugSetFloor(100);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -256,7 +255,7 @@ export function GameStateDebug({ store }: GameStateDebugProps) {
|
|||||||
onClick={() => {
|
onClick={() => {
|
||||||
// Reset floor HP
|
// Reset floor HP
|
||||||
if (store.resetFloorHP) {
|
if (store.resetFloorHP) {
|
||||||
store.resetFloorHP();
|
useGameStore.getState().resetFloorHP();
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -2,13 +2,8 @@
|
|||||||
|
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||||
import { Bug } from 'lucide-react';
|
import { Bug } from 'lucide-react';
|
||||||
import type { GameStore } from '@/lib/game/store';
|
|
||||||
|
|
||||||
interface GolemDebugProps {
|
export function GolemDebug() {
|
||||||
store: GameStore;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function GolemDebug({ store }: GolemDebugProps) {
|
|
||||||
return (
|
return (
|
||||||
<Card className="bg-gray-900/80 border-gray-700 md:col-span-2">
|
<Card className="bg-gray-900/80 border-gray-700 md:col-span-2">
|
||||||
<CardHeader className="pb-2">
|
<CardHeader className="pb-2">
|
||||||
|
|||||||
@@ -4,13 +4,11 @@ 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 { Separator } from '@/components/ui/separator';
|
import { Separator } from '@/components/ui/separator';
|
||||||
import { BookOpen } from 'lucide-react';
|
import { BookOpen } from 'lucide-react';
|
||||||
import type { GameStore } from '@/lib/game/store';
|
import { useGameStore } from '@/lib/game/store';
|
||||||
|
|
||||||
interface SkillDebugProps {
|
export function SkillDebug() {
|
||||||
store: GameStore;
|
const store = useGameStore((s) => s);
|
||||||
}
|
|
||||||
|
|
||||||
export function SkillDebug({ store }: SkillDebugProps) {
|
|
||||||
return (
|
return (
|
||||||
<Card className="bg-gray-900/80 border-gray-700 md:col-span-2">
|
<Card className="bg-gray-900/80 border-gray-700 md:col-span-2">
|
||||||
<CardHeader className="pb-2">
|
<CardHeader className="pb-2">
|
||||||
@@ -32,11 +30,9 @@ export function SkillDebug({ store }: SkillDebugProps) {
|
|||||||
// Level up all enchanting skills by 1
|
// Level up all enchanting skills by 1
|
||||||
const enchantSkills = ['enchanting', 'efficientEnchant', 'enchantSpeed','essenceRefining'];
|
const enchantSkills = ['enchanting', 'efficientEnchant', 'enchantSpeed','essenceRefining'];
|
||||||
enchantSkills.forEach(skillId => {
|
enchantSkills.forEach(skillId => {
|
||||||
if (store.skills[skillId] !== undefined) {
|
|
||||||
store.skills[skillId] = Math.min((store.skills[skillId] || 0) + 1, 10);
|
store.skills[skillId] = Math.min((store.skills[skillId] || 0) + 1, 10);
|
||||||
} else {
|
// Force update
|
||||||
store.skills[skillId] = 1;
|
useGameStore.setState({ skills: { ...store.skills } });
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -51,6 +47,7 @@ export function SkillDebug({ store }: SkillDebugProps) {
|
|||||||
enchantSkills.forEach(skillId => {
|
enchantSkills.forEach(skillId => {
|
||||||
store.skills[skillId] = 10;
|
store.skills[skillId] = 10;
|
||||||
});
|
});
|
||||||
|
useGameStore.setState({ skills: { ...store.skills } });
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Max All Enchanting
|
Max All Enchanting
|
||||||
@@ -68,12 +65,9 @@ export function SkillDebug({ store }: SkillDebugProps) {
|
|||||||
onClick={() => {
|
onClick={() => {
|
||||||
const manaSkills = ['manaWell', 'manaFlow', 'manaOverflow', 'fireManaCap', 'waterManaCap', 'airManaCap', 'earthManaCap', 'lightManaCap', 'darkManaCap', 'deathManaCap', 'metalManaCap', 'sandManaCap', 'lightningManaCap', 'transferenceManaCap'];
|
const manaSkills = ['manaWell', 'manaFlow', 'manaOverflow', 'fireManaCap', 'waterManaCap', 'airManaCap', 'earthManaCap', 'lightManaCap', 'darkManaCap', 'deathManaCap', 'metalManaCap', 'sandManaCap', 'lightningManaCap', 'transferenceManaCap'];
|
||||||
manaSkills.forEach(skillId => {
|
manaSkills.forEach(skillId => {
|
||||||
if (store.skills[skillId] !== undefined) {
|
|
||||||
store.skills[skillId] = Math.min((store.skills[skillId] || 0) + 1, 10);
|
store.skills[skillId] = Math.min((store.skills[skillId] || 0) + 1, 10);
|
||||||
} else {
|
|
||||||
store.skills[skillId] = 1;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
useGameStore.setState({ skills: { ...store.skills } });
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
+1 All Mana
|
+1 All Mana
|
||||||
@@ -86,6 +80,7 @@ export function SkillDebug({ store }: SkillDebugProps) {
|
|||||||
manaSkills.forEach(skillId => {
|
manaSkills.forEach(skillId => {
|
||||||
store.skills[skillId] = 10;
|
store.skills[skillId] = 10;
|
||||||
});
|
});
|
||||||
|
useGameStore.setState({ skills: { ...store.skills } });
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Max All Mana
|
Max All Mana
|
||||||
@@ -103,12 +98,9 @@ export function SkillDebug({ store }: SkillDebugProps) {
|
|||||||
onClick={() => {
|
onClick={() => {
|
||||||
const studySkills = ['quickLearner', 'focusedMind', 'meditation', 'knowledgeRetention'];
|
const studySkills = ['quickLearner', 'focusedMind', 'meditation', 'knowledgeRetention'];
|
||||||
studySkills.forEach(skillId => {
|
studySkills.forEach(skillId => {
|
||||||
if (store.skills[skillId] !== undefined) {
|
|
||||||
store.skills[skillId] = Math.min((store.skills[skillId] || 0) + 1, 10);
|
store.skills[skillId] = Math.min((store.skills[skillId] || 0) + 1, 10);
|
||||||
} else {
|
|
||||||
store.skills[skillId] = 1;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
useGameStore.setState({ skills: { ...store.skills } });
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
+1 All Study
|
+1 All Study
|
||||||
@@ -121,6 +113,7 @@ export function SkillDebug({ store }: SkillDebugProps) {
|
|||||||
studySkills.forEach(skillId => {
|
studySkills.forEach(skillId => {
|
||||||
store.skills[skillId] = 10;
|
store.skills[skillId] = 10;
|
||||||
});
|
});
|
||||||
|
useGameStore.setState({ skills: { ...store.skills } });
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Max All Study
|
Max All Study
|
||||||
@@ -138,12 +131,9 @@ export function SkillDebug({ store }: SkillDebugProps) {
|
|||||||
onClick={() => {
|
onClick={() => {
|
||||||
const craftSkills = ['effCrafting', 'fieldRepair', 'elemCrafting'];
|
const craftSkills = ['effCrafting', 'fieldRepair', 'elemCrafting'];
|
||||||
craftSkills.forEach(skillId => {
|
craftSkills.forEach(skillId => {
|
||||||
if (store.skills[skillId] !== undefined) {
|
|
||||||
store.skills[skillId] = Math.min((store.skills[skillId] || 0) + 1, 10);
|
store.skills[skillId] = Math.min((store.skills[skillId] || 0) + 1, 10);
|
||||||
} else {
|
|
||||||
store.skills[skillId] = 1;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
useGameStore.setState({ skills: { ...store.skills } });
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
+1 All Crafting
|
+1 All Crafting
|
||||||
@@ -156,6 +146,7 @@ export function SkillDebug({ store }: SkillDebugProps) {
|
|||||||
craftSkills.forEach(skillId => {
|
craftSkills.forEach(skillId => {
|
||||||
store.skills[skillId] = 10;
|
store.skills[skillId] = 10;
|
||||||
});
|
});
|
||||||
|
useGameStore.setState({ skills: { ...store.skills } });
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Max All Crafting
|
Max All Crafting
|
||||||
@@ -179,12 +170,13 @@ export function SkillDebug({ store }: SkillDebugProps) {
|
|||||||
'researchAdvancedFire', 'researchAdvancedWater', 'researchAdvancedAir',
|
'researchAdvancedFire', 'researchAdvancedWater', 'researchAdvancedAir',
|
||||||
'researchAdvancedEarth', 'researchAdvancedLight', 'researchAdvancedDark',
|
'researchAdvancedEarth', 'researchAdvancedLight', 'researchAdvancedDark',
|
||||||
'researchMasterFire', 'researchMasterWater', 'researchMasterEarth',
|
'researchMasterFire', 'researchMasterWater', 'researchMasterEarth',
|
||||||
'researchDamageEffects', 'researchCombatEffects',
|
'researchDamageEffects', 'researchCombatEffects', 'researchManaEffects',
|
||||||
'researchManaEffects', 'researchAdvancedManaEffects', 'researchUtilityEffects'
|
'researchAdvancedManaEffects', 'researchUtilityEffects'
|
||||||
];
|
];
|
||||||
researchSkills.forEach(skillId => {
|
researchSkills.forEach(skillId => {
|
||||||
store.skills[skillId] = 1;
|
store.skills[skillId] = 1;
|
||||||
});
|
});
|
||||||
|
useGameStore.setState({ skills: { ...store.skills } });
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Unlock All Research
|
Unlock All Research
|
||||||
@@ -194,9 +186,7 @@ export function SkillDebug({ store }: SkillDebugProps) {
|
|||||||
variant="outline"
|
variant="outline"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
// Add all unlocked effects to unlockedEffects
|
// Add all unlocked effects to unlockedEffects
|
||||||
const effectIds = Object.keys(store.unlockedEffects || {});
|
const effectIds = [
|
||||||
// Add spell effects, mana effects, combat effects, utility effects
|
|
||||||
const allEffectIds = [
|
|
||||||
// Spell effects
|
// Spell effects
|
||||||
'spell_manaBolt', 'spell_manaStrike', 'spell_fireball', 'spell_emberShot',
|
'spell_manaBolt', 'spell_manaStrike', 'spell_fireball', 'spell_emberShot',
|
||||||
'spell_waterJet', 'spell_iceShard', 'spell_gust', 'spell_windSlash',
|
'spell_waterJet', 'spell_iceShard', 'spell_gust', 'spell_windSlash',
|
||||||
@@ -229,11 +219,13 @@ export function SkillDebug({ store }: SkillDebugProps) {
|
|||||||
// Sword enchants
|
// Sword enchants
|
||||||
'sword_fire', 'sword_frost', 'sword_lightning', 'sword_void'
|
'sword_fire', 'sword_frost', 'sword_lightning', 'sword_void'
|
||||||
];
|
];
|
||||||
allEffectIds.forEach(id => {
|
const currentEffects = store.unlockedEffects || [];
|
||||||
if (!store.unlockedEffects.includes(id)) {
|
effectIds.forEach(id => {
|
||||||
store.unlockedEffects.push(id);
|
if (!currentEffects.includes(id)) {
|
||||||
|
currentEffects.push(id);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
useGameStore.setState({ unlockedEffects: currentEffects });
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Unlock All Effects
|
Unlock All Effects
|
||||||
@@ -260,27 +252,22 @@ export function SkillDebug({ store }: SkillDebugProps) {
|
|||||||
'spell_manaBolt', 'spell_manaStrike', 'spell_fireball', 'spell_emberShot',
|
'spell_manaBolt', 'spell_manaStrike', 'spell_fireball', 'spell_emberShot',
|
||||||
'spell_waterJet', 'spell_iceShard', 'spell_gust', 'spell_windSlash',
|
'spell_waterJet', 'spell_iceShard', 'spell_gust', 'spell_windSlash',
|
||||||
'spell_stoneBullet', 'spell_rockSpike', 'spell_lightLance', 'spell_radiance',
|
'spell_stoneBullet', 'spell_rockSpike', 'spell_lightLance', 'spell_radiance',
|
||||||
'spell_shadowBolt', 'spell_darkPulse', 'spell_drain', 'spell_inferno',
|
'spell_shadowBolt', 'spell_darkPulse', 'spell_drain',
|
||||||
'spell_flameWave', 'spell_tidalWave', 'spell_iceStorm', 'spell_hurricane',
|
|
||||||
'spell_windBlade', 'spell_earthquake', 'spell_stoneBarrage', 'spell_solarFlare',
|
|
||||||
'spell_divineSmite', 'spell_voidRift', 'spell_shadowStorm', 'spell_pyroclasm',
|
|
||||||
'spell_tsunami', 'spell_meteorStrike', 'spell_spark', 'spell_lightningBolt',
|
|
||||||
'spell_chainLightning', 'spell_stormCall', 'spell_thunderStrike', 'spell_metalShard',
|
|
||||||
'spell_ironFist', 'spell_steelTempest', 'spell_furnaceBlast', 'spell_sandBlast',
|
|
||||||
'spell_sandstorm', 'spell_desertWind', 'spell_duneCollapse',
|
|
||||||
'mana_cap_50', 'mana_cap_100', 'mana_regen_1', 'mana_regen_2', 'mana_regen_5',
|
'mana_cap_50', 'mana_cap_100', 'mana_regen_1', 'mana_regen_2', 'mana_regen_5',
|
||||||
'click_mana_1', 'click_mana_3', 'damage_5', 'damage_10', 'damage_pct_10',
|
'click_mana_1', 'click_mana_3', 'damage_5', 'damage_10', 'damage_pct_10',
|
||||||
'crit_5', 'attack_speed_10', 'meditate_10', 'study_10', 'insight_5',
|
'crit_5', 'attack_speed_10', 'meditate_10', 'study_10', 'insight_5',
|
||||||
'spell_echo_10', 'guardian_dmg_10', 'overpower_80',
|
'spell_echo_10', 'guardian_dmg_10', 'overpower_80'
|
||||||
'weapon_mana_cap_20', 'weapon_mana_cap_50', 'weapon_mana_cap_100',
|
|
||||||
'weapon_mana_regen_1', 'weapon_mana_regen_2', 'weapon_mana_regen_5',
|
|
||||||
'sword_fire', 'sword_frost', 'sword_lightning', 'sword_void'
|
|
||||||
];
|
];
|
||||||
|
const currentEffects = store.unlockedEffects || [];
|
||||||
allEffectIds.forEach(id => {
|
allEffectIds.forEach(id => {
|
||||||
if (!store.unlockedEffects.includes(id)) {
|
if (!currentEffects.includes(id)) {
|
||||||
store.unlockedEffects.push(id);
|
currentEffects.push(id);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
useGameStore.setState({
|
||||||
|
skills: { ...store.skills },
|
||||||
|
unlockedEffects: currentEffects
|
||||||
|
});
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
🚀 Max Everything
|
🚀 Max Everything
|
||||||
|
|||||||
@@ -1,20 +1,21 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { GUARDIANS } from '@/lib/game/constants';
|
import { fmtDec } from '@/lib/game/stores';
|
||||||
import { fmtDec } from '@/lib/game/store';
|
|
||||||
import type { GameStore } from '@/lib/game/types';
|
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||||
import { Swords } from 'lucide-react';
|
import { Swords } from 'lucide-react';
|
||||||
|
|
||||||
export interface CombatStatsSectionProps {
|
// Modular stores
|
||||||
store: GameStore;
|
import { useSkillStore, useCombatStore } from '@/lib/game/stores';
|
||||||
}
|
|
||||||
|
export function CombatStatsSection() {
|
||||||
|
// Get state from modular stores
|
||||||
|
const skills = useSkillStore((s) => s.skills);
|
||||||
|
const signedPacts = useCombatStore((s) => s.signedPacts);
|
||||||
|
|
||||||
export function CombatStatsSection({ store }: CombatStatsSectionProps) {
|
|
||||||
return (
|
return (
|
||||||
<Card className="bg-gray-900/80 border-gray-700">
|
<Card className="bg-gray-900/80 border-gray-700">
|
||||||
<CardHeader className="pb-2">
|
<CardHeader className="pb-2">
|
||||||
<CardTitle className="text-red-400 game-panel-title text-xs flex items-center gap-2">
|
<CardTitle className="text-red-400 text-sm flex items-center gap-2">
|
||||||
<Swords className="w-4 h-4" />
|
<Swords className="w-4 h-4" />
|
||||||
Combat Stats
|
Combat Stats
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
@@ -24,25 +25,25 @@ export function CombatStatsSection({ store }: CombatStatsSectionProps) {
|
|||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<div className="flex justify-between text-sm">
|
<div className="flex justify-between text-sm">
|
||||||
<span className="text-gray-400">Combat Training Bonus:</span>
|
<span className="text-gray-400">Combat Training Bonus:</span>
|
||||||
<span className="text-red-300">+{(store.skills.combatTrain || 0) * 5}</span>
|
<span className="text-red-300">+{(skills.combatTrain || 0) * 5}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between text-sm">
|
<div className="flex justify-between text-sm">
|
||||||
<span className="text-gray-400">Arcane Fury Multiplier:</span>
|
<span className="text-gray-400">Arcane Fury Multiplier:</span>
|
||||||
<span className="text-red-300">×{fmtDec(1 + (store.skills.arcaneFury || 0) * 0.1, 2)}</span>
|
<span className="text-red-300">×{fmtDec(1 + (skills.arcaneFury || 0) * 0.1, 2)}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between text-sm">
|
<div className="flex justify-between text-sm">
|
||||||
<span className="text-gray-400">Elemental Mastery:</span>
|
<span className="text-gray-400">Elemental Mastery:</span>
|
||||||
<span className="text-red-300">×{fmtDec(1 + (store.skills.elementalMastery || 0) * 0.15, 2)}</span>
|
<span className="text-red-300">×{fmtDec(1 + (skills.elementalMastery || 0) * 0.15, 2)}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between text-sm">
|
<div className="flex justify-between text-sm">
|
||||||
<span className="text-gray-400">Guardian Bane:</span>
|
<span className="text-gray-400">Guardian Bane:</span>
|
||||||
<span className="text-red-300">×{fmtDec(1 + (store.skills.guardianBane || 0) * 0.2, 2)} (vs guardians)</span>
|
<span className="text-red-300">×{fmtDec(1 + (skills.guardianBane || 0) * 0.2, 2)} (vs guardians)</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<div className="flex justify-between text-sm">
|
<div className="flex justify-between text-sm">
|
||||||
<span className="text-gray-400">Critical Hit Chance:</span>
|
<span className="text-gray-400">Critical Hit Chance:</span>
|
||||||
<span className="text-amber-300">{((store.skills.precision || 0) * 5)}%</span>
|
<span className="text-amber-300">{(skills.precision || 0) * 5}%</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between text-sm">
|
<div className="flex justify-between text-sm">
|
||||||
<span className="text-gray-400">Critical Multiplier:</span>
|
<span className="text-gray-400">Critical Multiplier:</span>
|
||||||
@@ -50,11 +51,10 @@ export function CombatStatsSection({ store }: CombatStatsSectionProps) {
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between text-sm">
|
<div className="flex justify-between text-sm">
|
||||||
<span className="text-gray-400">Spell Echo Chance:</span>
|
<span className="text-gray-400">Spell Echo Chance:</span>
|
||||||
<span className="text-amber-300">{((store.skills.spellEcho || 0) * 10)}%</span>
|
<span className="text-amber-300">{(skills.spellEcho || 0) * 10}%</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between text-sm">
|
<div className="flex justify-between text-sm">
|
||||||
<span className="text-gray-400">Pact Multiplier:</span>
|
<span className="text-amber-300">×{fmtDec(signedPacts.reduce((m, f) => m * (GUARDIANS[f]?.pact || 1), 1), 2)}</span>
|
||||||
<span className="text-amber-300">×{fmtDec(store.signedPacts.reduce((m, f) => m * (GUARDIANS[f]?.pact || 1), 1), 2)}</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,14 +2,16 @@
|
|||||||
|
|
||||||
import { getTierMultiplier } from '@/lib/game/skill-evolution';
|
import { getTierMultiplier } from '@/lib/game/skill-evolution';
|
||||||
import { hasSpecial, SPECIAL_EFFECTS } from '@/lib/game/effects';
|
import { hasSpecial, SPECIAL_EFFECTS } from '@/lib/game/effects';
|
||||||
import { fmt, fmtDec } from '@/lib/game/store';
|
import { fmt, fmtDec } from '@/lib/game/stores';
|
||||||
import type { GameStore, UnifiedEffects } from '@/lib/game/types';
|
import type { UnifiedEffects } from '@/lib/game/types';
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||||
import { Separator } from '@/components/ui/separator';
|
import { Separator } from '@/components/ui/separator';
|
||||||
import { Droplet } from 'lucide-react';
|
import { Droplet } from 'lucide-react';
|
||||||
|
|
||||||
|
// Modular stores
|
||||||
|
import { useSkillStore, usePrestigeStore, useManaStore } from '@/lib/game/stores';
|
||||||
|
|
||||||
export interface ManaStatsSectionProps {
|
export interface ManaStatsSectionProps {
|
||||||
store: GameStore;
|
|
||||||
upgradeEffects: UnifiedEffects;
|
upgradeEffects: UnifiedEffects;
|
||||||
maxMana: number;
|
maxMana: number;
|
||||||
baseRegen: number;
|
baseRegen: number;
|
||||||
@@ -26,7 +28,6 @@ export interface ManaStatsSectionProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function ManaStatsSection({
|
export function ManaStatsSection({
|
||||||
store,
|
|
||||||
upgradeEffects,
|
upgradeEffects,
|
||||||
maxMana,
|
maxMana,
|
||||||
baseRegen,
|
baseRegen,
|
||||||
@@ -41,6 +42,12 @@ export function ManaStatsSection({
|
|||||||
hasManaOverflow,
|
hasManaOverflow,
|
||||||
hasEternalFlow,
|
hasEternalFlow,
|
||||||
}: ManaStatsSectionProps) {
|
}: ManaStatsSectionProps) {
|
||||||
|
// Get state from modular stores
|
||||||
|
const skills = useSkillStore((s) => s.skills);
|
||||||
|
const skillTiers = useSkillStore((s) => s.skillTiers);
|
||||||
|
const prestigeUpgrades = usePrestigeStore((s) => s.prestigeUpgrades);
|
||||||
|
const rawMana = useManaStore((s) => s.rawMana);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card className="bg-gray-900/80 border-gray-700">
|
<Card className="bg-gray-900/80 border-gray-700">
|
||||||
<CardHeader className="pb-2">
|
<CardHeader className="pb-2">
|
||||||
@@ -60,9 +67,9 @@ export function ManaStatsSection({
|
|||||||
<span className="text-gray-400">Mana Well Bonus:</span>
|
<span className="text-gray-400">Mana Well Bonus:</span>
|
||||||
<span className="text-blue-300">
|
<span className="text-blue-300">
|
||||||
{(() => {
|
{(() => {
|
||||||
const mw = store.skillTiers?.manaWell || 1;
|
const mw = skillTiers?.manaWell || 1;
|
||||||
const tieredSkillId = mw > 1 ? `manaWell_t${mw}` : 'manaWell';
|
const tieredSkillId = mw > 1 ? `manaWell_t${mw}` : 'manaWell';
|
||||||
const level = store.skills[tieredSkillId] || store.skills.manaWell || 0;
|
const level = skills[tieredSkillId] || skills.manaWell || 0;
|
||||||
const tierMult = getTierMultiplier(tieredSkillId);
|
const tierMult = getTierMultiplier(tieredSkillId);
|
||||||
return `+${fmt(level * 100 * tierMult)} (${level} lvl × 100 × ${tierMult}x tier)`;
|
return `+${fmt(level * 100 * tierMult)} (${level} lvl × 100 × ${tierMult}x tier)`;
|
||||||
})()}
|
})()}
|
||||||
@@ -70,7 +77,7 @@ export function ManaStatsSection({
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between text-sm">
|
<div className="flex justify-between text-sm">
|
||||||
<span className="text-gray-400">Prestige Mana Well:</span>
|
<span className="text-gray-400">Prestige Mana Well:</span>
|
||||||
<span className="text-blue-300">+{fmt((store.prestigeUpgrades.manaWell || 0) * 500)}</span>
|
<span className="text-blue-300">+{fmt((prestigeUpgrades.manaWell || 0) * 500)}</span>
|
||||||
</div>
|
</div>
|
||||||
{upgradeEffects.maxManaBonus > 0 && (
|
{upgradeEffects.maxManaBonus > 0 && (
|
||||||
<div className="flex justify-between text-sm">
|
<div className="flex justify-between text-sm">
|
||||||
@@ -98,9 +105,9 @@ export function ManaStatsSection({
|
|||||||
<span className="text-gray-400">Mana Flow Bonus:</span>
|
<span className="text-gray-400">Mana Flow Bonus:</span>
|
||||||
<span className="text-blue-300">
|
<span className="text-blue-300">
|
||||||
{(() => {
|
{(() => {
|
||||||
const mf = store.skillTiers?.manaFlow || 1;
|
const mf = skillTiers?.manaFlow || 1;
|
||||||
const tieredSkillId = mf > 1 ? `manaFlow_t${mf}` : 'manaFlow';
|
const tieredSkillId = mf > 1 ? `manaFlow_t${mf}` : 'manaFlow';
|
||||||
const level = store.skills[tieredSkillId] || store.skills.manaFlow || 0;
|
const level = skills[tieredSkillId] || skills.manaFlow || 0;
|
||||||
const tierMult = getTierMultiplier(tieredSkillId);
|
const tierMult = getTierMultiplier(tieredSkillId);
|
||||||
return `+${fmtDec(level * 1 * tierMult)}/hr (${level} lvl × 1 × ${tierMult}x tier)`;
|
return `+${fmtDec(level * 1 * tierMult)}/hr (${level} lvl × 1 × ${tierMult}x tier)`;
|
||||||
})()}
|
})()}
|
||||||
@@ -108,15 +115,15 @@ export function ManaStatsSection({
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between text-sm">
|
<div className="flex justify-between text-sm">
|
||||||
<span className="text-gray-400">Mana Spring Bonus:</span>
|
<span className="text-gray-400">Mana Spring Bonus:</span>
|
||||||
<span className="text-blue-300">+{(store.skills.manaSpring || 0) * 2}/hr</span>
|
<span className="text-blue-300">+{(skills.manaSpring || 0) * 2}/hr</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between text-sm">
|
<div className="flex justify-between text-sm">
|
||||||
<span className="text-gray-400">Prestige Mana Flow:</span>
|
<span className="text-gray-400">Prestige Mana Flow:</span>
|
||||||
<span className="text-blue-300">+{fmtDec((store.prestigeUpgrades.manaFlow || 0) * 0.5)}/hr</span>
|
<span className="text-blue-300">+{fmtDec((prestigeUpgrades.manaFlow || 0) * 0.5)}/hr</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between text-sm">
|
<div className="flex justify-between text-sm">
|
||||||
<span className="text-gray-400">Temporal Echo:</span>
|
<span className="text-gray-400">Temporal Echo:</span>
|
||||||
<span className="text-blue-300">×{fmtDec(1 + (store.prestigeUpgrades.temporalEcho || 0) * 0.1, 2)}</span>
|
<span className="text-blue-300">×{fmtDec(1 + (prestigeUpgrades.temporalEcho || 0) * 0.1, 2)}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between text-sm font-semibold border-t border-gray-700 pt-2">
|
<div className="flex justify-between text-sm font-semibold border-t border-gray-700 pt-2">
|
||||||
<span className="text-gray-300">Base Regen:</span>
|
<span className="text-gray-300">Base Regen:</span>
|
||||||
@@ -167,26 +174,28 @@ export function ManaStatsSection({
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between text-sm">
|
<div className="flex justify-between text-sm">
|
||||||
<span className="text-gray-400">Mana Tap Bonus:</span>
|
<span className="text-gray-400">Mana Tap Bonus:</span>
|
||||||
<span className="text-purple-300">+{store.skills.manaTap || 0}</span>
|
<span className="text-purple-300">+{skills.manaTap || 0}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between text-sm">
|
<div className="flex justify-between text-sm">
|
||||||
<span className="text-gray-400">Mana Surge Bonus:</span>
|
<span className="text-gray-400">Mana Surge Bonus:</span>
|
||||||
<span className="text-purple-300">+{(store.skills.manaSurge || 0) * 3}</span>
|
<span className="text-purple-300">+{(skills.manaSurge || 0) * 3}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between text-sm">
|
<div className="flex justify-between text-sm">
|
||||||
<span className="text-gray-400">Mana Overflow:</span>
|
<span className="text-gray-400">Mana Overflow:</span>
|
||||||
<span className="text-purple-300">×{fmtDec(1 + (store.skills.manaOverflow || 0) * 0.25, 2)}</span>
|
<span className="text-purple-300">×{fmtDec(1 + (skills.manaOverflow || 0) * 0.25, 2)}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<div className="flex justify-between text-sm">
|
<div className="flex justify-between text-sm">
|
||||||
<span className="text-gray-400">Meditation Multiplier:</span>
|
<span className={`font-semibold ${meditationMultiplier > 1.5 ? 'text-purple-400' : 'text-gray-300'}`}>
|
||||||
|
Meditation Multiplier:
|
||||||
|
</span>
|
||||||
<span className={`font-semibold ${meditationMultiplier > 1.5 ? 'text-purple-400' : 'text-gray-300'}`}>
|
<span className={`font-semibold ${meditationMultiplier > 1.5 ? 'text-purple-400' : 'text-gray-300'}`}>
|
||||||
{fmtDec(meditationMultiplier, 2)}x
|
{fmtDec(meditationMultiplier, 2)}x
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between text-sm">
|
<div className="flex justify-between text-sm">
|
||||||
<span className="text-gray-400">Effective Regen:</span>
|
<span className="text-gray-300">Effective Regen:</span>
|
||||||
<span className="text-green-400 font-semibold">{fmtDec(effectiveRegen, 2)}/hr</span>
|
<span className="text-green-400 font-semibold">{fmtDec(effectiveRegen, 2)}/hr</span>
|
||||||
</div>
|
</div>
|
||||||
{incursionStrength > 0 && !hasSpecial(upgradeEffects, SPECIAL_EFFECTS.STEADY_STREAM) && (
|
{incursionStrength > 0 && !hasSpecial(upgradeEffects, SPECIAL_EFFECTS.STEADY_STREAM) && (
|
||||||
@@ -237,13 +246,13 @@ export function ManaStatsSection({
|
|||||||
<span className="text-green-400">Regen immune to ALL penalties</span>
|
<span className="text-green-400">Regen immune to ALL penalties</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{hasSpecial(upgradeEffects, SPECIAL_EFFECTS.MANA_TORRENT) && store.rawMana > maxMana * 0.75 && (
|
{hasSpecial(upgradeEffects, SPECIAL_EFFECTS.MANA_TORRENT) && rawMana > maxMana * 0.75 && (
|
||||||
<div className="flex justify-between text-sm">
|
<div className="flex justify-between text-sm">
|
||||||
<span className="text-cyan-400">Mana Torrent:</span>
|
<span className="text-cyan-400">Mana Torrent:</span>
|
||||||
<span className="text-cyan-400">+50% regen (high mana)</span>
|
<span className="text-cyan-400">+50% regen (high mana)</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{hasSpecial(upgradeEffects, SPECIAL_EFFECTS.DESPERATE_WELLS) && store.rawMana < maxMana * 0.25 && (
|
{hasSpecial(upgradeEffects, SPECIAL_EFFECTS.DESPERATE_WELLS) && rawMana < maxMana * 0.25 && (
|
||||||
<div className="flex justify-between text-sm">
|
<div className="flex justify-between text-sm">
|
||||||
<span className="text-cyan-400">Desperate Wells:</span>
|
<span className="text-cyan-400">Desperate Wells:</span>
|
||||||
<span className="text-cyan-400">+50% regen (low mana)</span>
|
<span className="text-cyan-400">+50% regen (low mana)</span>
|
||||||
|
|||||||
@@ -1,37 +1,68 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { ELEMENTS } from '@/lib/game/constants';
|
import { ELEMENTS } from '@/lib/game/constants';
|
||||||
import { fmt, fmtDec } from '@/lib/game/store';
|
import { fmt, fmtDec } from '@/lib/game/stores';
|
||||||
import type { GameStore } from '@/lib/game/types';
|
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
|
||||||
import { ATTUNEMENTS_DEF, getAttunementConversionRate } from '@/lib/game/data/attunements';
|
import { ATTUNEMENTS_DEF, getAttunementConversionRate } from '@/lib/game/data/attunements';
|
||||||
import { computeMaxMana, computeElementMax } from '@/lib/game/store';
|
import { computeMaxMana, computeElementMax } from '@/lib/game/stores';
|
||||||
import { computeEffectiveRegenForDisplay } from '@/lib/game/store-modules/computed-stats';
|
import { computeEffectiveRegenForDisplay } from '@/lib/game/store-modules/computed-stats';
|
||||||
import { getUnifiedEffects } from '@/lib/game/effects';
|
import { getUnifiedEffects } from '@/lib/game/effects';
|
||||||
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||||
import { Droplet } from 'lucide-react';
|
import { Droplet } from 'lucide-react';
|
||||||
import { Separator } from '@/components/ui/separator';
|
import { Separator } from '@/components/ui/separator';
|
||||||
|
|
||||||
export interface ManaTypeBreakdownProps {
|
// Modular stores
|
||||||
store: GameStore;
|
import { useCombatStore, useManaStore, useSkillStore, usePrestigeStore } from '@/lib/game/stores';
|
||||||
}
|
|
||||||
|
export function ManaTypeBreakdown() {
|
||||||
|
// Get state from modular stores
|
||||||
|
const skills = useSkillStore((s) => s.skills);
|
||||||
|
const skillTiers = useSkillStore((s) => s.skillTiers);
|
||||||
|
const skillUpgrades = useSkillStore((s) => s.skillUpgrades);
|
||||||
|
|
||||||
|
const prestigeUpgrades = usePrestigeStore((s) => s.prestigeUpgrades);
|
||||||
|
const elements = useManaStore((s) => s.elements);
|
||||||
|
const rawMana = useManaStore((s) => s.rawMana);
|
||||||
|
const attunements = useCombatStore((s) => s.attunements);
|
||||||
|
|
||||||
export function ManaTypeBreakdown({ store }: ManaTypeBreakdownProps) {
|
|
||||||
// Compute unified effects for regen calculations
|
// Compute unified effects for regen calculations
|
||||||
const effects = getUnifiedEffects(store);
|
const effects = getUnifiedEffects({
|
||||||
|
skillUpgrades,
|
||||||
|
skillTiers,
|
||||||
|
equippedInstances: {},
|
||||||
|
equipmentInstances: {}
|
||||||
|
});
|
||||||
|
|
||||||
// Get effective regen info for raw mana
|
// Get effective regen info for raw mana
|
||||||
const regenInfo = computeEffectiveRegenForDisplay(store, effects);
|
const regenInfo = computeEffectiveRegenForDisplay({
|
||||||
|
skills,
|
||||||
|
prestigeUpgrades,
|
||||||
|
skillUpgrades,
|
||||||
|
skillTiers,
|
||||||
|
elements,
|
||||||
|
rawMana,
|
||||||
|
attunements
|
||||||
|
} as any, effects);
|
||||||
|
|
||||||
// Compute max mana
|
// Compute max mana
|
||||||
const maxMana = computeMaxMana(store, effects);
|
const maxMana = computeMaxMana({
|
||||||
|
skills,
|
||||||
|
prestigeUpgrades,
|
||||||
|
skillUpgrades,
|
||||||
|
skillTiers
|
||||||
|
}, effects);
|
||||||
|
|
||||||
// Get unlocked elements sorted by category then name
|
// Get unlocked elements sorted by category then name
|
||||||
const unlockedElements = Object.entries(store.elements)
|
const unlockedElements = Object.entries(elements)
|
||||||
.filter(([, state]) => state.unlocked)
|
.filter(([, state]) => state.unlocked)
|
||||||
.map(([id, state]) => {
|
.map(([id, state]) => {
|
||||||
const def = ELEMENTS[id];
|
const def = ELEMENTS[id];
|
||||||
if (!def) return null;
|
if (!def) return null;
|
||||||
const elemMax = computeElementMax(store, effects, id);
|
const elemMax = computeElementMax({
|
||||||
|
skills,
|
||||||
|
prestigeUpgrades,
|
||||||
|
skillUpgrades,
|
||||||
|
skillTiers
|
||||||
|
} as any, effects, id);
|
||||||
return {
|
return {
|
||||||
id,
|
id,
|
||||||
name: def.name,
|
name: def.name,
|
||||||
@@ -69,7 +100,7 @@ export function ManaTypeBreakdown({ store }: ManaTypeBreakdownProps) {
|
|||||||
<span className="text-xs text-gray-500">(base)</span>
|
<span className="text-xs text-gray-500">(base)</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-sm">
|
<div className="text-sm">
|
||||||
<span className="text-gray-300">{fmt(store.rawMana)}</span>
|
<span className="text-gray-300">{fmt(rawMana)}</span>
|
||||||
<span className="text-gray-500"> / </span>
|
<span className="text-gray-500"> / </span>
|
||||||
<span className="text-gray-400">{fmt(maxMana)}</span>
|
<span className="text-gray-400">{fmt(maxMana)}</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -79,7 +110,7 @@ export function ManaTypeBreakdown({ store }: ManaTypeBreakdownProps) {
|
|||||||
<div className="w-full bg-gray-700 rounded-full h-2 mb-3">
|
<div className="w-full bg-gray-700 rounded-full h-2 mb-3">
|
||||||
<div
|
<div
|
||||||
className="h-2 rounded-full transition-all duration-300 bg-purple-500"
|
className="h-2 rounded-full transition-all duration-300 bg-purple-500"
|
||||||
style={{ width: `${Math.min(100, (store.rawMana / maxMana) * 100)}%` }}
|
style={{ width: `${Math.min(100, (rawMana / maxMana) * 100)}%` }}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -102,13 +133,15 @@ export function ManaTypeBreakdown({ store }: ManaTypeBreakdownProps) {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Show conversion drains by attunement */}
|
{/* Show conversion drains by attunement */}
|
||||||
{store.conversionDrains && Object.keys(store.conversionDrains).length > 0 && (
|
{attunements && Object.keys(attunements).length > 0 && (
|
||||||
<>
|
<>
|
||||||
<Separator className="bg-gray-700 my-1" />
|
<Separator className="bg-gray-700 my-1" />
|
||||||
<div className="text-gray-400 mb-1">Conversion Drains:</div>
|
<div className="text-gray-400 mb-1">Conversion Drains:</div>
|
||||||
{Object.entries(store.conversionDrains).map(([attId, rate]) => {
|
{Object.entries(attunements).map(([attId, attState]) => {
|
||||||
const attDef = ATTUNEMENTS_DEF[attId];
|
const attDef = ATTUNEMENTS_DEF[attId];
|
||||||
if (!attDef || rate <= 0) return null;
|
if (!attDef || attState.level === 0) return null;
|
||||||
|
const rate = getAttunementConversionRate(attId, attState.level);
|
||||||
|
if (rate <= 0) return null;
|
||||||
return (
|
return (
|
||||||
<div key={attId} className="flex justify-between pl-2">
|
<div key={attId} className="flex justify-between pl-2">
|
||||||
<span className="text-gray-500">{attDef.name}:</span>
|
<span className="text-gray-500">{attDef.name}:</span>
|
||||||
@@ -128,7 +161,7 @@ export function ManaTypeBreakdown({ store }: ManaTypeBreakdownProps) {
|
|||||||
if (!elem) return null;
|
if (!elem) return null;
|
||||||
|
|
||||||
// Find attunements that convert TO this element
|
// Find attunements that convert TO this element
|
||||||
const convertingAttunements = Object.entries(store.attunements || {})
|
const convertingAttunements = Object.entries(attunements || {})
|
||||||
.filter(([attId, attState]) => {
|
.filter(([attId, attState]) => {
|
||||||
if (!attState.active) return false;
|
if (!attState.active) return false;
|
||||||
const attDef = ATTUNEMENTS_DEF[attId];
|
const attDef = ATTUNEMENTS_DEF[attId];
|
||||||
@@ -177,7 +210,7 @@ export function ManaTypeBreakdown({ store }: ManaTypeBreakdownProps) {
|
|||||||
<div className="text-gray-500 pl-2">
|
<div className="text-gray-500 pl-2">
|
||||||
Source: {convertingAttunements.map(([attId]) => {
|
Source: {convertingAttunements.map(([attId]) => {
|
||||||
const attDef = ATTUNEMENTS_DEF[attId];
|
const attDef = ATTUNEMENTS_DEF[attId];
|
||||||
const level = store.attunements[attId]?.level || 1;
|
const level = attunements[attId]?.level || 1;
|
||||||
return `${attDef?.name} (Lv.${level})`;
|
return `${attDef?.name} (Lv.${level})`;
|
||||||
}).join(', ')}
|
}).join(', ')}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,17 +1,22 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { fmtDec } from '@/lib/game/store';
|
import { fmtDec } from '@/lib/game/stores';
|
||||||
import type { GameStore } from '@/lib/game/types';
|
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||||
import { BookOpen } from 'lucide-react';
|
import { BookOpen } from 'lucide-react';
|
||||||
|
|
||||||
|
// Modular stores
|
||||||
|
import { useSkillStore, usePrestigeStore } from '@/lib/game/stores';
|
||||||
|
|
||||||
export interface StudyStatsSectionProps {
|
export interface StudyStatsSectionProps {
|
||||||
store: GameStore;
|
|
||||||
studySpeedMult: number;
|
studySpeedMult: number;
|
||||||
studyCostMult: number;
|
studyCostMult: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function StudyStatsSection({ store, studySpeedMult, studyCostMult }: StudyStatsSectionProps) {
|
export function StudyStatsSection({ studySpeedMult, studyCostMult }: StudyStatsSectionProps) {
|
||||||
|
// Get state from modular stores
|
||||||
|
const skills = useSkillStore((s) => s.skills);
|
||||||
|
const prestigeUpgrades = usePrestigeStore((s) => s.prestigeUpgrades);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card className="bg-gray-900/80 border-gray-700">
|
<Card className="bg-gray-900/80 border-gray-700">
|
||||||
<CardHeader className="pb-2">
|
<CardHeader className="pb-2">
|
||||||
@@ -29,7 +34,7 @@ export function StudyStatsSection({ store, studySpeedMult, studyCostMult }: Stud
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between text-sm">
|
<div className="flex justify-between text-sm">
|
||||||
<span className="text-gray-400">Quick Learner Bonus:</span>
|
<span className="text-gray-400">Quick Learner Bonus:</span>
|
||||||
<span className="text-purple-300">+{((store.skills.quickLearner || 0) * 10)}%</span>
|
<span className="text-purple-300">+{((skills.quickLearner || 0) * 10)}%</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
@@ -39,13 +44,13 @@ export function StudyStatsSection({ store, studySpeedMult, studyCostMult }: Stud
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between text-sm">
|
<div className="flex justify-between text-sm">
|
||||||
<span className="text-gray-400">Focused Mind Bonus:</span>
|
<span className="text-gray-400">Focused Mind Bonus:</span>
|
||||||
<span className="text-purple-300">-{((store.skills.focusedMind || 0) * 5)}%</span>
|
<span className="text-purple-300">-{((skills.focusedMind || 0) * 5)}%</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<div className="flex justify-between text-sm">
|
<div className="flex justify-between text-sm">
|
||||||
<span className="text-gray-400">Progress Retention:</span>
|
<span className="text-gray-400">Progress Retention:</span>
|
||||||
<span className="text-purple-300">{Math.round((1 + (store.skills.knowledgeRetention || 0) * 0.2) * 100)}%</span>
|
<span className="text-purple-300">{Math.round((1 + (skills.knowledgeRetention || 0) * 0.2) * 100)}%</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,20 +1,23 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { SKILL_EVOLUTION_PATHS, getTierMultiplier } from '@/lib/game/skill-evolution';
|
import { SKILL_EVOLUTION_PATHS } from '@/lib/game/skill-evolution';
|
||||||
import { SKILLS_DEF } from '@/lib/game/constants';
|
import { SKILLS_DEF } from '@/lib/game/constants';
|
||||||
import type { GameStore, SkillUpgradeChoice } from '@/lib/game/types';
|
import type { SkillUpgradeChoice } from '@/lib/game/types';
|
||||||
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 { Star } from 'lucide-react';
|
import { Star } from 'lucide-react';
|
||||||
|
|
||||||
export interface UpgradeEffectsSectionProps {
|
// Modular stores
|
||||||
store: GameStore;
|
import { useSkillStore } from '@/lib/game/stores';
|
||||||
}
|
|
||||||
|
export function UpgradeEffectsSection() {
|
||||||
|
// Get state from modular stores
|
||||||
|
const skillUpgrades = useSkillStore((s) => s.skillUpgrades);
|
||||||
|
|
||||||
// Helper function to get all selected skill upgrades
|
// Helper function to get all selected skill upgrades
|
||||||
function getAllSelectedUpgrades(store: GameStore) {
|
function getAllSelectedUpgrades() {
|
||||||
const upgrades: { skillId: string; upgrade: SkillUpgradeChoice }[] = [];
|
const upgrades = [];
|
||||||
for (const [skillId, selectedIds] of Object.entries(store.skillUpgrades)) {
|
for (const [skillId, selectedIds] of Object.entries(skillUpgrades)) {
|
||||||
const baseSkillId = skillId.includes('_t') ? skillId.split('_t')[0] : skillId;
|
const baseSkillId = skillId.includes('_t') ? skillId.split('_t')[0] : skillId;
|
||||||
const path = SKILL_EVOLUTION_PATHS[baseSkillId];
|
const path = SKILL_EVOLUTION_PATHS[baseSkillId];
|
||||||
if (!path) continue;
|
if (!path) continue;
|
||||||
@@ -32,13 +35,12 @@ function getAllSelectedUpgrades(store: GameStore) {
|
|||||||
return upgrades;
|
return upgrades;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function UpgradeEffectsSection({ store }: UpgradeEffectsSectionProps) {
|
const selectedUpgrades = getAllSelectedUpgrades();
|
||||||
const selectedUpgrades = getAllSelectedUpgrades(store);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card className="bg-gray-900/80 border-gray-700">
|
<Card className="bg-gray-900/80 border-gray-700">
|
||||||
<CardHeader className="pb-2">
|
<CardHeader className="pb-2">
|
||||||
<CardTitle className="text-amber-400 game-panel-title text-xs flex items-center gap-2">
|
<CardTitle className="text-amber-400 text-sm flex items-center gap-2">
|
||||||
<Star className="w-4 h-4" />
|
<Star className="w-4 h-4" />
|
||||||
Active Skill Upgrades ({selectedUpgrades.length})
|
Active Skill Upgrades ({selectedUpgrades.length})
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
@@ -57,17 +59,17 @@ export function UpgradeEffectsSection({ store }: UpgradeEffectsSectionProps) {
|
|||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-xs text-gray-400 mt-1">{upgrade.desc}</div>
|
<div className="text-xs text-gray-400 mt-1">{upgrade.desc}</div>
|
||||||
{upgrade.effect.type === 'multiplier' && (
|
{upgrade.effect && upgrade.effect.type === 'multiplier' && (
|
||||||
<div className="text-xs text-green-400 mt-1">
|
<div className="text-xs text-green-400 mt-1">
|
||||||
+{Math.round((upgrade.effect.value! - 1) * 100)}% {upgrade.effect.stat}
|
+{Math.round((upgrade.effect.value - 1) * 100)}% {upgrade.effect.stat}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{upgrade.effect.type === 'bonus' && (
|
{upgrade.effect && upgrade.effect.type === 'bonus' && (
|
||||||
<div className="text-xs text-blue-400 mt-1">
|
<div className="text-xs text-blue-400 mt-1">
|
||||||
+{upgrade.effect.value} {upgrade.effect.stat}
|
+{upgrade.effect.value} {upgrade.effect.stat}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{upgrade.effect.type === 'special' && (
|
{upgrade.effect && upgrade.effect.type === 'special' && (
|
||||||
<div className="text-xs text-cyan-400 mt-1">
|
<div className="text-xs text-cyan-400 mt-1">
|
||||||
⚡ {upgrade.effect.specialDesc || 'Special effect active'}
|
⚡ {upgrade.effect.specialDesc || 'Special effect active'}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,31 +1,26 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import type { GameStore } from '@/lib/game/store';
|
import { GameStateDebug } from '@/components/game/debug/GameStateDebug';
|
||||||
import {
|
import {
|
||||||
GameStateDebug,
|
|
||||||
SkillDebug,
|
SkillDebug,
|
||||||
ElementDebug,
|
|
||||||
AttunementDebug,
|
AttunementDebug,
|
||||||
|
ElementDebug,
|
||||||
GolemDebug,
|
GolemDebug,
|
||||||
PactDebug
|
PactDebug
|
||||||
} from '@/components/game/debug';
|
} from '@/components/game/debug';
|
||||||
|
|
||||||
interface DebugTabProps {
|
export function DebugTab() {
|
||||||
store: GameStore;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function DebugTab({ store }: DebugTabProps) {
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<GameStateDebug store={store} />
|
<GameStateDebug />
|
||||||
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
<AttunementDebug store={store} />
|
<AttunementDebug />
|
||||||
<ElementDebug store={store} />
|
<ElementDebug />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<SkillDebug store={store} />
|
<SkillDebug />
|
||||||
<GolemDebug store={store} />
|
<GolemDebug />
|
||||||
<PactDebug />
|
<PactDebug />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
Executable → Regular
+115
-65
@@ -3,8 +3,9 @@
|
|||||||
import { ELEMENTS, GUARDIANS } from '@/lib/game/constants';
|
import { ELEMENTS, GUARDIANS } from '@/lib/game/constants';
|
||||||
import { getTierMultiplier } from '@/lib/game/skill-evolution';
|
import { getTierMultiplier } from '@/lib/game/skill-evolution';
|
||||||
import { hasSpecial, SPECIAL_EFFECTS } from '@/lib/game/effects';
|
import { hasSpecial, SPECIAL_EFFECTS } from '@/lib/game/effects';
|
||||||
import { fmt, fmtDec } from '@/lib/game/store';
|
import { getUnifiedEffects } from '@/lib/game/effects';
|
||||||
import type { GameStore, UnifiedEffects } from '@/lib/game/types';
|
import { fmt, fmtDec, computeMaxMana, computeRegen, computeClickMana, getMeditationBonus, getIncursionStrength, getStudySpeedMultiplier, getStudyCostMultiplier } from '@/lib/game/stores';
|
||||||
|
import type { UnifiedEffects } from '@/lib/game/types';
|
||||||
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';
|
||||||
@@ -15,57 +16,107 @@ import { CombatStatsSection } from '../stats/CombatStatsSection';
|
|||||||
import { StudyStatsSection } from '../stats/StudyStatsSection';
|
import { StudyStatsSection } from '../stats/StudyStatsSection';
|
||||||
import { UpgradeEffectsSection } from '../stats/UpgradeEffectsSection';
|
import { UpgradeEffectsSection } from '../stats/UpgradeEffectsSection';
|
||||||
|
|
||||||
export interface StatsTabProps {
|
// Modular stores
|
||||||
store: GameStore;
|
import { useCombatStore, useManaStore, useSkillStore, usePrestigeStore, useGameStore } from '@/lib/game/stores';
|
||||||
upgradeEffects: UnifiedEffects;
|
|
||||||
maxMana: number;
|
export function StatsTab() {
|
||||||
baseRegen: number;
|
// Get state from modular stores
|
||||||
clickMana: number;
|
const skills = useSkillStore((s) => s.skills);
|
||||||
meditationMultiplier: number;
|
const skillTiers = useSkillStore((s) => s.skillTiers);
|
||||||
effectiveRegen: number;
|
const skillUpgrades = useSkillStore((s) => s.skillUpgrades);
|
||||||
incursionStrength: number;
|
|
||||||
manaCascadeBonus: number;
|
const prestigeUpgrades = usePrestigeStore((s) => s.prestigeUpgrades);
|
||||||
manaWaterfallBonus: number;
|
const loopCount = usePrestigeStore((s) => s.loopCount);
|
||||||
hasManaWaterfall: boolean;
|
const insight = usePrestigeStore((s) => s.insight);
|
||||||
hasFlowSurge: boolean;
|
const totalInsight = usePrestigeStore((s) => s.totalInsight);
|
||||||
hasManaOverflow: boolean;
|
const memorySlots = usePrestigeStore((s) => s.memorySlots);
|
||||||
hasEternalFlow: boolean;
|
|
||||||
studySpeedMult: number;
|
const elements = useManaStore((s) => s.elements);
|
||||||
studyCostMult: number;
|
const totalManaGathered = useManaStore((s) => s.totalManaGathered);
|
||||||
}
|
const rawMana = useManaStore((s) => s.rawMana);
|
||||||
|
const meditateTicks = useManaStore((s) => s.meditateTicks);
|
||||||
|
|
||||||
|
const signedPacts = useCombatStore((s) => s.signedPacts);
|
||||||
|
const maxFloorReached = useCombatStore((s) => s.maxFloorReached);
|
||||||
|
const spells = useCombatStore((s) => s.spells);
|
||||||
|
|
||||||
|
// Compute unified effects
|
||||||
|
const upgradeEffects = getUnifiedEffects({
|
||||||
|
skillUpgrades,
|
||||||
|
skillTiers,
|
||||||
|
equippedInstances: {},
|
||||||
|
equipmentInstances: {}
|
||||||
|
}) as UnifiedEffects;
|
||||||
|
|
||||||
|
// Compute derived stats
|
||||||
|
const maxMana = computeMaxMana({
|
||||||
|
skills,
|
||||||
|
prestigeUpgrades,
|
||||||
|
skillUpgrades,
|
||||||
|
skillTiers
|
||||||
|
}, upgradeEffects);
|
||||||
|
|
||||||
|
const baseRegen = computeRegen({
|
||||||
|
skills,
|
||||||
|
prestigeUpgrades,
|
||||||
|
skillUpgrades,
|
||||||
|
skillTiers
|
||||||
|
}, upgradeEffects);
|
||||||
|
|
||||||
|
const clickMana = computeClickMana({
|
||||||
|
skills,
|
||||||
|
prestigeUpgrades,
|
||||||
|
skillUpgrades,
|
||||||
|
skillTiers
|
||||||
|
});
|
||||||
|
|
||||||
|
// Get meditation multiplier
|
||||||
|
const meditationMultiplier = getMeditationBonus(meditateTicks, skills, upgradeEffects.meditationEfficiency);
|
||||||
|
|
||||||
|
// Get incursion strength
|
||||||
|
const day = useGameStore((s) => s.day);
|
||||||
|
const hour = useGameStore((s) => s.hour);
|
||||||
|
const incursionStrength = getIncursionStrength(day, hour);
|
||||||
|
|
||||||
|
// Effective regen with incursion penalty
|
||||||
|
const effectiveRegenWithSpecials = baseRegen * (1 - incursionStrength);
|
||||||
|
|
||||||
|
// Mana Cascade bonus
|
||||||
|
const manaCascadeBonus = hasSpecial(upgradeEffects, SPECIAL_EFFECTS.MANA_CASCADE)
|
||||||
|
? Math.floor(maxMana / 100) * 0.1
|
||||||
|
: 0;
|
||||||
|
|
||||||
|
// Mana Waterfall bonus
|
||||||
|
const manaWaterfallBonus = hasSpecial(upgradeEffects, SPECIAL_EFFECTS.MANA_WATERFALL)
|
||||||
|
? Math.floor(maxMana / 100) * 0.25
|
||||||
|
: 0;
|
||||||
|
|
||||||
|
// Effective regen
|
||||||
|
const effectiveRegen = (effectiveRegenWithSpecials + manaCascadeBonus + manaWaterfallBonus) * meditationMultiplier;
|
||||||
|
|
||||||
|
// Get study speed/cost multipliers
|
||||||
|
const studySpeedMult = getStudySpeedMultiplier(skills, skillUpgrades, skillTiers);
|
||||||
|
const studyCostMult = getStudyCostMultiplier(skills, skillUpgrades, skillTiers);
|
||||||
|
|
||||||
|
// Check special effects
|
||||||
|
const hasManaWaterfall = hasSpecial(upgradeEffects, SPECIAL_EFFECTS.MANA_WATERFALL);
|
||||||
|
const hasFlowSurge = hasSpecial(upgradeEffects, SPECIAL_EFFECTS.FLOW_SURGE);
|
||||||
|
const hasManaOverflow = hasSpecial(upgradeEffects, SPECIAL_EFFECTS.MANA_OVERFLOW);
|
||||||
|
const hasEternalFlow = hasSpecial(upgradeEffects, SPECIAL_EFFECTS.ETERNAL_FLOW);
|
||||||
|
|
||||||
export function StatsTab({
|
|
||||||
store,
|
|
||||||
upgradeEffects,
|
|
||||||
maxMana,
|
|
||||||
baseRegen,
|
|
||||||
clickMana,
|
|
||||||
meditationMultiplier,
|
|
||||||
effectiveRegen,
|
|
||||||
incursionStrength,
|
|
||||||
manaCascadeBonus,
|
|
||||||
manaWaterfallBonus,
|
|
||||||
hasManaWaterfall,
|
|
||||||
hasFlowSurge,
|
|
||||||
hasManaOverflow,
|
|
||||||
hasEternalFlow,
|
|
||||||
studySpeedMult,
|
|
||||||
studyCostMult,
|
|
||||||
}: StatsTabProps) {
|
|
||||||
// Compute element max
|
// Compute element max
|
||||||
const elemMax = (() => {
|
const elemMax = (() => {
|
||||||
const ea = store.skillTiers?.elemAttune || 1;
|
const ea = skillTiers?.elemAttune || 1;
|
||||||
const tieredSkillId = ea > 1 ? `elemAttune_t${ea}` : 'elemAttune';
|
const tieredSkillId = ea > 1 ? `elemAttune_t${ea}` : 'elemAttune';
|
||||||
const level = store.skills[tieredSkillId] || store.skills.elemAttune || 0;
|
const level = skills[tieredSkillId] || skills.elemAttune || 0;
|
||||||
const tierMult = getTierMultiplier(tieredSkillId);
|
const tierMult = getTierMultiplier(tieredSkillId);
|
||||||
return 10 + level * 50 * tierMult + (store.prestigeUpgrades.elementalAttune || 0) * 25;
|
return 10 + level * 50 * tierMult + (prestigeUpgrades.elementalAttune || 0) * 25;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
{/* Mana Stats */}
|
{/* Mana Stats */}
|
||||||
<ManaStatsSection
|
<ManaStatsSection
|
||||||
store={store}
|
|
||||||
upgradeEffects={upgradeEffects}
|
upgradeEffects={upgradeEffects}
|
||||||
maxMana={maxMana}
|
maxMana={maxMana}
|
||||||
baseRegen={baseRegen}
|
baseRegen={baseRegen}
|
||||||
@@ -82,14 +133,13 @@ export function StatsTab({
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Mana Type Breakdown */}
|
{/* Mana Type Breakdown */}
|
||||||
<ManaTypeBreakdown store={store} />
|
<ManaTypeBreakdown />
|
||||||
|
|
||||||
{/* Combat Stats */}
|
{/* Combat Stats */}
|
||||||
<CombatStatsSection store={store} />
|
<CombatStatsSection />
|
||||||
|
|
||||||
{/* Study Stats */}
|
{/* Study Stats */}
|
||||||
<StudyStatsSection
|
<StudyStatsSection
|
||||||
store={store}
|
|
||||||
studySpeedMult={studySpeedMult}
|
studySpeedMult={studySpeedMult}
|
||||||
studyCostMult={studyCostMult}
|
studyCostMult={studyCostMult}
|
||||||
/>
|
/>
|
||||||
@@ -113,9 +163,9 @@ export function StatsTab({
|
|||||||
<span className="text-gray-400">Elem. Attunement Bonus:</span>
|
<span className="text-gray-400">Elem. Attunement Bonus:</span>
|
||||||
<span className="text-green-300">
|
<span className="text-green-300">
|
||||||
{(() => {
|
{(() => {
|
||||||
const ea = store.skillTiers?.elemAttune || 1;
|
const ea = skillTiers?.elemAttune || 1;
|
||||||
const tieredSkillId = ea > 1 ? `elemAttune_t${ea}` : 'elemAttune';
|
const tieredSkillId = ea > 1 ? `elemAttune_t${ea}` : 'elemAttune';
|
||||||
const level = store.skills[tieredSkillId] || store.skills.elemAttune || 0;
|
const level = skills[tieredSkillId] || skills.elemAttune || 0;
|
||||||
const tierMult = getTierMultiplier(tieredSkillId);
|
const tierMult = getTierMultiplier(tieredSkillId);
|
||||||
return `+${level * 50 * tierMult}`;
|
return `+${level * 50 * tierMult}`;
|
||||||
})()}
|
})()}
|
||||||
@@ -123,24 +173,24 @@ export function StatsTab({
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between text-sm">
|
<div className="flex justify-between text-sm">
|
||||||
<span className="text-gray-400">Prestige Attunement:</span>
|
<span className="text-gray-400">Prestige Attunement:</span>
|
||||||
<span className="text-green-300">+{(store.prestigeUpgrades.elementalAttune || 0) * 25}</span>
|
<span className="text-green-300">+{(prestigeUpgrades.elementalAttune || 0) * 25}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<div className="flex justify-between text-sm">
|
<div className="flex justify-between text-sm">
|
||||||
<span className="text-gray-400">Unlocked Elements:</span>
|
<span className="text-gray-400">Unlocked Elements:</span>
|
||||||
<span className="text-green-300">{Object.values(store.elements).filter(e => e.unlocked).length} / {Object.keys(ELEMENTS).length}</span>
|
<span className="text-green-300">{Object.values(elements).filter(e => e.unlocked).length} / {Object.keys(ELEMENTS).length}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between text-sm">
|
<div className="flex justify-between text-sm">
|
||||||
<span className="text-gray-400">Elem. Crafting Bonus:</span>
|
<span className="text-gray-400">Elem. Crafting Bonus:</span>
|
||||||
<span className="text-green-300">×{fmtDec(1 + (store.skills.elemCrafting || 0) * 0.25, 2)}</span>
|
<span className="text-green-300">×{fmtDec(1 + (skills.elemCrafting || 0) * 0.25, 2)}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Separator className="bg-gray-700 my-3" />
|
<Separator className="bg-gray-700 my-3" />
|
||||||
<div className="text-xs text-gray-400 mb-2">Elemental Mana Pools:</div>
|
<div className="text-xs text-gray-400 mb-2">Elemental Mana Pools:</div>
|
||||||
<div className="grid grid-cols-4 sm:grid-cols-6 md:grid-cols-8 gap-2">
|
<div className="grid grid-cols-4 sm:grid-cols-6 md:grid-cols-8 gap-2">
|
||||||
{Object.entries(store.elements)
|
{Object.entries(elements)
|
||||||
.filter(([, state]) => state.unlocked)
|
.filter(([, state]) => state.unlocked)
|
||||||
.map(([id, state]) => {
|
.map(([id, state]) => {
|
||||||
const def = ELEMENTS[id];
|
const def = ELEMENTS[id];
|
||||||
@@ -156,9 +206,9 @@ export function StatsTab({
|
|||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
{/* Active Upgrades */}
|
{/* Active Upgrades */}
|
||||||
<UpgradeEffectsSection store={store} />
|
<UpgradeEffectsSection />
|
||||||
|
|
||||||
{/* Enchantment Power (placeholder for Task 5) */}
|
{/* Enchantment Power */}
|
||||||
<Card className="bg-gray-900/80 border-gray-700">
|
<Card className="bg-gray-900/80 border-gray-700">
|
||||||
<CardHeader className="pb-2">
|
<CardHeader className="pb-2">
|
||||||
<CardTitle className="text-blue-400 game-panel-title text-xs flex items-center gap-2">
|
<CardTitle className="text-blue-400 game-panel-title text-xs flex items-center gap-2">
|
||||||
@@ -183,15 +233,15 @@ export function StatsTab({
|
|||||||
<CardHeader className="pb-2">
|
<CardHeader className="pb-2">
|
||||||
<CardTitle className="text-amber-400 game-panel-title text-xs flex items-center gap-2">
|
<CardTitle className="text-amber-400 game-panel-title text-xs flex items-center gap-2">
|
||||||
<Trophy className="w-4 h-4" />
|
<Trophy className="w-4 h-4" />
|
||||||
Signed Pacts ({store.signedPacts.length}/10)
|
Signed Pacts ({signedPacts.length}/10)
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
{store.signedPacts.length === 0 ? (
|
{signedPacts.length === 0 ? (
|
||||||
<div className="text-gray-500 text-sm">No pacts signed yet. Defeat guardians to earn pacts.</div>
|
<div className="text-gray-500 text-sm">No pacts signed yet. Defeat guardians to earn pacts.</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
{store.signedPacts.map((floor) => {
|
{signedPacts.map((floor) => {
|
||||||
const guardian = GUARDIANS[floor];
|
const guardian = GUARDIANS[floor];
|
||||||
if (!guardian) return null;
|
if (!guardian) return null;
|
||||||
return (
|
return (
|
||||||
@@ -214,7 +264,7 @@ export function StatsTab({
|
|||||||
})}
|
})}
|
||||||
<div className="flex justify-between text-sm font-semibold border-t border-gray-700 pt-2 mt-2">
|
<div className="flex justify-between text-sm font-semibold border-t border-gray-700 pt-2 mt-2">
|
||||||
<span className="text-gray-300">Combined Pact Multiplier:</span>
|
<span className="text-gray-300">Combined Pact Multiplier:</span>
|
||||||
<span className="text-amber-400">×{fmtDec(store.signedPacts.reduce((m, f) => m * (GUARDIANS[f]?.pact || 1), 1), 2)}</span>
|
<span className="text-amber-400">×{fmtDec(signedPacts.reduce((m, f) => m * (GUARDIANS[f]?.pact || 1), 1), 2)}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -232,38 +282,38 @@ export function StatsTab({
|
|||||||
<CardContent>
|
<CardContent>
|
||||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
|
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
|
||||||
<div className="p-3 bg-gray-800/50 rounded text-center">
|
<div className="p-3 bg-gray-800/50 rounded text-center">
|
||||||
<div className="text-2xl font-bold text-amber-400 game-mono">{store.loopCount}</div>
|
<div className="text-2xl font-bold text-amber-400 game-mono">{loopCount}</div>
|
||||||
<div className="text-xs text-gray-400">Loops Completed</div>
|
<div className="text-xs text-gray-400">Loops Completed</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="p-3 bg-gray-800/50 rounded text-center">
|
<div className="p-3 bg-gray-800/50 rounded text-center">
|
||||||
<div className="text-2xl font-bold text-purple-400 game-mono">{fmt(store.insight)}</div>
|
<div className="text-2xl font-bold text-purple-400 game-mono">{fmt(insight)}</div>
|
||||||
<div className="text-xs text-gray-400">Current Insight</div>
|
<div className="text-xs text-gray-400">Current Insight</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="p-3 bg-gray-800/50 rounded text-center">
|
<div className="p-3 bg-gray-800/50 rounded text-center">
|
||||||
<div className="text-2xl font-bold text-blue-400 game-mono">{fmt(store.totalInsight)}</div>
|
<div className="text-2xl font-bold text-blue-400 game-mono">{fmt(totalInsight)}</div>
|
||||||
<div className="text-xs text-gray-400">Total Insight</div>
|
<div className="text-xs text-gray-400">Total Insight</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="p-3 bg-gray-800/50 rounded text-center">
|
<div className="p-3 bg-gray-800/50 rounded text-center">
|
||||||
<div className="text-2xl font-bold text-green-400 game-mono">{store.maxFloorReached}</div>
|
<div className="text-2xl font-bold text-green-400 game-mono">{maxFloorReached}</div>
|
||||||
<div className="text-xs text-gray-400">Max Floor</div>
|
<div className="text-xs text-gray-400">Max Floor</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Separator className="bg-gray-700 my-3" />
|
<Separator className="bg-gray-700 my-3" />
|
||||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
|
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
|
||||||
<div className="p-3 bg-gray-800/50 rounded text-center">
|
<div className="p-3 bg-gray-800/50 rounded text-center">
|
||||||
<div className="text-xl font-bold text-gray-300 game-mono">{Object.values(store.spells).filter(s => s.learned).length}</div>
|
<div className="text-xl font-bold text-gray-300 game-mono">{Object.values(spells).filter(s => s.learned).length}</div>
|
||||||
<div className="text-xs text-gray-400">Spells Learned</div>
|
<div className="text-xs text-gray-400">Spells Learned</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="p-3 bg-gray-800/50 rounded text-center">
|
<div className="p-3 bg-gray-800/50 rounded text-center">
|
||||||
<div className="text-xl font-bold text-gray-300 game-mono">{Object.values(store.skills).reduce((a, b) => a + b, 0)}</div>
|
<div className="text-xl font-bold text-gray-300 game-mono">{Object.values(skills).reduce((a, b) => a + b, 0)}</div>
|
||||||
<div className="text-xs text-gray-400">Total Skill Levels</div>
|
<div className="text-xs text-gray-400">Total Skill Levels</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="p-3 bg-gray-800/50 rounded text-center">
|
<div className="p-3 bg-gray-800/50 rounded text-center">
|
||||||
<div className="text-xl font-bold text-gray-300 game-mono">{fmt(store.totalManaGathered)}</div>
|
<div className="text-xl font-bold text-gray-300 game-mono">{fmt(totalManaGathered)}</div>
|
||||||
<div className="text-xs text-gray-400">Total Mana Gathered</div>
|
<div className="text-xs text-gray-400">Total Mana Gathered</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="p-3 bg-gray-800/50 rounded text-center">
|
<div className="p-3 bg-gray-800/50 rounded text-center">
|
||||||
<div className="text-xl font-bold text-gray-300 game-mono">{store.memorySlots}</div>
|
<div className="text-xl font-bold text-gray-300 game-mono">{memorySlots}</div>
|
||||||
<div className="text-xs text-gray-400">Memory Slots</div>
|
<div className="text-xs text-gray-400">Memory Slots</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -21,14 +21,13 @@ export { useGameStore } from './gameStore';
|
|||||||
export { useGameLoop } from './gameHooks';
|
export { useGameLoop } from './gameHooks';
|
||||||
export type { GameCoordinatorState, GameCoordinatorStore } from './gameStore';
|
export type { GameCoordinatorState, GameCoordinatorStore } from './gameStore';
|
||||||
|
|
||||||
// Re-export utilities from utils.ts
|
// Re-export utilities from utils.ts and computed-stats
|
||||||
export {
|
export {
|
||||||
fmt,
|
fmt,
|
||||||
fmtDec,
|
fmtDec,
|
||||||
getFloorMaxHP,
|
getFloorMaxHP,
|
||||||
getFloorElement,
|
getFloorElement,
|
||||||
computeMaxMana,
|
computeMaxMana,
|
||||||
computeElementMax,
|
|
||||||
computeRegen,
|
computeRegen,
|
||||||
computeEffectiveRegen,
|
computeEffectiveRegen,
|
||||||
computeClickMana,
|
computeClickMana,
|
||||||
@@ -41,3 +40,7 @@ export {
|
|||||||
canAffordSpellCost,
|
canAffordSpellCost,
|
||||||
deductSpellCost,
|
deductSpellCost,
|
||||||
} from '../utils';
|
} from '../utils';
|
||||||
|
|
||||||
|
export { computeElementMax } from '../store-modules/computed-stats';
|
||||||
|
|
||||||
|
export { getStudySpeedMultiplier, getStudyCostMultiplier } from '../constants';
|
||||||
|
|||||||
Reference in New Issue
Block a user