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
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:
@@ -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">
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user