Move Loot/Achievements to separate tabs, add skill debug options, fix enchanting system
All checks were successful
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 2m15s

- Move LootInventoryDisplay to new LootTab (better mobile UX)
- Move AchievementsDisplay to new AchievementsTab (better mobile UX)
- Add skill research debug options to DebugTab (level up skills, enchanting skills)
- Fix enchanting: disenchant only possible in Prepare stage
- Fix enchanting: cannot apply enchantments to already enchanted gear
- Remove recover buttons from Apply stage (disenchant is only in Prepare stage now)
This commit is contained in:
Z User
2026-04-01 11:28:01 +00:00
parent 9220805ba0
commit 94cc9a179a
6 changed files with 485 additions and 120 deletions

View File

@@ -13,10 +13,9 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
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 { RotateCcw } from 'lucide-react'; import { RotateCcw } from 'lucide-react';
import { CraftingTab, SpireTab, SpellsTab, LabTab, SkillsTab, StatsTab, EquipmentTab, AttunementsTab, DebugTab } from '@/components/game/tabs'; import { CraftingTab, SpireTab, SpellsTab, LabTab, SkillsTab, StatsTab, EquipmentTab, AttunementsTab, DebugTab, LootTab, AchievementsTab } from '@/components/game/tabs';
import { ActionButtons, CalendarDisplay, ManaDisplay, TimeDisplay } from '@/components/game'; import { ActionButtons, CalendarDisplay, ManaDisplay, TimeDisplay } from '@/components/game';
import { LootInventoryDisplay } from '@/components/game/LootInventory'; // Loot and Achievements moved to separate tabs
import { AchievementsDisplay } from '@/components/game/AchievementsDisplay';
import { DebugName } from '@/lib/game/debug-context'; import { DebugName } from '@/lib/game/debug-context';
export default function ManaLoopGame() { export default function ManaLoopGame() {
@@ -210,31 +209,7 @@ export default function ManaLoopGame() {
/> />
</DebugName> </DebugName>
{/* Loot Inventory */} {/* Loot and Achievements moved to tabs */}
<DebugName name="LootInventoryDisplay">
<LootInventoryDisplay
inventory={store.lootInventory}
elements={store.elements}
equipmentInstances={store.equipmentInstances}
onDeleteMaterial={store.deleteMaterial}
onDeleteEquipment={store.deleteEquipmentInstance}
/>
</DebugName>
{/* Achievements */}
<DebugName name="AchievementsDisplay">
<AchievementsDisplay
achievements={store.achievements}
gameState={{
maxFloorReached: store.maxFloorReached,
totalManaGathered: store.totalManaGathered,
signedPacts: store.signedPacts,
totalSpellsCast: store.totalSpellsCast,
totalDamageDealt: store.totalDamageDealt,
totalCraftsCompleted: store.totalCraftsCompleted,
}}
/>
</DebugName>
</div> </div>
{/* Right Panel - Tabs */} {/* Right Panel - Tabs */}
@@ -247,6 +222,8 @@ export default function ManaLoopGame() {
<TabsTrigger value="spells" className="text-xs px-2 py-1">🔮 Spells</TabsTrigger> <TabsTrigger value="spells" className="text-xs px-2 py-1">🔮 Spells</TabsTrigger>
<TabsTrigger value="equipment" className="text-xs px-2 py-1">🛡 Gear</TabsTrigger> <TabsTrigger value="equipment" className="text-xs px-2 py-1">🛡 Gear</TabsTrigger>
<TabsTrigger value="crafting" className="text-xs px-2 py-1">🔧 Craft</TabsTrigger> <TabsTrigger value="crafting" className="text-xs px-2 py-1">🔧 Craft</TabsTrigger>
<TabsTrigger value="loot" className="text-xs px-2 py-1">💎 Loot</TabsTrigger>
<TabsTrigger value="achievements" className="text-xs px-2 py-1">🏆 Achieve</TabsTrigger>
<TabsTrigger value="lab" className="text-xs px-2 py-1">🔬 Lab</TabsTrigger> <TabsTrigger value="lab" className="text-xs px-2 py-1">🔬 Lab</TabsTrigger>
<TabsTrigger value="stats" className="text-xs px-2 py-1">📊 Stats</TabsTrigger> <TabsTrigger value="stats" className="text-xs px-2 py-1">📊 Stats</TabsTrigger>
<TabsTrigger value="debug" className="text-xs px-2 py-1">🔧 Debug</TabsTrigger> <TabsTrigger value="debug" className="text-xs px-2 py-1">🔧 Debug</TabsTrigger>
@@ -289,6 +266,18 @@ export default function ManaLoopGame() {
</DebugName> </DebugName>
</TabsContent> </TabsContent>
<TabsContent value="loot">
<DebugName name="LootTab">
<LootTab store={store} />
</DebugName>
</TabsContent>
<TabsContent value="achievements">
<DebugName name="AchievementsTab">
<AchievementsTab store={store} />
</DebugName>
</TabsContent>
<TabsContent value="lab"> <TabsContent value="lab">
<DebugName name="LabTab"> <DebugName name="LabTab">
<LabTab store={store} /> <LabTab store={store} />

View File

@@ -0,0 +1,43 @@
'use client';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Badge } from '@/components/ui/badge';
import type { GameStore } from '@/lib/game/store';
import { AchievementsDisplay } from '@/components/game/AchievementsDisplay';
export interface AchievementsTabProps {
store: GameStore;
}
export function AchievementsTab({ store }: AchievementsTabProps) {
const achievements = store.achievements;
const unlockedCount = achievements.unlocked.length;
return (
<div className="space-y-4">
<Card className="bg-gray-900/80 border-gray-700">
<CardHeader className="pb-2">
<CardTitle className="text-amber-400 text-sm flex items-center gap-2">
🏆 Achievements
<Badge className="ml-auto bg-amber-900/50 text-amber-300">
{unlockedCount} unlocked
</Badge>
</CardTitle>
</CardHeader>
<CardContent>
<AchievementsDisplay
achievements={achievements}
gameState={{
maxFloorReached: store.maxFloorReached,
totalManaGathered: store.totalManaGathered,
signedPacts: store.signedPacts,
totalSpellsCast: store.totalSpellsCast,
totalDamageDealt: store.totalDamageDealt,
totalCraftsCompleted: store.totalCraftsCompleted,
}}
/>
</CardContent>
</Card>
</div>
);
}

View File

@@ -383,7 +383,7 @@ export function CraftingTab({ store }: CraftingTabProps) {
{/* Equipment Selection */} {/* Equipment Selection */}
<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 text-sm">Select Equipment to Prepare</CardTitle> <CardTitle className="text-amber-400 text-sm">Select Equipment to Prepare or Disenchant</CardTitle>
</CardHeader> </CardHeader>
<CardContent> <CardContent>
{preparationProgress ? ( {preparationProgress ? (
@@ -401,28 +401,36 @@ export function CraftingTab({ store }: CraftingTabProps) {
) : ( ) : (
<ScrollArea className="h-64"> <ScrollArea className="h-64">
<div className="space-y-2"> <div className="space-y-2">
{equippedItems.map(({ slot, instance }) => ( {equippedItems.map(({ slot, instance }) => {
<div const hasEnchantments = instance.enchantments.length > 0;
key={instance.instanceId} return (
className={`p-3 rounded border cursor-pointer transition-all ${ <div
selectedEquipmentInstance === instance.instanceId key={instance.instanceId}
? 'border-amber-500 bg-amber-900/20' className={`p-3 rounded border cursor-pointer transition-all ${
: 'border-gray-700 bg-gray-800/50 hover:border-gray-600' selectedEquipmentInstance === instance.instanceId
}`} ? 'border-amber-500 bg-amber-900/20'
onClick={() => setSelectedEquipmentInstance(instance.instanceId)} : 'border-gray-700 bg-gray-800/50 hover:border-gray-600'
> } ${hasEnchantments ? 'border-l-4 border-l-red-600' : ''}`}
<div className="flex justify-between"> onClick={() => setSelectedEquipmentInstance(instance.instanceId)}
<div> >
<div className="font-semibold">{instance.name}</div> <div className="flex justify-between">
<div className="text-xs text-gray-400">{SLOT_NAMES[slot]}</div> <div>
</div> <div className="font-semibold">{instance.name}</div>
<div className="text-right text-sm"> <div className="text-xs text-gray-400">{SLOT_NAMES[slot]}</div>
<div className="text-green-400">{instance.usedCapacity}/{instance.totalCapacity} cap</div> {hasEnchantments && (
<div className="text-xs text-gray-400">{instance.enchantments.length} enchants</div> <div className="text-xs text-red-400 mt-1">
{instance.enchantments.length} enchantments - Disenchant to apply new
</div>
)}
</div>
<div className="text-right text-sm">
<div className="text-green-400">{instance.usedCapacity}/{instance.totalCapacity} cap</div>
<div className="text-xs text-gray-400">{instance.enchantments.length} enchants</div>
</div>
</div> </div>
</div> </div>
</div> );
))} })}
{equippedItems.length === 0 && ( {equippedItems.length === 0 && (
<div className="text-center text-gray-400 py-4">No equipped items</div> <div className="text-center text-gray-400 py-4">No equipped items</div>
)} )}
@@ -440,43 +448,79 @@ export function CraftingTab({ store }: CraftingTabProps) {
<CardContent> <CardContent>
{!selectedEquipmentInstance ? ( {!selectedEquipmentInstance ? (
<div className="text-center text-gray-400 py-8"> <div className="text-center text-gray-400 py-8">
Select equipment to prepare Select equipment to prepare or disenchant
</div> </div>
) : preparationProgress ? ( ) : preparationProgress ? (
<div className="text-gray-400">Preparation in progress...</div> <div className="text-gray-400">Preparation in progress...</div>
) : ( ) : (
(() => { (() => {
const instance = equipmentInstances[selectedEquipmentInstance]; const instance = equipmentInstances[selectedEquipmentInstance];
const hasEnchantments = instance.enchantments.length > 0;
const prepTime = 2 + Math.floor(instance.totalCapacity / 50); const prepTime = 2 + Math.floor(instance.totalCapacity / 50);
const manaCost = instance.totalCapacity * 10; const manaCost = instance.totalCapacity * 10;
// Calculate disenchant recovery
const disenchantLevel = skills.disenchanting || 0;
const recoveryRate = 0.1 + disenchantLevel * 0.2;
const totalRecoverable = instance.enchantments.reduce(
(sum, e) => sum + Math.floor(e.actualCost * recoveryRate),
0
);
return ( return (
<div className="space-y-4"> <div className="space-y-4">
<div className="text-lg font-semibold">{instance.name}</div> <div className="text-lg font-semibold">{instance.name}</div>
<Separator className="bg-gray-700" /> <Separator className="bg-gray-700" />
<div className="space-y-2 text-sm">
<div className="flex justify-between"> {/* Disenchant option for enchanted gear */}
<span className="text-gray-400">Capacity:</span> {hasEnchantments && (
<span>{instance.usedCapacity}/{instance.totalCapacity}</span> <div className="p-3 rounded border border-red-600/50 bg-red-900/20 space-y-3">
<div className="text-sm font-semibold text-red-400"> Equipment has enchantments</div>
<div className="text-xs text-gray-400">
You must disenchant before applying new enchantments.
</div>
<div className="flex justify-between text-sm">
<span className="text-gray-400">Recoverable Mana:</span>
<span className="text-green-400">{fmt(totalRecoverable)}</span>
</div>
<Button
className="w-full bg-red-600 hover:bg-red-700"
onClick={() => disenchantEquipment(instance.instanceId)}
>
<Trash2 className="w-4 h-4 mr-2" />
Disenchant & Recover {fmt(totalRecoverable)} Mana
</Button>
</div> </div>
<div className="flex justify-between"> )}
<span className="text-gray-400">Prep Time:</span>
<span>{prepTime}h</span> {/* Prepare option for non-enchanted gear */}
</div> {!hasEnchantments && (
<div className="flex justify-between"> <>
<span className="text-gray-400">Mana Cost:</span> <div className="space-y-2 text-sm">
<span className={rawMana < manaCost ? 'text-red-400' : 'text-green-400'}> <div className="flex justify-between">
{fmt(manaCost)} <span className="text-gray-400">Capacity:</span>
</span> <span>{instance.usedCapacity}/{instance.totalCapacity}</span>
</div> </div>
</div> <div className="flex justify-between">
<Button <span className="text-gray-400">Prep Time:</span>
className="w-full" <span>{prepTime}h</span>
disabled={rawMana < manaCost} </div>
onClick={() => startPreparing(selectedEquipmentInstance)} <div className="flex justify-between">
> <span className="text-gray-400">Mana Cost:</span>
Start Preparation ({prepTime}h, {fmt(manaCost)} mana) <span className={rawMana < manaCost ? 'text-red-400' : 'text-green-400'}>
</Button> {fmt(manaCost)}
</span>
</div>
</div>
<Button
className="w-full"
disabled={rawMana < manaCost}
onClick={() => startPreparing(selectedEquipmentInstance)}
>
Start Preparation ({prepTime}h, {fmt(manaCost)} mana)
</Button>
</>
)}
</div> </div>
); );
})() })()
@@ -517,10 +561,12 @@ export function CraftingTab({ store }: CraftingTabProps) {
) : ( ) : (
<div className="space-y-4"> <div className="space-y-4">
<div> <div>
<div className="text-sm text-gray-400 mb-2">Equipment:</div> <div className="text-sm text-gray-400 mb-2">Equipment (without enchantments):</div>
<ScrollArea className="h-32"> <ScrollArea className="h-32">
<div className="space-y-1"> <div className="space-y-1">
{equippedItems.map(({ slot, instance }) => ( {equippedItems
.filter(({ instance }) => instance.enchantments.length === 0)
.map(({ slot, instance }) => (
<div <div
key={instance.instanceId} key={instance.instanceId}
className={`p-2 rounded border cursor-pointer text-sm ${ className={`p-2 rounded border cursor-pointer text-sm ${
@@ -533,6 +579,11 @@ export function CraftingTab({ store }: CraftingTabProps) {
{instance.name} ({instance.usedCapacity}/{instance.totalCapacity} cap) {instance.name} ({instance.usedCapacity}/{instance.totalCapacity} cap)
</div> </div>
))} ))}
{equippedItems.filter(({ instance }) => instance.enchantments.length === 0).length === 0 && (
<div className="text-center text-gray-500 text-xs py-2">
No unenchanted equipment available. Disenchant in Prepare stage first.
</div>
)}
</div> </div>
</ScrollArea> </ScrollArea>
</div> </div>
@@ -632,51 +683,6 @@ export function CraftingTab({ store }: CraftingTabProps) {
)} )}
</CardContent> </CardContent>
</Card> </Card>
{/* Disenchant Section */}
<Card className="bg-gray-900/80 border-gray-700 lg:col-span-2">
<CardHeader className="pb-2">
<CardTitle className="text-amber-400 text-sm">Disenchant Equipment</CardTitle>
</CardHeader>
<CardContent>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-2">
{equippedItems
.filter(({ instance }) => instance.enchantments.length > 0)
.map(({ slot, instance }) => {
const disenchantLevel = skills.disenchanting || 0;
const recoveryRate = 0.1 + disenchantLevel * 0.2;
const totalRecoverable = instance.enchantments.reduce(
(sum, e) => sum + Math.floor(e.actualCost * recoveryRate),
0
);
return (
<div key={instance.instanceId} className="p-3 rounded border border-gray-700 bg-gray-800/50">
<div className="flex justify-between items-start">
<div>
<div className="font-semibold">{instance.name}</div>
<div className="text-xs text-gray-400">{instance.enchantments.length} enchantments</div>
</div>
<Button
size="sm"
variant="outline"
onClick={() => disenchantEquipment(instance.instanceId)}
>
<Trash2 className="w-4 h-4 mr-1" />
Recover {fmt(totalRecoverable)}
</Button>
</div>
</div>
);
})}
{equippedItems.filter(({ instance }) => instance.enchantments.length > 0).length === 0 && (
<div className="col-span-full text-center text-gray-400 py-4">
No enchanted equipment to disenchant
</div>
)}
</div>
</CardContent>
</Card>
</div> </div>
); );

View File

@@ -9,7 +9,7 @@ import { Switch } from '@/components/ui/switch';
import { Label } from '@/components/ui/label'; import { Label } from '@/components/ui/label';
import { import {
RotateCcw, Bug, Plus, Minus, Lock, Unlock, Zap, RotateCcw, Bug, Plus, Minus, Lock, Unlock, Zap,
Clock, Star, AlertTriangle, Sparkles, Settings, Eye Clock, Star, AlertTriangle, Sparkles, Settings, Eye, BookOpen
} from 'lucide-react'; } from 'lucide-react';
import type { GameStore } from '@/lib/game/types'; import type { GameStore } from '@/lib/game/types';
import { ATTUNEMENTS_DEF } from '@/lib/game/data/attunements'; import { ATTUNEMENTS_DEF } from '@/lib/game/data/attunements';
@@ -415,6 +415,285 @@ export function DebugTab({ store }: DebugTabProps) {
</div> </div>
</CardContent> </CardContent>
</Card> </Card>
{/* Skill Research Debug */}
<Card className="bg-gray-900/80 border-gray-700 md:col-span-2">
<CardHeader className="pb-2">
<CardTitle className="text-purple-400 text-sm flex items-center gap-2">
<BookOpen className="w-4 h-4" />
Skill Research Debug
</CardTitle>
</CardHeader>
<CardContent>
<div className="space-y-3">
{/* Enchanting Skills */}
<div>
<div className="text-xs text-gray-400 mb-2">Enchanting Skills:</div>
<div className="flex gap-2 flex-wrap">
<Button
size="sm"
variant="outline"
onClick={() => {
// Level up all enchanting skills by 1
const enchantSkills = ['enchanting', 'efficientEnchant', 'disenchanting', 'enchantSpeed', 'scrollCrafting', 'essenceRefining'];
enchantSkills.forEach(skillId => {
if (store.skills[skillId] !== undefined) {
store.skills[skillId] = Math.min((store.skills[skillId] || 0) + 1, 10);
} else {
store.skills[skillId] = 1;
}
});
}}
>
+1 All Enchanting
</Button>
<Button
size="sm"
variant="outline"
onClick={() => {
// Max all enchanting skills
const enchantSkills = ['enchanting', 'efficientEnchant', 'disenchanting', 'enchantSpeed', 'scrollCrafting', 'essenceRefining'];
enchantSkills.forEach(skillId => {
store.skills[skillId] = 10;
});
}}
>
Max All Enchanting
</Button>
</div>
</div>
{/* Mana Skills */}
<div>
<div className="text-xs text-gray-400 mb-2">Mana Skills:</div>
<div className="flex gap-2 flex-wrap">
<Button
size="sm"
variant="outline"
onClick={() => {
const manaSkills = ['manaWell', 'manaFlow', 'elemAttune', 'manaOverflow'];
manaSkills.forEach(skillId => {
if (store.skills[skillId] !== undefined) {
store.skills[skillId] = Math.min((store.skills[skillId] || 0) + 1, 10);
} else {
store.skills[skillId] = 1;
}
});
}}
>
+1 All Mana
</Button>
<Button
size="sm"
variant="outline"
onClick={() => {
const manaSkills = ['manaWell', 'manaFlow', 'elemAttune', 'manaOverflow'];
manaSkills.forEach(skillId => {
store.skills[skillId] = 10;
});
}}
>
Max All Mana
</Button>
</div>
</div>
{/* Study Skills */}
<div>
<div className="text-xs text-gray-400 mb-2">Study Skills:</div>
<div className="flex gap-2 flex-wrap">
<Button
size="sm"
variant="outline"
onClick={() => {
const studySkills = ['quickLearner', 'focusedMind', 'meditation', 'knowledgeRetention'];
studySkills.forEach(skillId => {
if (store.skills[skillId] !== undefined) {
store.skills[skillId] = Math.min((store.skills[skillId] || 0) + 1, 10);
} else {
store.skills[skillId] = 1;
}
});
}}
>
+1 All Study
</Button>
<Button
size="sm"
variant="outline"
onClick={() => {
const studySkills = ['quickLearner', 'focusedMind', 'meditation', 'knowledgeRetention'];
studySkills.forEach(skillId => {
store.skills[skillId] = 10;
});
}}
>
Max All Study
</Button>
</div>
</div>
{/* Crafting Skills */}
<div>
<div className="text-xs text-gray-400 mb-2">Crafting Skills:</div>
<div className="flex gap-2 flex-wrap">
<Button
size="sm"
variant="outline"
onClick={() => {
const craftSkills = ['effCrafting', 'fieldRepair', 'elemCrafting'];
craftSkills.forEach(skillId => {
if (store.skills[skillId] !== undefined) {
store.skills[skillId] = Math.min((store.skills[skillId] || 0) + 1, 10);
} else {
store.skills[skillId] = 1;
}
});
}}
>
+1 All Crafting
</Button>
<Button
size="sm"
variant="outline"
onClick={() => {
const craftSkills = ['effCrafting', 'fieldRepair', 'elemCrafting'];
craftSkills.forEach(skillId => {
store.skills[skillId] = 10;
});
}}
>
Max All Crafting
</Button>
</div>
</div>
{/* Research Effects */}
<div>
<div className="text-xs text-gray-400 mb-2">Research Effects:</div>
<div className="flex gap-2 flex-wrap">
<Button
size="sm"
variant="outline"
onClick={() => {
// Unlock all spell research
const researchSkills = [
'researchManaSpells', 'researchFireSpells', 'researchWaterSpells',
'researchAirSpells', 'researchEarthSpells', 'researchLightSpells',
'researchDarkSpells', 'researchLifeDeathSpells',
'researchAdvancedFire', 'researchAdvancedWater', 'researchAdvancedAir',
'researchAdvancedEarth', 'researchAdvancedLight', 'researchAdvancedDark',
'researchMasterFire', 'researchMasterWater', 'researchMasterEarth',
'researchDamageEffects', 'researchCombatEffects',
'researchManaEffects', 'researchAdvancedManaEffects', 'researchUtilityEffects'
];
researchSkills.forEach(skillId => {
store.skills[skillId] = 1;
});
}}
>
Unlock All Research
</Button>
<Button
size="sm"
variant="outline"
onClick={() => {
// Add all unlocked effects to unlockedEffects
const effectIds = Object.keys(store.unlockedEffects || {});
// Add spell effects, mana effects, combat effects, utility effects
const allEffectIds = [
// Spell effects
'spell_manaBolt', 'spell_manaStrike', 'spell_fireball', 'spell_emberShot',
'spell_waterJet', 'spell_iceShard', 'spell_gust', 'spell_windSlash',
'spell_stoneBullet', 'spell_rockSpike', 'spell_lightLance', 'spell_radiance',
'spell_shadowBolt', 'spell_darkPulse', 'spell_drain',
// Tier 2 spells
'spell_inferno', 'spell_flameWave', 'spell_tidalWave', 'spell_iceStorm',
'spell_hurricane', 'spell_windBlade', 'spell_earthquake', 'spell_stoneBarrage',
'spell_solarFlare', 'spell_divineSmite', 'spell_voidRift', 'spell_shadowStorm',
// Tier 3 spells
'spell_pyroclasm', 'spell_tsunami', 'spell_meteorStrike',
// Lightning
'spell_spark', 'spell_lightningBolt', 'spell_chainLightning',
'spell_stormCall', 'spell_thunderStrike',
// Metal and Sand
'spell_metalShard', 'spell_ironFist', 'spell_steelTempest', 'spell_furnaceBlast',
'spell_sandBlast', 'spell_sandstorm', 'spell_desertWind', 'spell_duneCollapse',
// Mana effects
'mana_cap_50', 'mana_cap_100', 'mana_regen_1', 'mana_regen_2', 'mana_regen_5',
'click_mana_1', 'click_mana_3',
// Combat effects
'damage_5', 'damage_10', 'damage_pct_10', 'crit_5', 'attack_speed_10',
// Utility effects
'meditate_10', 'study_10', 'insight_5',
// Special
'spell_echo_10', 'guardian_dmg_10', 'overpower_80',
// Weapon mana
'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 enchants
'sword_fire', 'sword_frost', 'sword_lightning', 'sword_void'
];
allEffectIds.forEach(id => {
if (!store.unlockedEffects.includes(id)) {
store.unlockedEffects.push(id);
}
});
}}
>
Unlock All Effects
</Button>
</div>
</div>
{/* Max All */}
<Separator className="bg-gray-700" />
<div className="flex gap-2">
<Button
size="sm"
className="bg-purple-600 hover:bg-purple-700"
onClick={() => {
// Max all skills
Object.keys(store.skills).forEach(skillId => {
const current = store.skills[skillId] || 0;
if (current < 10) {
store.skills[skillId] = 10;
}
});
// Unlock all effects
const allEffectIds = [
'spell_manaBolt', 'spell_manaStrike', 'spell_fireball', 'spell_emberShot',
'spell_waterJet', 'spell_iceShard', 'spell_gust', 'spell_windSlash',
'spell_stoneBullet', 'spell_rockSpike', 'spell_lightLance', 'spell_radiance',
'spell_shadowBolt', 'spell_darkPulse', 'spell_drain', 'spell_inferno',
'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',
'click_mana_1', 'click_mana_3', 'damage_5', 'damage_10', 'damage_pct_10',
'crit_5', 'attack_speed_10', 'meditate_10', 'study_10', 'insight_5',
'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'
];
allEffectIds.forEach(id => {
if (!store.unlockedEffects.includes(id)) {
store.unlockedEffects.push(id);
}
});
}}
>
🚀 Max Everything
</Button>
</div>
</div>
</CardContent>
</Card>
</div> </div>
</div> </div>
); );

View File

@@ -0,0 +1,46 @@
'use client';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Badge } from '@/components/ui/badge';
import type { GameStore } from '@/lib/game/store';
import { LootInventoryDisplay } from '@/components/game/LootInventory';
export interface LootTabProps {
store: GameStore;
}
export function LootTab({ store }: LootTabProps) {
const inventory = store.lootInventory;
const elements = store.elements;
const equipmentInstances = store.equipmentInstances;
// Count items for badge
const materialCount = Object.values(inventory.materials).reduce((a, b) => a + b, 0);
const blueprintCount = inventory.blueprints.length;
const equipmentCount = Object.keys(equipmentInstances).length;
const totalItems = materialCount + blueprintCount + equipmentCount;
return (
<div className="space-y-4">
<Card className="bg-gray-900/80 border-gray-700">
<CardHeader className="pb-2">
<CardTitle className="text-amber-400 text-sm flex items-center gap-2">
💎 Loot Inventory
<Badge className="ml-auto bg-gray-800 text-gray-300">
{totalItems} items
</Badge>
</CardTitle>
</CardHeader>
<CardContent>
<LootInventoryDisplay
inventory={inventory}
elements={elements}
equipmentInstances={equipmentInstances}
onDeleteMaterial={store.deleteMaterial}
onDeleteEquipment={store.deleteEquipmentInstance}
/>
</CardContent>
</Card>
</div>
);
}

View File

@@ -10,3 +10,5 @@ export { StatsTab } from './StatsTab';
export { EquipmentTab } from './EquipmentTab'; export { EquipmentTab } from './EquipmentTab';
export { AttunementsTab } from './AttunementsTab'; export { AttunementsTab } from './AttunementsTab';
export { DebugTab } from './DebugTab'; export { DebugTab } from './DebugTab';
export { LootTab } from './LootTab';
export { AchievementsTab } from './AchievementsTab';