feat: add wizard and physical gear branches to Fabricator
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m21s
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m21s
- Split fabricator-recipes.ts into 4 files (all under 400 lines): - fabricator-recipes.ts: core/elemental equipment recipes + helpers - fabricator-wizard-recipes.ts: 7 wizard branch recipes (staffs, circlet, robe, catalyst, pendant) - fabricator-physical-recipes.ts: 9 physical branch recipes (blades, helm, robe, boots, gauntlets, shields) - fabricator-material-recipes.ts: 12 material crafting recipes - Added branch filter UI (All/Elemental/Wizard/Physical) to FabricatorSubTab - All 902 tests pass
This commit is contained in:
@@ -7,7 +7,7 @@ import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { ScrollArea } from '@/components/ui/scroll-area';
|
||||
import { Separator } from '@/components/ui/separator';
|
||||
import { Anvil, FlaskConical, Hammer, Package } from 'lucide-react';
|
||||
import { Anvil, FlaskConical, Hammer, Package, Sparkles, Sword } from 'lucide-react';
|
||||
import { MaterialRecipeCard } from './MaterialRecipeCard';
|
||||
import {
|
||||
FABRICATOR_RECIPES,
|
||||
@@ -20,6 +20,24 @@ import { LOOT_DROPS, LOOT_RARITY_COLORS } from '@/lib/game/data/loot-drops';
|
||||
import { useCraftingStore, useManaStore } from '@/lib/game/stores';
|
||||
import type { FabricatorRecipe } from '@/lib/game/data/fabricator-recipes';
|
||||
|
||||
const BRANCH_RECIPE_IDS = new Set([
|
||||
'oakStaff', 'arcanistStaff', 'battlestaff', 'arcanistCirclet', 'arcanistRobe',
|
||||
'voidCatalyst', 'arcanistPendant',
|
||||
'crystalBlade', 'arcanistBlade', 'voidBlade', 'battleHelm', 'battleRobe',
|
||||
'battleBoots', 'combatGauntlets', 'runicShield', 'manaShield',
|
||||
]);
|
||||
|
||||
function isWizardBranch(recipe: FabricatorRecipe): boolean {
|
||||
return ['oakStaff', 'arcanistStaff', 'battlestaff', 'arcanistCirclet',
|
||||
'arcanistRobe', 'voidCatalyst', 'arcanistPendant'].includes(recipe.id);
|
||||
}
|
||||
|
||||
function isPhysicalBranch(recipe: FabricatorRecipe): boolean {
|
||||
return ['crystalBlade', 'arcanistBlade', 'voidBlade', 'battleHelm',
|
||||
'battleRobe', 'battleBoots', 'combatGauntlets', 'runicShield',
|
||||
'manaShield'].includes(recipe.id);
|
||||
}
|
||||
|
||||
function RecipeCard({
|
||||
recipe,
|
||||
materials,
|
||||
@@ -35,7 +53,6 @@ function RecipeCard({
|
||||
onCraft: (recipe: FabricatorRecipe) => void;
|
||||
isCrafting: boolean;
|
||||
}) {
|
||||
// Determine the correct mana amount based on the recipe's mana type
|
||||
const pool = recipe.manaType === 'raw'
|
||||
? rawMana
|
||||
: (elementalMana[recipe.manaType]?.current ?? 0);
|
||||
@@ -111,9 +128,12 @@ function RecipeCard({
|
||||
);
|
||||
}
|
||||
|
||||
type BranchFilter = 'all' | 'elemental' | 'wizard' | 'physical';
|
||||
|
||||
export function FabricatorSubTab() {
|
||||
const [selectedManaType, setSelectedManaType] = useState<string>('earth');
|
||||
const [activeSection, setActiveSection] = useState<'equipment' | 'materials'>('equipment');
|
||||
const [branchFilter, setBranchFilter] = useState<BranchFilter>('all');
|
||||
|
||||
const lootInventory = useCraftingStore((s) => s.lootInventory);
|
||||
const equipmentCraftingProgress = useCraftingStore((s) => s.equipmentCraftingProgress);
|
||||
@@ -127,10 +147,17 @@ export function FabricatorSubTab() {
|
||||
return [...new Set(FABRICATOR_RECIPES.map((r) => r.manaType))];
|
||||
}, []);
|
||||
|
||||
const filteredRecipes = useMemo(
|
||||
() => getRecipesByManaType(selectedManaType),
|
||||
[selectedManaType],
|
||||
);
|
||||
const filteredRecipes = useMemo(() => {
|
||||
let recipes = FABRICATOR_RECIPES;
|
||||
if (branchFilter === 'elemental') {
|
||||
recipes = recipes.filter(r => !BRANCH_RECIPE_IDS.has(r.id));
|
||||
} else if (branchFilter === 'wizard') {
|
||||
recipes = recipes.filter(r => isWizardBranch(r));
|
||||
} else if (branchFilter === 'physical') {
|
||||
recipes = recipes.filter(r => isPhysicalBranch(r));
|
||||
}
|
||||
return recipes.filter(r => r.manaType === selectedManaType);
|
||||
}, [selectedManaType, branchFilter]);
|
||||
|
||||
const isCrafting = equipmentCraftingProgress !== null;
|
||||
|
||||
@@ -166,20 +193,58 @@ export function FabricatorSubTab() {
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Mana type filter — only for equipment */}
|
||||
{/* Branch filter — only for equipment */}
|
||||
{activeSection === 'equipment' && (
|
||||
<div className="flex gap-2 flex-wrap">
|
||||
{availableManaTypes.map((mt) => (
|
||||
<>
|
||||
<div className="flex gap-2 flex-wrap">
|
||||
<Button
|
||||
key={mt}
|
||||
variant={selectedManaType === mt ? 'default' : 'outline'}
|
||||
variant={branchFilter === 'all' ? 'default' : 'outline'}
|
||||
size="sm"
|
||||
onClick={() => setSelectedManaType(mt)}
|
||||
onClick={() => setBranchFilter('all')}
|
||||
>
|
||||
{MANA_TYPE_LABELS[mt] ?? mt}
|
||||
<Anvil className="w-3 h-3 mr-1" />
|
||||
All
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
<Button
|
||||
variant={branchFilter === 'elemental' ? 'default' : 'outline'}
|
||||
size="sm"
|
||||
onClick={() => setBranchFilter('elemental')}
|
||||
>
|
||||
<Anvil className="w-3 h-3 mr-1" />
|
||||
Elemental
|
||||
</Button>
|
||||
<Button
|
||||
variant={branchFilter === 'wizard' ? 'default' : 'outline'}
|
||||
size="sm"
|
||||
onClick={() => setBranchFilter('wizard')}
|
||||
>
|
||||
<Sparkles className="w-3 h-3 mr-1" />
|
||||
Wizard
|
||||
</Button>
|
||||
<Button
|
||||
variant={branchFilter === 'physical' ? 'default' : 'outline'}
|
||||
size="sm"
|
||||
onClick={() => setBranchFilter('physical')}
|
||||
>
|
||||
<Sword className="w-3 h-3 mr-1" />
|
||||
Physical
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Mana type filter */}
|
||||
<div className="flex gap-2 flex-wrap">
|
||||
{availableManaTypes.map((mt) => (
|
||||
<Button
|
||||
key={mt}
|
||||
variant={selectedManaType === mt ? 'default' : 'outline'}
|
||||
size="sm"
|
||||
onClick={() => setSelectedManaType(mt)}
|
||||
>
|
||||
{MANA_TYPE_LABELS[mt] ?? mt}
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
||||
|
||||
Reference in New Issue
Block a user