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:
2026-03-26 11:04:50 +00:00
parent 4f4cbeb527
commit 1d2dce75cc
6 changed files with 734 additions and 5 deletions

View File

@@ -47,7 +47,8 @@ import {
type FamiliarBonuses,
DEFAULT_FAMILIAR_BONUSES,
} from './familiar-slice';
import { rollLootDrops } from './data/loot-drops';
import { rollLootDrops, LOOT_DROPS } from './data/loot-drops';
import { CRAFTING_RECIPES, canCraftRecipe } from './data/crafting-recipes';
// Default empty effects for when effects aren't provided
const DEFAULT_EFFECTS: ComputedEffects = {
@@ -560,6 +561,9 @@ function makeInitial(overrides: Partial<GameState> = {}): GameState {
blueprints: [],
},
lootDropsToday: 0,
// Equipment Crafting Progress
equipmentCraftingProgress: null,
// Achievements
achievements: {
@@ -1912,6 +1916,98 @@ export const useGameStore = create<GameStore>()(
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 and mana
const { canCraft } = 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,
},
log: [`🔨 Started crafting ${recipe.name}...`, ...state.log.slice(0, 49)],
}));
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: [`🚫 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 = LOOT_DROPS[materialId]?.name || materialId;
return {
lootInventory: {
...state.lootInventory,
materials: newMaterials,
},
log: [`🗑️ Deleted ${amount}x ${dropName}.`, ...state.log.slice(0, 49)],
};
});
},
// ─── Floor Navigation ────────────────────────────────────────────────────────
setClimbDirection: (direction: 'up' | 'down') => {