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:
@@ -1,94 +0,0 @@
|
|||||||
# StatsTab Loading Bug Investigation Report
|
|
||||||
|
|
||||||
## Critical Issue Found
|
|
||||||
|
|
||||||
**Main Problem**: The `StatsTab.tsx` component has incorrect import paths that prevent it from loading. All section imports use the pattern `./StatsTab/...` instead of `./` which causes TypeScript compilation failures.
|
|
||||||
|
|
||||||
### Root Cause Analysis
|
|
||||||
|
|
||||||
1. **Incorrect Import Paths**: In `StatsTab.tsx`, sections are imported with incorrect relative paths:
|
|
||||||
```typescript
|
|
||||||
// BROKEN: All these imports are wrong
|
|
||||||
import { ManaStatsSection } from './StatsTab/ManaStatsSection';
|
|
||||||
import { CombatStatsSection } from './StatsTab/CombatStatsSection';
|
|
||||||
import { PactStatusSection } from './StatsTab/PactStatusSection';
|
|
||||||
import { StudyStatsSection } from './StatsTab/StudyStatsSection';
|
|
||||||
import { ElementStatsSection } from './StatsTab/ElementStatsSection';
|
|
||||||
import { LoopStatsSection } from './StatsTab/LoopStatsSection';
|
|
||||||
|
|
||||||
// CORRECT (pattern used by other tabs):
|
|
||||||
// import { ComponentName } from './ComponentName';
|
|
||||||
```
|
|
||||||
|
|
||||||
2. **Missing Test Files**: No test files exist for StatsTab, unlike other tabs which have comprehensive test coverage.
|
|
||||||
|
|
||||||
3. **TypeScript Compilation Status**: The codebase has significant other TypeScript errors, but StatsTab itself would fail to compile due to the import resolution errors.
|
|
||||||
|
|
||||||
### File Structure Analysis
|
|
||||||
|
|
||||||
```
|
|
||||||
src/components/game/tabs/
|
|
||||||
├── StatsTab/ ← Directory contains section files
|
|
||||||
│ ├── CombatStatsSection.tsx
|
|
||||||
│ ├── ElementStatsSection.tsx
|
|
||||||
│ ├── LoopStatsSection.tsx
|
|
||||||
│ ├── ManaStatsSection.tsx
|
|
||||||
│ ├── PactStatusSection.tsx
|
|
||||||
│ └── StudyStatsSection.tsx
|
|
||||||
└── StatsTab.tsx ← Main component file expecting nested 'StatsTab/' paths
|
|
||||||
```
|
|
||||||
|
|
||||||
### Impact
|
|
||||||
|
|
||||||
- StatsTab does not load due to import resolution errors
|
|
||||||
- Application fails to compile for StatsTab modules
|
|
||||||
- No way to access character stats information
|
|
||||||
|
|
||||||
### Comparison with Other Tabs
|
|
||||||
|
|
||||||
All other tabs follow the correct pattern:
|
|
||||||
|
|
||||||
**EquipmentTab.tsx** (lines 7-9):
|
|
||||||
```typescript
|
|
||||||
import { EquipmentSlotGrid } from './EquipmentTab/EquipmentSlotGrid';
|
|
||||||
import { InventoryList } from './EquipmentTab/InventoryList';
|
|
||||||
import { EquipmentEffectsSummary } from './EquipmentTab/EquipmentEffectsSummary';
|
|
||||||
```
|
|
||||||
|
|
||||||
Note: EquipmentTab uses `./EquipmentTab/...` pattern while StatsTab incorrectly uses `./StatsTab/...` pattern relative to StatsTab.tsx.
|
|
||||||
|
|
||||||
### Recommended Fix
|
|
||||||
|
|
||||||
Change all import paths in `StatsTab.tsx` from:
|
|
||||||
```typescript
|
|
||||||
import { SectionName } from './StatsTab/SectionName';
|
|
||||||
```
|
|
||||||
|
|
||||||
To:
|
|
||||||
```typescript
|
|
||||||
import { SectionName } from './SectionName';
|
|
||||||
```
|
|
||||||
|
|
||||||
This will make all section files resolve correctly since they're located directly in the `StatsTab/` directory.
|
|
||||||
|
|
||||||
### Files Read
|
|
||||||
|
|
||||||
- ✅ StatsTab.tsx (main component)
|
|
||||||
- ✅ ManaStatsSection.tsx
|
|
||||||
- ✅ CombatStatsSection.tsx
|
|
||||||
- ✅ ElementStatsSection.tsx
|
|
||||||
- ✅ LoopStatsSection.tsx
|
|
||||||
- ✅ PactStatusSection.tsx
|
|
||||||
- ✅ StudyStatsSection.tsx
|
|
||||||
- ✅ index.ts (showing StatsTab export)
|
|
||||||
|
|
||||||
### Assessment
|
|
||||||
|
|
||||||
**Clear Root Cause**: The incorrect import paths prevent the component from loading. Fixing these import paths will resolve the issue.
|
|
||||||
|
|
||||||
**Likely Guiding Factors**:
|
|
||||||
1. File was moved or renamed after being created, causing import paths to become stale
|
|
||||||
2. Developer accidentally referenced the directory name in import paths
|
|
||||||
3. Copy-paste error when creating StatsTab from another tab template
|
|
||||||
|
|
||||||
The fix is straightforward: correct all six import statements to use the proper relative path.
|
|
||||||
@@ -1,11 +1,4 @@
|
|||||||
# Circular Dependencies
|
# Circular Dependencies
|
||||||
Generated: 2026-05-27T12:13:52.087Z
|
Generated: 2026-05-27T12:39:49.991Z
|
||||||
Found: 1 circular chain(s) — these MUST be fixed before modifying involved files.
|
|
||||||
|
|
||||||
1. 1) data/fabricator-recipes.ts > data/material-recipes.ts
|
No circular dependencies found. ✅
|
||||||
|
|
||||||
## How to fix
|
|
||||||
1. Identify which import in the chain can be extracted to a shared types/utils file.
|
|
||||||
2. Move the shared type or function there.
|
|
||||||
3. Both files import from the new shared module instead of each other.
|
|
||||||
4. Run: bunx madge --circular src/lib/game (should return clean)
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"_meta": {
|
"_meta": {
|
||||||
"generated": "2026-05-27T12:13:50.329Z",
|
"generated": "2026-05-27T12:39:48.093Z",
|
||||||
"description": "Import dependency graph for src/lib/game. Keys are files, values are arrays of files they import.",
|
"description": "Import dependency graph for src/lib/game. Keys are files, values are arrays of files they import.",
|
||||||
"usage": "To find what a file affects, search for its path in the VALUES. To find what a file depends on, look at its KEY entry."
|
"usage": "To find what a file affects, search for its path in the VALUES. To find what a file depends on, look at its KEY entry."
|
||||||
},
|
},
|
||||||
@@ -365,9 +365,11 @@
|
|||||||
"data/equipment/equipment-types-data.ts",
|
"data/equipment/equipment-types-data.ts",
|
||||||
"data/equipment/types.ts"
|
"data/equipment/types.ts"
|
||||||
],
|
],
|
||||||
|
"data/fabricator-recipe-types.ts": [
|
||||||
|
"data/equipment/types.ts"
|
||||||
|
],
|
||||||
"data/fabricator-recipes.ts": [
|
"data/fabricator-recipes.ts": [
|
||||||
"data/equipment/types.ts",
|
"data/fabricator-recipe-types.ts"
|
||||||
"data/material-recipes.ts"
|
|
||||||
],
|
],
|
||||||
"data/golems/base-golems.ts": [
|
"data/golems/base-golems.ts": [
|
||||||
"data/golems/types.ts"
|
"data/golems/types.ts"
|
||||||
@@ -405,9 +407,6 @@
|
|||||||
"data/loot-drops.ts": [
|
"data/loot-drops.ts": [
|
||||||
"types/game.ts"
|
"types/game.ts"
|
||||||
],
|
],
|
||||||
"data/material-recipes.ts": [
|
|
||||||
"data/fabricator-recipes.ts"
|
|
||||||
],
|
|
||||||
"effects.ts": [
|
"effects.ts": [
|
||||||
"data/enchantment-effects.ts",
|
"data/enchantment-effects.ts",
|
||||||
"effects/discipline-effects.ts",
|
"effects/discipline-effects.ts",
|
||||||
|
|||||||
@@ -313,8 +313,11 @@ Mana-Loop/
|
|||||||
│ │ │ ├── crafting-recipes.ts
|
│ │ │ ├── crafting-recipes.ts
|
||||||
│ │ │ ├── enchantment-effects.ts
|
│ │ │ ├── enchantment-effects.ts
|
||||||
│ │ │ ├── enchantment-types.ts
|
│ │ │ ├── enchantment-types.ts
|
||||||
|
│ │ │ ├── fabricator-material-recipes.ts
|
||||||
|
│ │ │ ├── fabricator-physical-recipes.ts
|
||||||
│ │ │ ├── fabricator-recipe-types.ts
|
│ │ │ ├── fabricator-recipe-types.ts
|
||||||
│ │ │ ├── fabricator-recipes.ts
|
│ │ │ ├── fabricator-recipes.ts
|
||||||
|
│ │ │ ├── fabricator-wizard-recipes.ts
|
||||||
│ │ │ ├── guardian-data.ts
|
│ │ │ ├── guardian-data.ts
|
||||||
│ │ │ ├── guardian-encounters.ts
|
│ │ │ ├── guardian-encounters.ts
|
||||||
│ │ │ └── loot-drops.ts
|
│ │ │ └── loot-drops.ts
|
||||||
@@ -389,7 +392,6 @@ Mana-Loop/
|
|||||||
├── Caddyfile
|
├── Caddyfile
|
||||||
├── Dockerfile
|
├── Dockerfile
|
||||||
├── README.md
|
├── README.md
|
||||||
├── STATS_TAB_INVESTIGATION_REPORT.md
|
|
||||||
├── bun.lock
|
├── bun.lock
|
||||||
├── bunfig.toml
|
├── bunfig.toml
|
||||||
├── components.json
|
├── components.json
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
|||||||
import { Badge } from '@/components/ui/badge';
|
import { Badge } from '@/components/ui/badge';
|
||||||
import { ScrollArea } from '@/components/ui/scroll-area';
|
import { ScrollArea } from '@/components/ui/scroll-area';
|
||||||
import { Separator } from '@/components/ui/separator';
|
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 { MaterialRecipeCard } from './MaterialRecipeCard';
|
||||||
import {
|
import {
|
||||||
FABRICATOR_RECIPES,
|
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 { useCraftingStore, useManaStore } from '@/lib/game/stores';
|
||||||
import type { FabricatorRecipe } from '@/lib/game/data/fabricator-recipes';
|
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({
|
function RecipeCard({
|
||||||
recipe,
|
recipe,
|
||||||
materials,
|
materials,
|
||||||
@@ -35,7 +53,6 @@ function RecipeCard({
|
|||||||
onCraft: (recipe: FabricatorRecipe) => void;
|
onCraft: (recipe: FabricatorRecipe) => void;
|
||||||
isCrafting: boolean;
|
isCrafting: boolean;
|
||||||
}) {
|
}) {
|
||||||
// Determine the correct mana amount based on the recipe's mana type
|
|
||||||
const pool = recipe.manaType === 'raw'
|
const pool = recipe.manaType === 'raw'
|
||||||
? rawMana
|
? rawMana
|
||||||
: (elementalMana[recipe.manaType]?.current ?? 0);
|
: (elementalMana[recipe.manaType]?.current ?? 0);
|
||||||
@@ -111,9 +128,12 @@ function RecipeCard({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type BranchFilter = 'all' | 'elemental' | 'wizard' | 'physical';
|
||||||
|
|
||||||
export function FabricatorSubTab() {
|
export function FabricatorSubTab() {
|
||||||
const [selectedManaType, setSelectedManaType] = useState<string>('earth');
|
const [selectedManaType, setSelectedManaType] = useState<string>('earth');
|
||||||
const [activeSection, setActiveSection] = useState<'equipment' | 'materials'>('equipment');
|
const [activeSection, setActiveSection] = useState<'equipment' | 'materials'>('equipment');
|
||||||
|
const [branchFilter, setBranchFilter] = useState<BranchFilter>('all');
|
||||||
|
|
||||||
const lootInventory = useCraftingStore((s) => s.lootInventory);
|
const lootInventory = useCraftingStore((s) => s.lootInventory);
|
||||||
const equipmentCraftingProgress = useCraftingStore((s) => s.equipmentCraftingProgress);
|
const equipmentCraftingProgress = useCraftingStore((s) => s.equipmentCraftingProgress);
|
||||||
@@ -127,10 +147,17 @@ export function FabricatorSubTab() {
|
|||||||
return [...new Set(FABRICATOR_RECIPES.map((r) => r.manaType))];
|
return [...new Set(FABRICATOR_RECIPES.map((r) => r.manaType))];
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const filteredRecipes = useMemo(
|
const filteredRecipes = useMemo(() => {
|
||||||
() => getRecipesByManaType(selectedManaType),
|
let recipes = FABRICATOR_RECIPES;
|
||||||
[selectedManaType],
|
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;
|
const isCrafting = equipmentCraftingProgress !== null;
|
||||||
|
|
||||||
@@ -166,20 +193,58 @@ export function FabricatorSubTab() {
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Mana type filter — only for equipment */}
|
{/* Branch filter — only for equipment */}
|
||||||
{activeSection === 'equipment' && (
|
{activeSection === 'equipment' && (
|
||||||
<div className="flex gap-2 flex-wrap">
|
<>
|
||||||
{availableManaTypes.map((mt) => (
|
<div className="flex gap-2 flex-wrap">
|
||||||
<Button
|
<Button
|
||||||
key={mt}
|
variant={branchFilter === 'all' ? 'default' : 'outline'}
|
||||||
variant={selectedManaType === mt ? 'default' : 'outline'}
|
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={() => setSelectedManaType(mt)}
|
onClick={() => setBranchFilter('all')}
|
||||||
>
|
>
|
||||||
{MANA_TYPE_LABELS[mt] ?? mt}
|
<Anvil className="w-3 h-3 mr-1" />
|
||||||
|
All
|
||||||
</Button>
|
</Button>
|
||||||
))}
|
<Button
|
||||||
</div>
|
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">
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
||||||
|
|||||||
@@ -0,0 +1,183 @@
|
|||||||
|
// ─── Material Crafting Recipes ─────────────────────────────────────────────────
|
||||||
|
// Separate file to keep equipment recipes under the 400-line limit.
|
||||||
|
|
||||||
|
import type { FabricatorRecipe } from './fabricator-recipe-types';
|
||||||
|
|
||||||
|
export const MATERIAL_RECIPES: FabricatorRecipe[] = [
|
||||||
|
{
|
||||||
|
id: 'manaCrystal',
|
||||||
|
name: 'Mana Crystal',
|
||||||
|
description: 'Condense raw mana into a stable crystal. Used in all advanced crafting.',
|
||||||
|
manaType: 'raw',
|
||||||
|
equipmentTypeId: 'basicStaff',
|
||||||
|
slot: 'mainHand',
|
||||||
|
materials: {},
|
||||||
|
manaCost: 500,
|
||||||
|
craftTime: 1,
|
||||||
|
rarity: 'uncommon',
|
||||||
|
gearTrait: 'Produces 1 Mana Crystal',
|
||||||
|
recipeType: 'material',
|
||||||
|
resultMaterial: 'manaCrystal',
|
||||||
|
resultAmount: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'manaCrystalDustCraft',
|
||||||
|
name: 'Mana Crystal Dust',
|
||||||
|
description: 'Grind a Mana Crystal into dust. Used as a base material.',
|
||||||
|
manaType: 'raw',
|
||||||
|
equipmentTypeId: 'basicStaff',
|
||||||
|
slot: 'mainHand',
|
||||||
|
materials: { manaCrystal: 1 },
|
||||||
|
manaCost: 10,
|
||||||
|
craftTime: 1,
|
||||||
|
rarity: 'common',
|
||||||
|
gearTrait: 'Produces 2 Mana Crystal Dust',
|
||||||
|
recipeType: 'material',
|
||||||
|
resultMaterial: 'manaCrystalDust',
|
||||||
|
resultAmount: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'fireCrystal',
|
||||||
|
name: 'Fire Attuned Crystal',
|
||||||
|
description: 'Infuse a Mana Crystal with fire mana to attune it to the flame element.',
|
||||||
|
manaType: 'fire',
|
||||||
|
equipmentTypeId: 'basicStaff',
|
||||||
|
slot: 'mainHand',
|
||||||
|
materials: { manaCrystal: 1 },
|
||||||
|
manaCost: 100,
|
||||||
|
craftTime: 1,
|
||||||
|
rarity: 'rare',
|
||||||
|
gearTrait: 'Produces 1 Fire Attuned Crystal',
|
||||||
|
recipeType: 'material',
|
||||||
|
resultMaterial: 'fireCrystal',
|
||||||
|
resultAmount: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'waterCrystal',
|
||||||
|
name: 'Water Attuned Crystal',
|
||||||
|
description: 'Infuse a Mana Crystal with water mana to attune it to the flow element.',
|
||||||
|
manaType: 'water',
|
||||||
|
equipmentTypeId: 'basicStaff',
|
||||||
|
slot: 'mainHand',
|
||||||
|
materials: { manaCrystal: 1 },
|
||||||
|
manaCost: 100,
|
||||||
|
craftTime: 1,
|
||||||
|
rarity: 'rare',
|
||||||
|
gearTrait: 'Produces 1 Water Attuned Crystal',
|
||||||
|
recipeType: 'material',
|
||||||
|
resultMaterial: 'waterCrystal',
|
||||||
|
resultAmount: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'airCrystal',
|
||||||
|
name: 'Air Attuned Crystal',
|
||||||
|
description: 'Infuse a Mana Crystal with air mana to attune it to the wind element.',
|
||||||
|
manaType: 'air',
|
||||||
|
equipmentTypeId: 'basicStaff',
|
||||||
|
slot: 'mainHand',
|
||||||
|
materials: { manaCrystal: 1 },
|
||||||
|
manaCost: 100,
|
||||||
|
craftTime: 1,
|
||||||
|
rarity: 'rare',
|
||||||
|
gearTrait: 'Produces 1 Air Attuned Crystal',
|
||||||
|
recipeType: 'material',
|
||||||
|
resultMaterial: 'airCrystal',
|
||||||
|
resultAmount: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'earthCrystal',
|
||||||
|
name: 'Earth Attuned Crystal',
|
||||||
|
description: 'Infuse a Mana Crystal with earth mana to attune it to the stone element.',
|
||||||
|
manaType: 'earth',
|
||||||
|
equipmentTypeId: 'basicStaff',
|
||||||
|
slot: 'mainHand',
|
||||||
|
materials: { manaCrystal: 1 },
|
||||||
|
manaCost: 100,
|
||||||
|
craftTime: 1,
|
||||||
|
rarity: 'rare',
|
||||||
|
gearTrait: 'Produces 1 Earth Attuned Crystal',
|
||||||
|
recipeType: 'material',
|
||||||
|
resultMaterial: 'earthCrystal',
|
||||||
|
resultAmount: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'lightCrystal',
|
||||||
|
name: 'Light Attuned Crystal',
|
||||||
|
description: 'Infuse a Mana Crystal with light mana to attune it to the radiant element.',
|
||||||
|
manaType: 'light',
|
||||||
|
equipmentTypeId: 'basicStaff',
|
||||||
|
slot: 'mainHand',
|
||||||
|
materials: { manaCrystal: 1 },
|
||||||
|
manaCost: 100,
|
||||||
|
craftTime: 1,
|
||||||
|
rarity: 'rare',
|
||||||
|
gearTrait: 'Produces 1 Light Attuned Crystal',
|
||||||
|
recipeType: 'material',
|
||||||
|
resultMaterial: 'lightCrystal',
|
||||||
|
resultAmount: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'darkCrystal',
|
||||||
|
name: 'Dark Attuned Crystal',
|
||||||
|
description: 'Infuse a Mana Crystal with dark mana to attune it to the shadow element.',
|
||||||
|
manaType: 'dark',
|
||||||
|
equipmentTypeId: 'basicStaff',
|
||||||
|
slot: 'mainHand',
|
||||||
|
materials: { manaCrystal: 1 },
|
||||||
|
manaCost: 100,
|
||||||
|
craftTime: 1,
|
||||||
|
rarity: 'rare',
|
||||||
|
gearTrait: 'Produces 1 Dark Attuned Crystal',
|
||||||
|
recipeType: 'material',
|
||||||
|
resultMaterial: 'darkCrystal',
|
||||||
|
resultAmount: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'metalCrystal',
|
||||||
|
name: 'Metal Attuned Crystal',
|
||||||
|
description: 'Infuse a Mana Crystal with metal mana to attune it to the alloy element.',
|
||||||
|
manaType: 'metal',
|
||||||
|
equipmentTypeId: 'basicStaff',
|
||||||
|
slot: 'mainHand',
|
||||||
|
materials: { manaCrystal: 1 },
|
||||||
|
manaCost: 100,
|
||||||
|
craftTime: 1,
|
||||||
|
rarity: 'rare',
|
||||||
|
gearTrait: 'Produces 1 Metal Attuned Crystal',
|
||||||
|
recipeType: 'material',
|
||||||
|
resultMaterial: 'metalCrystal',
|
||||||
|
resultAmount: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'crystalCrystal',
|
||||||
|
name: 'Crystal Attuned Crystal',
|
||||||
|
description: 'Infuse a Mana Crystal with crystal mana to attune it to the prismatic element.',
|
||||||
|
manaType: 'crystal',
|
||||||
|
equipmentTypeId: 'basicStaff',
|
||||||
|
slot: 'mainHand',
|
||||||
|
materials: { manaCrystal: 1 },
|
||||||
|
manaCost: 100,
|
||||||
|
craftTime: 1,
|
||||||
|
rarity: 'epic',
|
||||||
|
gearTrait: 'Produces 1 Crystal Attuned Crystal',
|
||||||
|
recipeType: 'material',
|
||||||
|
resultMaterial: 'crystalCrystal',
|
||||||
|
resultAmount: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'elementalCore',
|
||||||
|
name: 'Elemental Core',
|
||||||
|
description: 'Combine mana crystals and all four base elements into a powerful core.',
|
||||||
|
manaType: 'raw',
|
||||||
|
equipmentTypeId: 'basicStaff',
|
||||||
|
slot: 'mainHand',
|
||||||
|
materials: { manaCrystal: 10 },
|
||||||
|
manaCost: 100,
|
||||||
|
craftTime: 10,
|
||||||
|
rarity: 'epic',
|
||||||
|
gearTrait: 'Produces 1 Elemental Core',
|
||||||
|
recipeType: 'material',
|
||||||
|
resultMaterial: 'elementalCore',
|
||||||
|
resultAmount: 1,
|
||||||
|
},
|
||||||
|
];
|
||||||
@@ -0,0 +1,125 @@
|
|||||||
|
// ─── Physical Branch Recipes ────────────────────────────────────────────────────
|
||||||
|
// Sword, speed, and combat gear focused on attack speed, damage, and combat
|
||||||
|
// effectiveness.
|
||||||
|
|
||||||
|
import type { FabricatorRecipe } from './fabricator-recipe-types';
|
||||||
|
|
||||||
|
export const PHYSICAL_BRANCH_RECIPES: FabricatorRecipe[] = [
|
||||||
|
{
|
||||||
|
id: 'crystalBlade',
|
||||||
|
name: 'Crystal Blade',
|
||||||
|
description: 'A blade made of crystallized mana. Excellent for elemental enchantments.',
|
||||||
|
manaType: 'crystal',
|
||||||
|
equipmentTypeId: 'crystalBlade',
|
||||||
|
slot: 'mainHand',
|
||||||
|
materials: { manaCrystalDust: 8, crystalShard: 4, elementalCore: 2 },
|
||||||
|
manaCost: 500,
|
||||||
|
craftTime: 5,
|
||||||
|
rarity: 'rare',
|
||||||
|
gearTrait: '+20% Cast Speed, +15% Spell Damage',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'arcanistBlade',
|
||||||
|
name: 'Arcanist Blade',
|
||||||
|
description: 'A sword forged for battle mages. High capacity for powerful enchantments.',
|
||||||
|
manaType: 'metal',
|
||||||
|
equipmentTypeId: 'arcanistBlade',
|
||||||
|
slot: 'mainHand',
|
||||||
|
materials: { manaCrystalDust: 10, metalShard: 5, elementalCore: 3 },
|
||||||
|
manaCost: 600,
|
||||||
|
craftTime: 7,
|
||||||
|
rarity: 'epic',
|
||||||
|
gearTrait: '+15% Spell Damage, +15% Cast Speed, +10% Enchantment Power',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'voidBlade',
|
||||||
|
name: 'Void-Touched Blade',
|
||||||
|
description: 'A blade corrupted by void energy. Powerful but consumes more mana.',
|
||||||
|
manaType: 'crystal',
|
||||||
|
equipmentTypeId: 'voidBlade',
|
||||||
|
slot: 'mainHand',
|
||||||
|
materials: { manaCrystalDust: 9, darkCrystal: 3, voidEssence: 2, elementalCore: 2 },
|
||||||
|
manaCost: 550,
|
||||||
|
craftTime: 6,
|
||||||
|
rarity: 'epic',
|
||||||
|
gearTrait: '+25% Spell Damage, +10% Attack Speed',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'battleHelm',
|
||||||
|
name: 'Battle Helm',
|
||||||
|
description: 'A sturdy helm for battle mages. Balances protection with magical capacity.',
|
||||||
|
manaType: 'metal',
|
||||||
|
equipmentTypeId: 'battleHelm',
|
||||||
|
slot: 'head',
|
||||||
|
materials: { manaCrystalDust: 6, metalShard: 3, elementalCore: 1 },
|
||||||
|
manaCost: 350,
|
||||||
|
craftTime: 4,
|
||||||
|
rarity: 'rare',
|
||||||
|
gearTrait: '+10% Spell Damage, +15% Attack Speed',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'battleRobe',
|
||||||
|
name: 'Battle Robe',
|
||||||
|
description: 'A reinforced robe designed for combat mages. Durable without sacrificing capacity.',
|
||||||
|
manaType: 'sand',
|
||||||
|
equipmentTypeId: 'battleRobe',
|
||||||
|
slot: 'body',
|
||||||
|
materials: { manaCrystalDust: 8, sandShard: 3, elementalCore: 2 },
|
||||||
|
manaCost: 400,
|
||||||
|
craftTime: 5,
|
||||||
|
rarity: 'rare',
|
||||||
|
gearTrait: '+15% Cast Speed, +10% Evasion',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'battleBoots',
|
||||||
|
name: 'Battle Boots',
|
||||||
|
description: 'Sturdy boots for combat situations. Lightweight yet protective.',
|
||||||
|
manaType: 'sand',
|
||||||
|
equipmentTypeId: 'battleBoots',
|
||||||
|
slot: 'feet',
|
||||||
|
materials: { manaCrystalDust: 4, sandShard: 2 },
|
||||||
|
manaCost: 180,
|
||||||
|
craftTime: 3,
|
||||||
|
rarity: 'uncommon',
|
||||||
|
gearTrait: '+12% Cast Speed, +8% Attack Speed',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'combatGauntlets',
|
||||||
|
name: 'Combat Gauntlets',
|
||||||
|
description: 'Armored gauntlets for battle mages. Enhances both casting and melee.',
|
||||||
|
manaType: 'metal',
|
||||||
|
equipmentTypeId: 'combatGauntlets',
|
||||||
|
slot: 'hands',
|
||||||
|
materials: { manaCrystalDust: 5, metalShard: 2, elementalCore: 1 },
|
||||||
|
manaCost: 300,
|
||||||
|
craftTime: 3,
|
||||||
|
rarity: 'uncommon',
|
||||||
|
gearTrait: '+10% Attack Speed, +10% Cast Speed',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'runicShield',
|
||||||
|
name: 'Runic Shield',
|
||||||
|
description: 'A shield engraved with protective runes. Excellent for defensive enchantments.',
|
||||||
|
manaType: 'earth',
|
||||||
|
equipmentTypeId: 'runicShield',
|
||||||
|
slot: 'offHand',
|
||||||
|
materials: { manaCrystalDust: 8, earthShard: 4, elementalCore: 2 },
|
||||||
|
manaCost: 450,
|
||||||
|
craftTime: 5,
|
||||||
|
rarity: 'rare',
|
||||||
|
gearTrait: '+20% Defensive Enchantment Power, +25 Earth Mana Capacity',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'manaShield',
|
||||||
|
name: 'Mana Shield',
|
||||||
|
description: 'A crystalline shield that can store and reflect mana. Deadly in the right hands.',
|
||||||
|
manaType: 'crystal',
|
||||||
|
equipmentTypeId: 'manaShield',
|
||||||
|
slot: 'offHand',
|
||||||
|
materials: { manaCrystalDust: 10, crystalShard: 5, elementalCore: 2 },
|
||||||
|
manaCost: 550,
|
||||||
|
craftTime: 6,
|
||||||
|
rarity: 'epic',
|
||||||
|
gearTrait: '+15% Spell Damage, +15% Defensive Enchantment Power',
|
||||||
|
},
|
||||||
|
];
|
||||||
@@ -1,10 +1,15 @@
|
|||||||
// ─── Fabricator Recipes ──────────────────────────────────────────────────────
|
// ─── Fabricator Equipment Recipes ──────────────────────────────────────────────
|
||||||
// Crafting recipes for the Fabricator attunement.
|
// Core and elemental recipes. Branch recipes are in separate files.
|
||||||
// Each recipe is tied to a mana type the player has unlocked.
|
// Material recipes are in fabricator-material-recipes.ts.
|
||||||
|
|
||||||
|
import type { FabricatorRecipe } from './fabricator-recipe-types';
|
||||||
export type { FabricatorRecipe, MANA_TYPE_LABELS } from './fabricator-recipe-types';
|
export type { FabricatorRecipe, MANA_TYPE_LABELS } from './fabricator-recipe-types';
|
||||||
|
export { MATERIAL_RECIPES } from './fabricator-material-recipes';
|
||||||
|
export { WIZARD_BRANCH_RECIPES } from './fabricator-wizard-recipes';
|
||||||
|
export { PHYSICAL_BRANCH_RECIPES } from './fabricator-physical-recipes';
|
||||||
|
|
||||||
|
import { WIZARD_BRANCH_RECIPES } from './fabricator-wizard-recipes';
|
||||||
|
import { PHYSICAL_BRANCH_RECIPES } from './fabricator-physical-recipes';
|
||||||
|
|
||||||
export const FABRICATOR_RECIPES: FabricatorRecipe[] = [
|
export const FABRICATOR_RECIPES: FabricatorRecipe[] = [
|
||||||
// ─── Earth Gear (Compacted Earth — high defense) ──────────────────────
|
// ─── Earth Gear (Compacted Earth — high defense) ──────────────────────
|
||||||
@@ -171,187 +176,9 @@ export const FABRICATOR_RECIPES: FabricatorRecipe[] = [
|
|||||||
gearTrait: '+20% cast speed, +5% evasion',
|
gearTrait: '+20% cast speed, +5% evasion',
|
||||||
},
|
},
|
||||||
|
|
||||||
];
|
// ─── Branch recipes ───────────────────────────────────────────────────
|
||||||
|
...WIZARD_BRANCH_RECIPES,
|
||||||
// ─── Material Crafting Recipes ───────────────────────────────────────────────
|
...PHYSICAL_BRANCH_RECIPES,
|
||||||
|
|
||||||
export const MATERIAL_RECIPES: FabricatorRecipe[] = [
|
|
||||||
{
|
|
||||||
id: 'manaCrystal',
|
|
||||||
name: 'Mana Crystal',
|
|
||||||
description: 'Condense raw mana into a stable crystal. Used in all advanced crafting.',
|
|
||||||
manaType: 'raw',
|
|
||||||
equipmentTypeId: 'basicStaff',
|
|
||||||
slot: 'mainHand',
|
|
||||||
materials: {},
|
|
||||||
manaCost: 500,
|
|
||||||
craftTime: 1,
|
|
||||||
rarity: 'uncommon',
|
|
||||||
gearTrait: 'Produces 1 Mana Crystal',
|
|
||||||
recipeType: 'material',
|
|
||||||
resultMaterial: 'manaCrystal',
|
|
||||||
resultAmount: 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'manaCrystalDustCraft',
|
|
||||||
name: 'Mana Crystal Dust',
|
|
||||||
description: 'Grind a Mana Crystal into dust. Used as a base material.',
|
|
||||||
manaType: 'raw',
|
|
||||||
equipmentTypeId: 'basicStaff',
|
|
||||||
slot: 'mainHand',
|
|
||||||
materials: { manaCrystal: 1 },
|
|
||||||
manaCost: 10,
|
|
||||||
craftTime: 1,
|
|
||||||
rarity: 'common',
|
|
||||||
gearTrait: 'Produces 2 Mana Crystal Dust',
|
|
||||||
recipeType: 'material',
|
|
||||||
resultMaterial: 'manaCrystalDust',
|
|
||||||
resultAmount: 2,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'fireCrystal',
|
|
||||||
name: 'Fire Attuned Crystal',
|
|
||||||
description: 'Infuse a Mana Crystal with fire mana to attune it to the flame element.',
|
|
||||||
manaType: 'fire',
|
|
||||||
equipmentTypeId: 'basicStaff',
|
|
||||||
slot: 'mainHand',
|
|
||||||
materials: { manaCrystal: 1 },
|
|
||||||
manaCost: 100,
|
|
||||||
craftTime: 1,
|
|
||||||
rarity: 'rare',
|
|
||||||
gearTrait: 'Produces 1 Fire Attuned Crystal',
|
|
||||||
recipeType: 'material',
|
|
||||||
resultMaterial: 'fireCrystal',
|
|
||||||
resultAmount: 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'waterCrystal',
|
|
||||||
name: 'Water Attuned Crystal',
|
|
||||||
description: 'Infuse a Mana Crystal with water mana to attune it to the flow element.',
|
|
||||||
manaType: 'water',
|
|
||||||
equipmentTypeId: 'basicStaff',
|
|
||||||
slot: 'mainHand',
|
|
||||||
materials: { manaCrystal: 1 },
|
|
||||||
manaCost: 100,
|
|
||||||
craftTime: 1,
|
|
||||||
rarity: 'rare',
|
|
||||||
gearTrait: 'Produces 1 Water Attuned Crystal',
|
|
||||||
recipeType: 'material',
|
|
||||||
resultMaterial: 'waterCrystal',
|
|
||||||
resultAmount: 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'airCrystal',
|
|
||||||
name: 'Air Attuned Crystal',
|
|
||||||
description: 'Infuse a Mana Crystal with air mana to attune it to the wind element.',
|
|
||||||
manaType: 'air',
|
|
||||||
equipmentTypeId: 'basicStaff',
|
|
||||||
slot: 'mainHand',
|
|
||||||
materials: { manaCrystal: 1 },
|
|
||||||
manaCost: 100,
|
|
||||||
craftTime: 1,
|
|
||||||
rarity: 'rare',
|
|
||||||
gearTrait: 'Produces 1 Air Attuned Crystal',
|
|
||||||
recipeType: 'material',
|
|
||||||
resultMaterial: 'airCrystal',
|
|
||||||
resultAmount: 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'earthCrystal',
|
|
||||||
name: 'Earth Attuned Crystal',
|
|
||||||
description: 'Infuse a Mana Crystal with earth mana to attune it to the stone element.',
|
|
||||||
manaType: 'earth',
|
|
||||||
equipmentTypeId: 'basicStaff',
|
|
||||||
slot: 'mainHand',
|
|
||||||
materials: { manaCrystal: 1 },
|
|
||||||
manaCost: 100,
|
|
||||||
craftTime: 1,
|
|
||||||
rarity: 'rare',
|
|
||||||
gearTrait: 'Produces 1 Earth Attuned Crystal',
|
|
||||||
recipeType: 'material',
|
|
||||||
resultMaterial: 'earthCrystal',
|
|
||||||
resultAmount: 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'lightCrystal',
|
|
||||||
name: 'Light Attuned Crystal',
|
|
||||||
description: 'Infuse a Mana Crystal with light mana to attune it to the radiant element.',
|
|
||||||
manaType: 'light',
|
|
||||||
equipmentTypeId: 'basicStaff',
|
|
||||||
slot: 'mainHand',
|
|
||||||
materials: { manaCrystal: 1 },
|
|
||||||
manaCost: 100,
|
|
||||||
craftTime: 1,
|
|
||||||
rarity: 'rare',
|
|
||||||
gearTrait: 'Produces 1 Light Attuned Crystal',
|
|
||||||
recipeType: 'material',
|
|
||||||
resultMaterial: 'lightCrystal',
|
|
||||||
resultAmount: 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'darkCrystal',
|
|
||||||
name: 'Dark Attuned Crystal',
|
|
||||||
description: 'Infuse a Mana Crystal with dark mana to attune it to the shadow element.',
|
|
||||||
manaType: 'dark',
|
|
||||||
equipmentTypeId: 'basicStaff',
|
|
||||||
slot: 'mainHand',
|
|
||||||
materials: { manaCrystal: 1 },
|
|
||||||
manaCost: 100,
|
|
||||||
craftTime: 1,
|
|
||||||
rarity: 'rare',
|
|
||||||
gearTrait: 'Produces 1 Dark Attuned Crystal',
|
|
||||||
recipeType: 'material',
|
|
||||||
resultMaterial: 'darkCrystal',
|
|
||||||
resultAmount: 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'metalCrystal',
|
|
||||||
name: 'Metal Attuned Crystal',
|
|
||||||
description: 'Infuse a Mana Crystal with metal mana to attune it to the alloy element.',
|
|
||||||
manaType: 'metal',
|
|
||||||
equipmentTypeId: 'basicStaff',
|
|
||||||
slot: 'mainHand',
|
|
||||||
materials: { manaCrystal: 1 },
|
|
||||||
manaCost: 100,
|
|
||||||
craftTime: 1,
|
|
||||||
rarity: 'rare',
|
|
||||||
gearTrait: 'Produces 1 Metal Attuned Crystal',
|
|
||||||
recipeType: 'material',
|
|
||||||
resultMaterial: 'metalCrystal',
|
|
||||||
resultAmount: 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'crystalCrystal',
|
|
||||||
name: 'Crystal Attuned Crystal',
|
|
||||||
description: 'Infuse a Mana Crystal with crystal mana to attune it to the prismatic element.',
|
|
||||||
manaType: 'crystal',
|
|
||||||
equipmentTypeId: 'basicStaff',
|
|
||||||
slot: 'mainHand',
|
|
||||||
materials: { manaCrystal: 1 },
|
|
||||||
manaCost: 100,
|
|
||||||
craftTime: 1,
|
|
||||||
rarity: 'epic',
|
|
||||||
gearTrait: 'Produces 1 Crystal Attuned Crystal',
|
|
||||||
recipeType: 'material',
|
|
||||||
resultMaterial: 'crystalCrystal',
|
|
||||||
resultAmount: 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'elementalCore',
|
|
||||||
name: 'Elemental Core',
|
|
||||||
description: 'Combine mana crystals and all four base elements into a powerful core.',
|
|
||||||
manaType: 'raw',
|
|
||||||
equipmentTypeId: 'basicStaff',
|
|
||||||
slot: 'mainHand',
|
|
||||||
materials: { manaCrystal: 10 },
|
|
||||||
manaCost: 100,
|
|
||||||
craftTime: 10,
|
|
||||||
rarity: 'epic',
|
|
||||||
gearTrait: 'Produces 1 Elemental Core',
|
|
||||||
recipeType: 'material',
|
|
||||||
resultMaterial: 'elementalCore',
|
|
||||||
resultAmount: 1,
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
|
|
||||||
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
||||||
@@ -361,7 +188,7 @@ export function getRecipesByManaType(manaType: string): FabricatorRecipe[] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function getRecipeById(id: string): FabricatorRecipe | undefined {
|
export function getRecipeById(id: string): FabricatorRecipe | undefined {
|
||||||
return FABRICATOR_RECIPES.find(r => r.id === id) ?? MATERIAL_RECIPES.find(r => r.id === id);
|
return FABRICATOR_RECIPES.find(r => r.id === id);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function canCraftRecipe(
|
export function canCraftRecipe(
|
||||||
@@ -381,9 +208,6 @@ export function canCraftRecipe(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If manaType is provided, manaAmount is already the correct pool value.
|
|
||||||
// Otherwise fall back to treating manaAmount as raw mana (backward compat).
|
|
||||||
const effectiveManaType = manaType ?? recipe.manaType;
|
|
||||||
const missingMana = Math.max(0, recipe.manaCost - manaAmount);
|
const missingMana = Math.max(0, recipe.manaCost - manaAmount);
|
||||||
if (missingMana > 0) {
|
if (missingMana > 0) {
|
||||||
canCraft = false;
|
canCraft = false;
|
||||||
|
|||||||
@@ -0,0 +1,99 @@
|
|||||||
|
// ─── Wizard Branch Recipes ─────────────────────────────────────────────────────
|
||||||
|
// Staff, crown/circlet, and catalyst gear focused on mana capacity, regen,
|
||||||
|
// enchantment power, and spell casting.
|
||||||
|
|
||||||
|
import type { FabricatorRecipe } from './fabricator-recipe-types';
|
||||||
|
|
||||||
|
export const WIZARD_BRANCH_RECIPES: FabricatorRecipe[] = [
|
||||||
|
{
|
||||||
|
id: 'oakStaff',
|
||||||
|
name: 'Oak Staff',
|
||||||
|
description: 'A sturdy oak staff with decent mana capacity. Reliable for extended casting.',
|
||||||
|
manaType: 'earth',
|
||||||
|
equipmentTypeId: 'oakStaff',
|
||||||
|
slot: 'mainHand',
|
||||||
|
materials: { manaCrystalDust: 5, earthShard: 2 },
|
||||||
|
manaCost: 200,
|
||||||
|
craftTime: 3,
|
||||||
|
rarity: 'uncommon',
|
||||||
|
gearTrait: '+15% Mana Regen, +20 Earth Mana Capacity',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'arcanistStaff',
|
||||||
|
name: 'Arcanist Staff',
|
||||||
|
description: 'A staff designed for advanced spellcasters. High capacity for complex enchantments.',
|
||||||
|
manaType: 'crystal',
|
||||||
|
equipmentTypeId: 'arcanistStaff',
|
||||||
|
slot: 'mainHand',
|
||||||
|
materials: { manaCrystalDust: 12, crystalShard: 6, elementalCore: 3 },
|
||||||
|
manaCost: 700,
|
||||||
|
craftTime: 8,
|
||||||
|
rarity: 'epic',
|
||||||
|
gearTrait: '+30% Enchantment Capacity, +20% Mana Regen',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'battlestaff',
|
||||||
|
name: 'Battlestaff',
|
||||||
|
description: 'A reinforced staff suitable for both casting and combat. Balanced and sturdy.',
|
||||||
|
manaType: 'metal',
|
||||||
|
equipmentTypeId: 'battlestaff',
|
||||||
|
slot: 'mainHand',
|
||||||
|
materials: { manaCrystalDust: 8, metalShard: 4, elementalCore: 2 },
|
||||||
|
manaCost: 500,
|
||||||
|
craftTime: 6,
|
||||||
|
rarity: 'rare',
|
||||||
|
gearTrait: '+10% Enchantment Power, +10% Cast Speed',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'arcanistCirclet',
|
||||||
|
name: 'Arcanist Circlet',
|
||||||
|
description: 'A silver circlet worn by accomplished arcanists. Sharpens mental focus.',
|
||||||
|
manaType: 'crystal',
|
||||||
|
equipmentTypeId: 'arcanistCirclet',
|
||||||
|
slot: 'head',
|
||||||
|
materials: { manaCrystalDust: 6, crystalShard: 2, lightCrystal: 1 },
|
||||||
|
manaCost: 300,
|
||||||
|
craftTime: 4,
|
||||||
|
rarity: 'rare',
|
||||||
|
gearTrait: '+20% Enchantment Power, +15 Light Mana Capacity',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'arcanistRobe',
|
||||||
|
name: 'Arcanist Robe',
|
||||||
|
description: 'An ornate robe for master arcanists. High capacity for body armor.',
|
||||||
|
manaType: 'crystal',
|
||||||
|
equipmentTypeId: 'arcanistRobe',
|
||||||
|
slot: 'body',
|
||||||
|
materials: { manaCrystalDust: 14, crystalShard: 7, elementalCore: 3 },
|
||||||
|
manaCost: 800,
|
||||||
|
craftTime: 8,
|
||||||
|
rarity: 'epic',
|
||||||
|
gearTrait: '+35% Enchantment Capacity, +10% all Mana Regen',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'voidCatalyst',
|
||||||
|
name: 'Void Catalyst',
|
||||||
|
description: 'A rare catalyst touched by void energy. High capacity but volatile.',
|
||||||
|
manaType: 'crystal',
|
||||||
|
equipmentTypeId: 'voidCatalyst',
|
||||||
|
slot: 'mainHand',
|
||||||
|
materials: { manaCrystalDust: 10, darkCrystal: 3, voidEssence: 2, elementalCore: 2 },
|
||||||
|
manaCost: 600,
|
||||||
|
craftTime: 7,
|
||||||
|
rarity: 'epic',
|
||||||
|
gearTrait: '+30% Dark Enchantment Power, +15% Spell Damage',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'arcanistPendant',
|
||||||
|
name: 'Arcanist Pendant',
|
||||||
|
description: 'A powerful pendant worn by master arcanists. Amplifies all enchantment effects.',
|
||||||
|
manaType: 'crystal',
|
||||||
|
equipmentTypeId: 'arcanistPendant',
|
||||||
|
slot: 'accessory1',
|
||||||
|
materials: { manaCrystalDust: 8, crystalShard: 4, elementalCore: 2 },
|
||||||
|
manaCost: 500,
|
||||||
|
craftTime: 5,
|
||||||
|
rarity: 'epic',
|
||||||
|
gearTrait: '+15% all enchantment effects, +10% Mana Regen',
|
||||||
|
},
|
||||||
|
];
|
||||||
Reference in New Issue
Block a user