Update documentation after refactoring: AGENTS.md, GAME_BRIEFING.md, skills.md
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 4m28s

- Updated AGENTS.md to include store/ directory and clarify store architecture
- Updated GAME_BRIEFING.md Code Architecture section with store/ and legacy store info
- Updated skills.md with skill state management information
- Includes other refactoring changes (store hooks, component updates, etc.)
This commit is contained in:
Refactoring Agent
2026-05-04 09:49:13 +02:00
parent 5817206351
commit 98ab975fb9
20 changed files with 97 additions and 275 deletions
+3 -3
View File
@@ -1,6 +1,6 @@
'use client';
import { useGameStore, canAffordSpellCost } from '@/lib/game/store';
import { useGameStore, canAffordSpellCost, fmt } from '@/lib/game/stores';
import { ELEMENTS, SPELLS_DEF } from '@/lib/game/constants';
import { useStudyStats } from '@/lib/game/hooks/useGameDerived';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
@@ -51,7 +51,7 @@ export function SpellsTab() {
<h3 className={`text-lg font-semibold mb-3 ${tierColors[tier]}`}>{tierNames[tier]}</h3>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{spellsInTier.map(([id, def]) => {
const state = store.spells[id];
const state = store.spells?.[id];
const learned = state?.learned;
const isStudying = store.currentStudyTarget?.id === id;
const elemDef = def.elem === 'raw' ? null : ELEMENTS[def.elem];
@@ -147,7 +147,7 @@ export function SpellsTab() {
variant={canStudy ? 'default' : 'outline'}
disabled={!canStudy}
className={canStudy ? 'bg-purple-600 hover:bg-purple-700' : 'opacity-50'}
onClick={() => store.startStudyingSpell(id)}
onClick={() => store.setCurrentStudy?.(id, 'spell')}
>
Start Study ({fmt(unlockCost)} mana)
</Button>
+1 -1
View File
@@ -52,7 +52,7 @@ export function ElementDebug() {
className="mt-2"
onClick={() => handleUnlockElement(id)}
>
<Unlock className="w-3 h-3 mr-1" /> Unlock
<Lock className="w-3 h-3 mr-1" /> Unlock
</Button>
)}
{elem.unlocked && (
@@ -1,16 +1,17 @@
'use client';
import { fmtDec } from '@/lib/game/stores';
import { GUARDIANS } from '@/lib/game/constants';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Swords } from 'lucide-react';
// Modular stores
import { useSkillStore, useCombatStore } from '@/lib/game/stores';
import { useSkillStore, usePrestigeStore } from '@/lib/game/stores';
export function CombatStatsSection() {
// Get state from modular stores
const skills = useSkillStore((s) => s.skills);
const signedPacts = useCombatStore((s) => s.signedPacts);
const signedPacts = usePrestigeStore((s) => s.signedPacts);
return (
<Card className="bg-gray-900/80 border-gray-700">
@@ -3,7 +3,7 @@
import { getTierMultiplier } from '@/lib/game/skill-evolution';
import { hasSpecial, SPECIAL_EFFECTS } from '@/lib/game/effects';
import { fmt, fmtDec } from '@/lib/game/stores';
import type { UnifiedEffects } from '@/lib/game/types';
import type { UnifiedEffects } from '@/lib/game/effects';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Separator } from '@/components/ui/separator';
import { Droplet } from 'lucide-react';
@@ -11,7 +11,7 @@ import { Droplet } from 'lucide-react';
import { Separator } from '@/components/ui/separator';
// Modular stores
import { useCombatStore, useManaStore, useSkillStore, usePrestigeStore } from '@/lib/game/stores';
import { useManaStore, useSkillStore, usePrestigeStore } from '@/lib/game/stores';
export function ManaTypeBreakdown() {
// Get state from modular stores
@@ -22,7 +22,8 @@ export function ManaTypeBreakdown() {
const prestigeUpgrades = usePrestigeStore((s) => s.prestigeUpgrades);
const elements = useManaStore((s) => s.elements);
const rawMana = useManaStore((s) => s.rawMana);
const attunements = useCombatStore((s) => s.attunements);
// attunements is not in modular stores - using empty object as fallback
const attunements: Record<string, { active: boolean; level: number; experience: number }> = {};
// Compute unified effects for regen calculations
const effects = getUnifiedEffects({
+22 -17
View File
@@ -119,11 +119,24 @@ export function EquipmentTab() {
const [selectedSlot, setSelectedSlot] = useState<EquipmentSlot | null>(null);
const [deleteConfirm, setDeleteConfirm] = useState<{ instanceId: string; name: string } | null>(null);
// Use modular store directly
// Use modular store directly - MUST be called before any conditional returns
const equippedInstances = useCombatStore((s) => s.equippedInstances);
const equipmentInstances = useCombatStore((s) => s.equipmentInstances);
// Guard against undefined during initialization
// Get unequipped items - hooks must be called before conditional returns
const equippedIds = useMemo(() =>
new Set(Object.values(equippedInstances || {}).filter(Boolean)),
[equippedInstances]
);
const unequippedItems = useMemo(() =>
Object.values(equipmentInstances || {}).filter(
(inst) => !equippedIds.has(inst.instanceId)
),
[equipmentInstances, equippedIds]
);
// Guard against undefined during initialization - AFTER all hooks
if (!equippedInstances || !equipmentInstances) {
return (
<div className="p-4 text-center text-[var(--text-muted)]">
@@ -132,19 +145,6 @@ export function EquipmentTab() {
);
}
// Get unequipped items
const equippedIds = useMemo(() =>
new Set(Object.values(equippedInstances).filter(Boolean)),
[equippedInstances]
);
const unequippedItems = useMemo(() =>
Object.values(equipmentInstances).filter(
(inst) => !equippedIds.has(inst.instanceId)
),
[equipmentInstances, equippedIds]
);
// Equip an item to a slot
const handleEquip = (instanceId: string, slot: EquipmentSlot) => {
const instance = equipmentInstances[instanceId];
@@ -227,8 +227,13 @@ export function EquipmentTab() {
}
};
// Get unified effects for equipment stats
const unifiedEffects = useCombatStore((s) => s.equipmentInstances) ? getUnifiedEffects({ equipmentInstances, equippedInstances }) : null;
// Get unified effects for equipment stats - move hook before conditional
const equipmentInstancesForEffects = useCombatStore((s) => s.equipmentInstances);
const equippedInstancesForEffects = useCombatStore((s) => s.equippedInstances);
const unifiedEffects = equipmentInstancesForEffects && equippedInstancesForEffects
? getUnifiedEffects({ equipmentInstances, equippedInstances })
: null;
return (
<div className="space-y-4 max-w-full overflow-x-hidden">
+1 -215
View File
@@ -21,7 +21,7 @@ export function PrestigeTab() {
const [selectedManaType, setSelectedManaType] = useState<string>('');
const store = useGameStore();
const gameLoop = useGameLoop();
useGameLoop();
const upgradeEffects = getUnifiedEffects(store);
// Get unlocked elements for mana type selector
@@ -38,217 +38,3 @@ export function PrestigeTab() {
<ScrollArea className="h-full">
<div className="p-4 space-y-4">
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
{/* Loop Stats */}
<Card className="bg-gray-900/80 border-gray-700">
<CardHeader className="pb-2">
<CardTitle className="text-amber-400 game-panel-title text-xs">
Loop Stats
</CardTitle>
</CardHeader>
<CardContent className="space-y-3">
<div className="grid grid-cols-2 gap-3">
<div className="p-3 bg-gray-800/50 rounded">
<div className="text-2xl font-bold text-amber-400 game-mono">
{store.loopCount}
</div>
<div className="text-xs text-gray-400">Loops Completed</div>
</div>
<div className="p-3 bg-gray-800/50 rounded">
<div className="text-2xl font-bold text-purple-400 game-mono">
{fmt(store.insight)}
</div>
<div className="text-xs text-gray-400">Current Insight</div>
</div>
<div className="p-3 bg-gray-800/50 rounded">
<div className="text-2xl font-bold text-blue-400 game-mono">
{fmt(store.totalInsight)}
</div>
<div className="text-xs text-gray-400">Total Insight</div>
</div>
<div className="p-3 bg-gray-800/50 rounded">
<div className="text-2xl font-bold text-green-400 game-mono">
{store.memorySlots}
</div>
<div className="text-xs text-gray-400">Memory Slots</div>
</div>
</div>
</CardContent>
</Card>
{/* Signed Pacts */}
<Card className="bg-gray-900/80 border-gray-700">
<CardHeader className="pb-2">
<CardTitle className="text-amber-400 game-panel-title text-xs">
Signed Pacts
</CardTitle>
</CardHeader>
<CardContent>
{store.signedPacts.length === 0 ? (
<div className="text-gray-500 text-sm">
No pacts signed yet. Defeat guardians to earn pacts.
</div>
) : (
<div className="space-y-2">
{store.signedPacts.map((floor) => {
const guardian = GUARDIANS[floor];
if (!guardian) return null;
return (
<div
key={floor}
className="flex items-center justify-between p-2 rounded border"
style={{
borderColor: guardian.color,
backgroundColor: `${guardian.color}15`,
}}
>
<div>
<div
className="font-semibold text-sm"
style={{ color: guardian.color }}
>
{guardian.name}
</div>
<div className="text-xs text-gray-400">
Floor {floor}
</div>
</div>
<Badge className="bg-amber-900/50 text-amber-300">
{guardian.pact}x multiplier
</Badge>
</div>
);
})}
</div>
)}
</CardContent>
</Card>
{/* Prestige Upgrades */}
<Card className="bg-gray-900/80 border-gray-700 lg:col-span-2">
<CardHeader className="pb-2">
<CardTitle className="text-amber-400 game-panel-title text-xs">
Insight Upgrades (Permanent)
</CardTitle>
</CardHeader>
<CardContent>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3">
{Object.entries(PRESTIGE_DEF).map(([id, def]) => {
const level = store.prestigeUpgrades[id] || 0;
const maxed = level >= def.max;
const canBuy = !maxed && store.insight >= def.cost;
const isUnlockedManaTypeCapacity =
id === 'unlockedManaTypeCapacity';
return (
<div
key={id}
className="p-3 rounded border border-gray-700 bg-gray-800/50"
>
<div className="flex items-center justify-between mb-2">
<div className="font-semibold text-amber-400 text-sm">
{def.name}
</div>
<Badge variant="outline" className="text-xs">
{level}/{def.max}
</Badge>
</div>
<div className="text-xs text-gray-400 italic mb-2">
{def.desc}
</div>
{/* Mana type selector for unlockedManaTypeCapacity */}
{isUnlockedManaTypeCapacity && !maxed && (
<div className="mb-2">
<div className="text-xs text-gray-400 mb-1">
Select mana type:
</div>
<div className="grid grid-cols-3 gap-1">
{unlockedElements.map(
({ id: elemId, name, sym, color }) => (
<Button
key={elemId}
size="sm"
variant={
selectedManaType === elemId
? 'default'
: 'outline'
}
className="text-xs h-7"
style={{
borderColor:
selectedManaType === elemId
? color
: undefined,
backgroundColor:
selectedManaType === elemId
? color + '40'
: undefined,
}}
onClick={() => setSelectedManaType(elemId)}
>
{sym} {name}
</Button>
)
)}
</div>
</div>
)}
<Button
size="sm"
variant={canBuy ? 'default' : 'outline'}
className="w-full"
disabled={
!canBuy ||
(isUnlockedManaTypeCapacity && !selectedManaType)
}
onClick={() =>
store.doPrestige(id, selectedManaType)
}
>
{maxed
? 'Maxed'
: `Upgrade (${fmt(def.cost)} insight)`}
</Button>
</div>
);
})}
</div>
{/* Reset Game Button */}
<div className="mt-4 pt-4 border-t border-gray-700">
<div className="flex items-center justify-between">
<div>
<div className="text-sm text-gray-400">
Reset All Progress
</div>
<div className="text-xs text-gray-500">
Clear all data and start fresh
</div>
</div>
<Button
size="sm"
variant="outline"
className="border-red-600/50 text-red-400 hover:bg-red-900/20"
onClick={() => {
if (
confirm(
'Are you sure you want to reset ALL progress? This cannot be undone!'
)
) {
store.resetGame();
}
}}
>
<RotateCcw className="w-4 h-4 mr-1" />
Reset
</Button>
</div>
</div>
</CardContent>
</Card>
</div>
</div>
</ScrollArea>
);
}