Add equipment crafting system
- Add crafting-recipes.ts with blueprint definitions and material requirements - Update crafting-slice.ts with equipment crafting functions - Add EquipmentCraftingProgress type and state - Update CraftingTab.tsx with new Craft tab for equipment crafting - Add material deletion functionality - Update store.ts with equipment crafting methods - Update page.tsx to pass new props to CraftingTab Features: - Players can craft equipment from discovered blueprints - Crafting requires materials and mana - Materials are obtained from loot drops - New Craft tab in the crafting interface - Shows blueprint details and material requirements
This commit is contained in:
@@ -1,9 +1,10 @@
|
||||
// ─── Crafting Store Slice ─────────────────────────────────────────────────────────
|
||||
// Handles equipment and enchantment system: design, prepare, apply stages
|
||||
|
||||
import type { GameState, EquipmentInstance, AppliedEnchantment, EnchantmentDesign, DesignEffect, EquipmentSlot } from './types';
|
||||
import type { GameState, EquipmentInstance, AppliedEnchantment, EnchantmentDesign, DesignEffect, EquipmentSlot, EquipmentCraftingProgress, LootInventory } from './types';
|
||||
import { EQUIPMENT_TYPES, type EquipmentCategory } from './data/equipment';
|
||||
import { ENCHANTMENT_EFFECTS, calculateEffectCapacityCost } from './data/enchantment-effects';
|
||||
import { CRAFTING_RECIPES, canCraftRecipe, type CraftingRecipe } from './data/crafting-recipes';
|
||||
import { SPELLS_DEF } from './constants';
|
||||
|
||||
// ─── Helper Functions ─────────────────────────────────────────────────────────
|
||||
@@ -88,6 +89,11 @@ export interface CraftingActions {
|
||||
// Disenchanting
|
||||
disenchantEquipment: (instanceId: string) => void;
|
||||
|
||||
// Equipment Crafting (from blueprints)
|
||||
startCraftingEquipment: (blueprintId: string) => boolean;
|
||||
cancelEquipmentCrafting: () => void;
|
||||
deleteMaterial: (materialId: string, amount: number) => void;
|
||||
|
||||
// Computed getters
|
||||
getEquipmentSpells: () => string[];
|
||||
getEquipmentEffects: () => Record<string, number>;
|
||||
@@ -502,6 +508,97 @@ export function createCraftingSlice(
|
||||
if (!instance) return 0;
|
||||
return instance.totalCapacity - instance.usedCapacity;
|
||||
},
|
||||
|
||||
// ─── Equipment Crafting (from Blueprints) ───────────────────────────────────
|
||||
|
||||
startCraftingEquipment: (blueprintId: string) => {
|
||||
const state = get();
|
||||
const recipe = CRAFTING_RECIPES[blueprintId];
|
||||
if (!recipe) return false;
|
||||
|
||||
// Check if player has the blueprint
|
||||
if (!state.lootInventory.blueprints.includes(blueprintId)) return false;
|
||||
|
||||
// Check materials
|
||||
const { canCraft, missingMaterials, missingMana } = canCraftRecipe(
|
||||
recipe,
|
||||
state.lootInventory.materials,
|
||||
state.rawMana
|
||||
);
|
||||
|
||||
if (!canCraft) return false;
|
||||
|
||||
// Deduct materials
|
||||
const newMaterials = { ...state.lootInventory.materials };
|
||||
for (const [matId, amount] of Object.entries(recipe.materials)) {
|
||||
newMaterials[matId] = (newMaterials[matId] || 0) - amount;
|
||||
if (newMaterials[matId] <= 0) {
|
||||
delete newMaterials[matId];
|
||||
}
|
||||
}
|
||||
|
||||
// Start crafting progress
|
||||
set((state) => ({
|
||||
lootInventory: {
|
||||
...state.lootInventory,
|
||||
materials: newMaterials,
|
||||
},
|
||||
rawMana: state.rawMana - recipe.manaCost,
|
||||
currentAction: 'craft',
|
||||
equipmentCraftingProgress: {
|
||||
blueprintId,
|
||||
equipmentTypeId: recipe.equipmentTypeId,
|
||||
progress: 0,
|
||||
required: recipe.craftTime,
|
||||
manaSpent: recipe.manaCost,
|
||||
},
|
||||
}));
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
cancelEquipmentCrafting: () => {
|
||||
set((state) => {
|
||||
const progress = state.equipmentCraftingProgress;
|
||||
if (!progress) return {};
|
||||
|
||||
const recipe = CRAFTING_RECIPES[progress.blueprintId];
|
||||
if (!recipe) return { currentAction: 'meditate', equipmentCraftingProgress: null };
|
||||
|
||||
// Refund 50% of mana
|
||||
const manaRefund = Math.floor(progress.manaSpent * 0.5);
|
||||
|
||||
return {
|
||||
currentAction: 'meditate',
|
||||
equipmentCraftingProgress: null,
|
||||
rawMana: state.rawMana + manaRefund,
|
||||
log: [`🚫 Equipment crafting cancelled. Refunded ${manaRefund} mana.`, ...state.log.slice(0, 49)],
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
deleteMaterial: (materialId: string, amount: number) => {
|
||||
set((state) => {
|
||||
const currentAmount = state.lootInventory.materials[materialId] || 0;
|
||||
const newAmount = Math.max(0, currentAmount - amount);
|
||||
const newMaterials = { ...state.lootInventory.materials };
|
||||
|
||||
if (newAmount <= 0) {
|
||||
delete newMaterials[materialId];
|
||||
} else {
|
||||
newMaterials[materialId] = newAmount;
|
||||
}
|
||||
|
||||
const dropName = materialId; // Could look up in LOOT_DROPS for proper name
|
||||
return {
|
||||
lootInventory: {
|
||||
...state.lootInventory,
|
||||
materials: newMaterials,
|
||||
},
|
||||
log: [`🗑️ Deleted ${amount}x ${dropName}.`, ...state.log.slice(0, 49)],
|
||||
};
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -620,6 +717,59 @@ export function processCraftingTick(
|
||||
}
|
||||
}
|
||||
|
||||
// Process equipment crafting progress
|
||||
if (state.currentAction === 'craft' && state.equipmentCraftingProgress) {
|
||||
const craft = state.equipmentCraftingProgress;
|
||||
const progress = craft.progress + 0.04; // HOURS_PER_TICK
|
||||
|
||||
if (progress >= craft.required) {
|
||||
// Crafting complete - create the equipment!
|
||||
const recipe = CRAFTING_RECIPES[craft.blueprintId];
|
||||
const equipType = recipe ? EQUIPMENT_TYPES[recipe.equipmentTypeId] : null;
|
||||
|
||||
if (recipe && equipType) {
|
||||
const instanceId = generateInstanceId();
|
||||
const newInstance: EquipmentInstance = {
|
||||
instanceId,
|
||||
typeId: recipe.equipmentTypeId,
|
||||
name: recipe.name,
|
||||
enchantments: [],
|
||||
usedCapacity: 0,
|
||||
totalCapacity: equipType.baseCapacity,
|
||||
rarity: recipe.rarity,
|
||||
quality: 100,
|
||||
};
|
||||
|
||||
updates = {
|
||||
...updates,
|
||||
equipmentCraftingProgress: null,
|
||||
currentAction: 'meditate',
|
||||
equipmentInstances: {
|
||||
...state.equipmentInstances,
|
||||
[instanceId]: newInstance,
|
||||
},
|
||||
totalCraftsCompleted: (state.totalCraftsCompleted || 0) + 1,
|
||||
log: [`🔨 Crafted ${recipe.name}!`, ...log],
|
||||
};
|
||||
} else {
|
||||
updates = {
|
||||
...updates,
|
||||
equipmentCraftingProgress: null,
|
||||
currentAction: 'meditate',
|
||||
log: ['⚠️ Crafting failed - invalid recipe!', ...log],
|
||||
};
|
||||
}
|
||||
} else {
|
||||
updates = {
|
||||
...updates,
|
||||
equipmentCraftingProgress: {
|
||||
...craft,
|
||||
progress,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return updates;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user