fix: SkillsTab barrel export, equipment store reads, LabTab re-export, debug null guards, GrimoireTab loaded state, spire tab switching
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m48s
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m48s
This commit is contained in:
@@ -9,7 +9,7 @@ import { useAttunementStore } from '@/lib/game/stores';
|
|||||||
export function AttunementDebug() {
|
export function AttunementDebug() {
|
||||||
const attunements = useAttunementStore((s) => s.attunements);
|
const attunements = useAttunementStore((s) => s.attunements);
|
||||||
const debugUnlockAttunement = useAttunementStore((s) => s.debugUnlockAttunement);
|
const debugUnlockAttunement = useAttunementStore((s) => s.debugUnlockAttunement);
|
||||||
const debugAddAttunementXP = useAttunementStore((s) => s.debugAddAttunementXP);
|
const addAttunementXP = useAttunementStore((s) => s.addAttunementXP);
|
||||||
|
|
||||||
const handleUnlockAttunement = (id: string) => {
|
const handleUnlockAttunement = (id: string) => {
|
||||||
if (debugUnlockAttunement) {
|
if (debugUnlockAttunement) {
|
||||||
@@ -18,8 +18,8 @@ export function AttunementDebug() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleAddAttunementXP = (id: string, amount: number) => {
|
const handleAddAttunementXP = (id: string, amount: number) => {
|
||||||
if (debugAddAttunementXP) {
|
if (addAttunementXP) {
|
||||||
debugAddAttunementXP(id, amount);
|
addAttunementXP(id, amount);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -32,7 +32,7 @@ export function AttunementDebug() {
|
|||||||
</CardTitle>
|
</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="space-y-3">
|
<CardContent className="space-y-3">
|
||||||
{Object.entries(ATTUNEMENTS_DEF).map(([id, def]) => {
|
{Object.entries(ATTUNEMENTS_DEF || {}).map(([id, def]) => {
|
||||||
const isActive = attunements?.[id]?.active;
|
const isActive = attunements?.[id]?.active;
|
||||||
const level = attunements?.[id]?.level || 1;
|
const level = attunements?.[id]?.level || 1;
|
||||||
const xp = attunements?.[id]?.experience || 0;
|
const xp = attunements?.[id]?.experience || 0;
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ export function ElementDebug() {
|
|||||||
|
|
||||||
const handleAddElementalMana = (element: string, amount: number) => {
|
const handleAddElementalMana = (element: string, amount: number) => {
|
||||||
if (addElementMana) {
|
if (addElementMana) {
|
||||||
addElementMana(element, amount);
|
addElementMana(element, amount, 100);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -31,7 +31,7 @@ export function ElementDebug() {
|
|||||||
</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, elem]) => {
|
{Object.entries(elements || {}).map(([id, elem]) => {
|
||||||
const def = ELEMENTS[id];
|
const def = ELEMENTS[id];
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -51,8 +51,7 @@ export function GameStateDebug() {
|
|||||||
|
|
||||||
const getMaxMana = () => {
|
const getMaxMana = () => {
|
||||||
return computeMaxMana(
|
return computeMaxMana(
|
||||||
{ skills: {}, prestigeUpgrades: {}, skillUpgrades: {}, skillTiers: {} },
|
{ skills: {}, prestigeUpgrades: {}, skillUpgrades: {}, skillTiers: {} }
|
||||||
{ maxManaBonus: 0, maxManaMultiplier: 1 }
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ export function PactDebug() {
|
|||||||
const addLog = useUIStore((s) => s.addLog);
|
const addLog = useUIStore((s) => s.addLog);
|
||||||
|
|
||||||
// Get all guardian floors
|
// Get all guardian floors
|
||||||
const guardianFloors = Object.keys(GUARDIANS).map(Number).sort((a, b) => a - b);
|
const guardianFloors = Object.keys(GUARDIANS || {}).map(Number).sort((a, b) => a - b);
|
||||||
|
|
||||||
// Force sign a pact with a guardian (bypass costs and time)
|
// Force sign a pact with a guardian (bypass costs and time)
|
||||||
const forcePact = (floor: number) => {
|
const forcePact = (floor: number) => {
|
||||||
@@ -118,7 +118,7 @@ export function PactDebug() {
|
|||||||
Floor {floor} | {guardian.pact}x multiplier
|
Floor {floor} | {guardian.pact}x multiplier
|
||||||
</div>
|
</div>
|
||||||
<div className="text-xs text-gray-500">
|
<div className="text-xs text-gray-500">
|
||||||
Unlocks: {guardian.unlocksMana.map(e => ELEMENTS[e]?.name || e).join(', ')}
|
Element: {ELEMENTS[guardian.element]?.name || guardian.element}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex gap-1">
|
<div className="flex gap-1">
|
||||||
|
|||||||
@@ -4,12 +4,12 @@ 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 { useSkillStore, useGameStore } from '@/lib/game/stores';
|
import { useSkillStore } from '@/lib/game/stores';
|
||||||
|
|
||||||
export function SkillDebug() {
|
export function SkillDebug() {
|
||||||
const skills = useSkillStore((s) => s.skills);
|
const skills = useSkillStore((s) => s.skills);
|
||||||
const setSkills = useSkillStore.setState;
|
const setSkills = useSkillStore.setState;
|
||||||
const setGameState = useGameStore.setState;
|
const setGameState = useSkillStore.setState;
|
||||||
|
|
||||||
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">
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ import { EnchantmentsPanel } from './EnchantmentsPanel';
|
|||||||
import { useGameToast } from '@/components/game/GameToast';
|
import { useGameToast } from '@/components/game/GameToast';
|
||||||
import { ConfirmDialog } from '@/components/game/ConfirmDialog';
|
import { ConfirmDialog } from '@/components/game/ConfirmDialog';
|
||||||
import { equipItem, unequipItem, deleteEquipmentInstance } from '@/lib/game/crafting-actions';
|
import { equipItem, unequipItem, deleteEquipmentInstance } from '@/lib/game/crafting-actions';
|
||||||
import { useCombatStore } from '@/lib/game/stores';
|
import { useCombatStore, useCraftingStore } from '@/lib/game/stores';
|
||||||
|
|
||||||
// Rarity color mappings using design system tokens
|
// Rarity color mappings using design system tokens
|
||||||
export const RARITY_BORDER_COLORS: Record<string, string> = {
|
export const RARITY_BORDER_COLORS: Record<string, string> = {
|
||||||
@@ -120,8 +120,8 @@ export function EquipmentTab() {
|
|||||||
const [deleteConfirm, setDeleteConfirm] = useState<{ instanceId: string; name: string } | null>(null);
|
const [deleteConfirm, setDeleteConfirm] = useState<{ instanceId: string; name: string } | null>(null);
|
||||||
|
|
||||||
// Use modular store directly - MUST be called before any conditional returns
|
// Use modular store directly - MUST be called before any conditional returns
|
||||||
const equippedInstances = useCombatStore((s) => s.equippedInstances);
|
const equippedInstances = useCraftingStore((s) => s.equippedInstances);
|
||||||
const equipmentInstances = useCombatStore((s) => s.equipmentInstances);
|
const equipmentInstances = useCraftingStore((s) => s.equipmentInstances);
|
||||||
|
|
||||||
// Get unequipped items - hooks must be called before conditional returns
|
// Get unequipped items - hooks must be called before conditional returns
|
||||||
const equippedIds = useMemo(() =>
|
const equippedIds = useMemo(() =>
|
||||||
|
|||||||
@@ -1,122 +1 @@
|
|||||||
'use client';
|
export { LabTab } from '../LabTab';
|
||||||
|
|
||||||
import { GameCard, ElementBadge, ActionButton } from '@/components/ui';
|
|
||||||
import { Button } from '@/components/ui/button';
|
|
||||||
import { ELEMENTS } from '@/lib/game/constants';
|
|
||||||
|
|
||||||
interface LabTabProps {
|
|
||||||
store: {
|
|
||||||
rawMana: number;
|
|
||||||
elements: Record<string, { current: number; max: number; unlocked: boolean }>;
|
|
||||||
skills: Record<string, number>;
|
|
||||||
craftComposite: (target: string) => void;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function LabTab({ store }: LabTabProps) {
|
|
||||||
// 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 && state.current > 0)
|
|
||||||
.map(([id, state]) => {
|
|
||||||
const def = ELEMENTS[id];
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
key={id}
|
|
||||||
className="p-2 rounded border border-[var(--border-subtle)] bg-[var(--bg-sunken)]"
|
|
||||||
>
|
|
||||||
<div className="text-lg text-center">
|
|
||||||
<ElementBadge elementId={id} size="sm" />
|
|
||||||
</div>
|
|
||||||
<div className="text-xs font-semibold text-center" style={{ color: `var(--mana-${id})` }}>{def?.name}</div>
|
|
||||||
<div className="text-xs text-[var(--text-secondary)] font-[var(--font-mono)] text-center">{state.current}/{state.max}</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
// Render composite crafting
|
|
||||||
const renderCompositeCrafting = () => {
|
|
||||||
const compositeElements = Object.entries(ELEMENTS)
|
|
||||||
.filter(([, def]) => def.recipe)
|
|
||||||
.filter(([id]) => (store.elements || {})[id]?.unlocked);
|
|
||||||
|
|
||||||
if (compositeElements.length === 0) return null;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<GameCard>
|
|
||||||
<div className="pb-2">
|
|
||||||
<h3 className="text-sm font-semibold text-[var(--text-primary)]">Composite Crafting</h3>
|
|
||||||
</div>
|
|
||||||
<div className="space-y-2">
|
|
||||||
{compositeElements.map(([id, def]) => {
|
|
||||||
const recipe = def.recipe || [];
|
|
||||||
const canCraft = recipe.every(r => ((store.elements || {})[r]?.current || 0) >= 1);
|
|
||||||
const craftBonus = 1 + (store.skills.elemCrafting || 0) * 0.25;
|
|
||||||
const output = Math.floor(craftBonus);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div key={id} className="p-2 rounded border border-[var(--border-subtle)] bg-[var(--bg-sunken)] flex items-center justify-between">
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<ElementBadge elementId={id} size="md" />
|
|
||||||
<span className="text-sm" style={{ color: `var(--mana-${id})` }}>{def.name}</span>
|
|
||||||
<span className="text-xs text-[var(--text-muted)]">
|
|
||||||
({recipe.map(r => {
|
|
||||||
const rDef = ELEMENTS[r];
|
|
||||||
return rDef?.sym || r;
|
|
||||||
}).join(' + ')})
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<Button
|
|
||||||
size="sm"
|
|
||||||
variant="outline"
|
|
||||||
disabled={!canCraft}
|
|
||||||
onClick={() => store.craftComposite(id)}
|
|
||||||
className={!canCraft ? 'opacity-50 cursor-not-allowed' : ''}
|
|
||||||
>
|
|
||||||
Craft ({output})
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
</GameCard>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
// 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 (
|
|
||||||
<GameCard>
|
|
||||||
<div className="pt-6">
|
|
||||||
<div className="text-center text-[var(--text-muted)]">
|
|
||||||
No elemental mana available. Gather or convert mana to see elemental pools.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</GameCard>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
|
||||||
{/* Elemental Mana Display */}
|
|
||||||
<GameCard className="lg:col-span-2">
|
|
||||||
<div className="pb-2">
|
|
||||||
<h3 className="text-sm font-semibold text-[var(--text-primary)]">Elemental Mana</h3>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
{renderElementsGrid()}
|
|
||||||
</div>
|
|
||||||
</GameCard>
|
|
||||||
|
|
||||||
{/* Composite Crafting */}
|
|
||||||
{renderCompositeCrafting()}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
LabTab.displayName = "LabTab";
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useCombatStore, useManaStore, useSkillStore } from '@/lib/game/stores';
|
import { useCombatStore, useCraftingStore, useManaStore, useSkillStore } from '@/lib/game/stores';
|
||||||
import { GameCard, ElementBadge } from '@/components/ui';
|
import { GameCard, ElementBadge } from '@/components/ui';
|
||||||
import { Badge } from '@/components/ui/badge';
|
import { Badge } from '@/components/ui/badge';
|
||||||
import { ELEMENTS, SPELLS_DEF } from '@/lib/game/constants';
|
import { ELEMENTS, SPELLS_DEF } from '@/lib/game/constants';
|
||||||
@@ -14,8 +14,8 @@ export function SpellsTab() {
|
|||||||
const activeSpell = useCombatStore((s) => s.activeSpell);
|
const activeSpell = useCombatStore((s) => s.activeSpell);
|
||||||
const setSpell = useCombatStore((s) => s.setSpell);
|
const setSpell = useCombatStore((s) => s.setSpell);
|
||||||
|
|
||||||
const equippedInstances = useCombatStore((s) => s.equippedInstances);
|
const equippedInstances = useCraftingStore((s) => s.equippedInstances);
|
||||||
const equipmentInstances = useCombatStore((s) => s.equipmentInstances);
|
const equipmentInstances = useCraftingStore((s) => s.equipmentInstances);
|
||||||
|
|
||||||
const rawMana = useManaStore((s) => s.rawMana);
|
const rawMana = useManaStore((s) => s.rawMana);
|
||||||
const elements = useManaStore((s) => s.elements);
|
const elements = useManaStore((s) => s.elements);
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ export { SpireTab } from './SpireTab';
|
|||||||
export { SpellsTab } from './SpellsTab';
|
export { SpellsTab } from './SpellsTab';
|
||||||
export { LabTab } from './LabTab';
|
export { LabTab } from './LabTab';
|
||||||
// SkillsTab is now exported from src/components/game/index.ts
|
// SkillsTab is now exported from src/components/game/index.ts
|
||||||
// export { SkillsTab } from './SkillsTab';
|
export { SkillsTab } from '../SkillsTab';
|
||||||
export { StatsTab } from './StatsTab';
|
export { StatsTab } from './StatsTab';
|
||||||
export { EquipmentTab } from './EquipmentTab';
|
export { EquipmentTab } from './EquipmentTab';
|
||||||
export { AttunementsTab } from './AttunementsTab';
|
export { AttunementsTab } from './AttunementsTab';
|
||||||
|
|||||||
@@ -66,6 +66,15 @@ export interface PrestigeState {
|
|||||||
|
|
||||||
// Reset
|
// Reset
|
||||||
resetPrestige: () => void;
|
resetPrestige: () => void;
|
||||||
|
|
||||||
|
// Debug helpers
|
||||||
|
debugSetSignedPacts: (pacts: number[]) => void;
|
||||||
|
debugSetPactDetails: (details: Record<number, {
|
||||||
|
floor: number;
|
||||||
|
guardianId: string;
|
||||||
|
signedAt: { day: number; hour: number };
|
||||||
|
skillLevels: Record<string, number>;
|
||||||
|
}>) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const initialState = {
|
const initialState = {
|
||||||
|
|||||||
Reference in New Issue
Block a user