Initial commit
This commit is contained in:
206
src/components/game/shared/MemorySlotPicker.tsx
Executable file
206
src/components/game/shared/MemorySlotPicker.tsx
Executable file
@@ -0,0 +1,206 @@
|
||||
'use client';
|
||||
|
||||
import { useState, useMemo } from 'react';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { ScrollArea } from '@/components/ui/scroll-area';
|
||||
import { Save, Trash2, Star, ChevronUp } from 'lucide-react';
|
||||
import { useGameContext } from '../GameContext';
|
||||
import { SKILLS_DEF } from '@/lib/game/constants';
|
||||
import { getTierMultiplier, getBaseSkillId } from '@/lib/game/skill-evolution';
|
||||
import type { Memory } from '@/lib/game/types';
|
||||
|
||||
interface MemorySlotPickerProps {
|
||||
onConfirm?: () => void;
|
||||
}
|
||||
|
||||
export function MemorySlotPicker({ onConfirm }: MemorySlotPickerProps) {
|
||||
const { store } = useGameContext();
|
||||
const [selectedSkills, setSelectedSkills] = useState<Memory[]>(store.memories || []);
|
||||
|
||||
// Get all skills that have progress and can be saved
|
||||
const saveableSkills = useMemo(() => {
|
||||
const skills: { skillId: string; level: number; tier: number; upgrades: string[]; name: string }[] = [];
|
||||
|
||||
for (const [skillId, level] of Object.entries(store.skills)) {
|
||||
if (level && level > 0) {
|
||||
const baseSkillId = getBaseSkillId(skillId);
|
||||
const tier = store.skillTiers?.[baseSkillId] || 1;
|
||||
const tieredSkillId = tier > 1 ? `${baseSkillId}_t${tier}` : baseSkillId;
|
||||
const upgrades = store.skillUpgrades?.[tieredSkillId] || [];
|
||||
const skillDef = SKILLS_DEF[baseSkillId];
|
||||
|
||||
// Only include if it's a base skill (not a tiered variant in the skills object)
|
||||
if (skillId === baseSkillId || skillId.includes('_t')) {
|
||||
// Get the actual skill ID and level
|
||||
const actualLevel = store.skills[tieredSkillId] || store.skills[baseSkillId] || 0;
|
||||
if (actualLevel > 0) {
|
||||
skills.push({
|
||||
skillId: baseSkillId,
|
||||
level: actualLevel,
|
||||
tier,
|
||||
upgrades,
|
||||
name: skillDef?.name || baseSkillId,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove duplicates and keep highest tier/level
|
||||
const uniqueSkills = new Map<string, typeof skills[0]>();
|
||||
for (const skill of skills) {
|
||||
const existing = uniqueSkills.get(skill.skillId);
|
||||
if (!existing || skill.tier > existing.tier || (skill.tier === existing.tier && skill.level > existing.level)) {
|
||||
uniqueSkills.set(skill.skillId, skill);
|
||||
}
|
||||
}
|
||||
|
||||
return Array.from(uniqueSkills.values()).sort((a, b) => {
|
||||
// Sort by tier then level then name
|
||||
if (a.tier !== b.tier) return b.tier - a.tier;
|
||||
if (a.level !== b.level) return b.level - a.level;
|
||||
return a.name.localeCompare(b.name);
|
||||
});
|
||||
}, [store.skills, store.skillTiers, store.skillUpgrades]);
|
||||
|
||||
const isSkillSelected = (skillId: string) => selectedSkills.some(m => m.skillId === skillId);
|
||||
|
||||
const canAddMore = selectedSkills.length < store.memorySlots;
|
||||
|
||||
const toggleSkill = (skillId: string) => {
|
||||
const existingIndex = selectedSkills.findIndex(m => m.skillId === skillId);
|
||||
|
||||
if (existingIndex >= 0) {
|
||||
// Remove it
|
||||
setSelectedSkills(selectedSkills.filter((_, i) => i !== existingIndex));
|
||||
} else if (canAddMore) {
|
||||
// Add it
|
||||
const skill = saveableSkills.find(s => s.skillId === skillId);
|
||||
if (skill) {
|
||||
setSelectedSkills([...selectedSkills, {
|
||||
skillId: skill.skillId,
|
||||
level: skill.level,
|
||||
tier: skill.tier,
|
||||
upgrades: skill.upgrades,
|
||||
}]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleConfirm = () => {
|
||||
// Clear and re-add selected memories
|
||||
store.clearMemories();
|
||||
for (const memory of selectedSkills) {
|
||||
store.addMemory(memory);
|
||||
}
|
||||
onConfirm?.();
|
||||
};
|
||||
|
||||
return (
|
||||
<Card className="bg-gray-900/80 border-gray-700">
|
||||
<CardHeader className="pb-2">
|
||||
<CardTitle className="text-amber-400 game-panel-title text-sm flex items-center gap-2">
|
||||
<Save className="w-4 h-4" />
|
||||
Memory Slots ({selectedSkills.length}/{store.memorySlots})
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-3">
|
||||
<p className="text-xs text-gray-400">
|
||||
Select skills to preserve in your memory. Saved skills will retain their level, tier, and upgrades in the next loop.
|
||||
</p>
|
||||
|
||||
{/* Selected Skills */}
|
||||
{selectedSkills.length > 0 && (
|
||||
<div className="space-y-1">
|
||||
<div className="text-xs text-green-400 game-panel-title">Saved to Memory:</div>
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{selectedSkills.map((memory) => {
|
||||
const skillDef = SKILLS_DEF[memory.skillId];
|
||||
return (
|
||||
<Badge
|
||||
key={memory.skillId}
|
||||
className="bg-amber-900/50 text-amber-200 cursor-pointer hover:bg-red-900/50"
|
||||
onClick={() => toggleSkill(memory.skillId)}
|
||||
>
|
||||
{skillDef?.name || memory.skillId}
|
||||
{' '}Lv.{memory.level}
|
||||
{memory.tier > 1 && ` T${memory.tier}`}
|
||||
{memory.upgrades.length > 0 && ` (${memory.upgrades.length}⭐)`}
|
||||
<Trash2 className="w-3 h-3 ml-1" />
|
||||
</Badge>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Available Skills */}
|
||||
<div className="text-xs text-gray-400 game-panel-title">Skills to Save:</div>
|
||||
<ScrollArea className="h-48">
|
||||
<div className="space-y-1 pr-2">
|
||||
{saveableSkills.length === 0 ? (
|
||||
<div className="text-gray-500 text-xs text-center py-4">
|
||||
No skills with progress to save
|
||||
</div>
|
||||
) : (
|
||||
saveableSkills.map((skill) => {
|
||||
const isSelected = isSkillSelected(skill.skillId);
|
||||
const tierMult = getTierMultiplier(skill.tier > 1 ? `${skill.skillId}_t${skill.tier}` : skill.skillId);
|
||||
|
||||
return (
|
||||
<div
|
||||
key={skill.skillId}
|
||||
className={`p-2 rounded border cursor-pointer transition-all ${
|
||||
isSelected
|
||||
? 'border-amber-500 bg-amber-900/30'
|
||||
: canAddMore
|
||||
? 'border-gray-700 bg-gray-800/50 hover:border-gray-600'
|
||||
: 'border-gray-800 bg-gray-900/30 opacity-50'
|
||||
}`}
|
||||
onClick={() => toggleSkill(skill.skillId)}
|
||||
>
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="font-semibold text-sm">{skill.name}</span>
|
||||
{skill.tier > 1 && (
|
||||
<Badge className="bg-purple-600/50 text-purple-200 text-xs">
|
||||
Tier {skill.tier} ({tierMult}x)
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-purple-400 text-sm">Lv.{skill.level}</span>
|
||||
{skill.upgrades.length > 0 && (
|
||||
<Badge className="bg-amber-700/50 text-amber-200 text-xs flex items-center gap-1">
|
||||
<Star className="w-3 h-3" />
|
||||
{skill.upgrades.length}
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{skill.upgrades.length > 0 && (
|
||||
<div className="text-xs text-gray-500 mt-1">
|
||||
Upgrades: {skill.upgrades.length} selected
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})
|
||||
)}
|
||||
</div>
|
||||
</ScrollArea>
|
||||
|
||||
{/* Confirm Button */}
|
||||
<Button
|
||||
className="w-full bg-amber-600 hover:bg-amber-700"
|
||||
onClick={handleConfirm}
|
||||
>
|
||||
<Save className="w-4 h-4 mr-2" />
|
||||
Confirm Memories
|
||||
</Button>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user