fix: fabricator recipes now use correct elemental mana type
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m37s

- fabricator-recipes.ts: add optional manaType param to canCraftRecipe for clarity
- FabricatorSubTab.tsx: read elemental mana from store based on recipe manaType instead of always using rawMana
- craftingStore.ts: add startFabricatorCrafting action that deducts correct mana type
- craftingStore.types.ts: add startFabricatorCrafting to CraftingActions interface
- crafting-fabricator.ts: new helper file to keep craftingStore.ts under 400 lines

Fixes #155
This commit is contained in:
2026-05-27 11:06:24 +02:00
parent 707a1eef31
commit 64b472572b
8 changed files with 198 additions and 27 deletions
+141
View File
@@ -0,0 +1,141 @@
// ─── Fabricator Crafting Helpers ──────────────────────────────────────────────
// Separate file to avoid exceeding the 400-line limit in craftingStore.ts.
import type { EquipmentCraftingProgress } from './types';
import type { FabricatorRecipe } from './data/fabricator-recipes';
import { FABRICATOR_RECIPES } from './data/fabricator-recipes';
// ─── Lookup ───────────────────────────────────────────────────────────────────
export function getFabricatorRecipe(recipeId: string): FabricatorRecipe | undefined {
return FABRICATOR_RECIPES.find(r => r.id === recipeId);
}
// ─── Cost Check ───────────────────────────────────────────────────────────────
export interface FabricatorCostCheck {
canCraft: boolean;
missingMana: number;
missingMaterials: Record<string, number>;
}
export function checkFabricatorCosts(
recipe: FabricatorRecipe,
materials: Record<string, number>,
rawMana: number,
elements: Record<string, { current: number; max: number; unlocked: boolean }>,
): FabricatorCostCheck {
const missingMaterials: Record<string, number> = {};
let canCraft = true;
for (const [matId, required] of Object.entries(recipe.materials)) {
const available = materials[matId] || 0;
if (available < required) {
missingMaterials[matId] = required - available;
canCraft = false;
}
}
const manaAvailable = recipe.manaType === 'raw'
? rawMana
: (elements[recipe.manaType]?.current ?? 0);
const missingMana = Math.max(0, recipe.manaCost - manaAvailable);
if (missingMana > 0) canCraft = false;
return { canCraft, missingMana, missingMaterials };
}
// ─── Mana Deduction ───────────────────────────────────────────────────────────
export interface ManaDeduction {
rawMana: number;
elements: Record<string, { current: number; max: number; unlocked: boolean }>;
}
export function deductFabricatorMana(
recipe: FabricatorRecipe,
rawMana: number,
elements: Record<string, { current: number; max: number; unlocked: boolean }>,
): ManaDeduction | null {
if (recipe.manaType === 'raw') {
if (rawMana < recipe.manaCost) return null;
return { rawMana: rawMana - recipe.manaCost, elements };
}
const elemState = elements[recipe.manaType];
if (!elemState || !elemState.unlocked || elemState.current < recipe.manaCost) return null;
return {
rawMana,
elements: {
...elements,
[recipe.manaType]: {
...elements[recipe.manaType],
current: elements[recipe.manaType].current - recipe.manaCost,
},
},
};
}
// ─── Mana Refund (on cancel) ──────────────────────────────────────────────────
export interface ManaRefund {
rawMana: number;
elements: Record<string, { current: number; max: number; unlocked: boolean }>;
}
export function refundFabricatorMana(
recipe: FabricatorRecipe,
refundAmount: number,
rawMana: number,
elements: Record<string, { current: number; max: number; unlocked: boolean }>,
): ManaRefund {
if (recipe.manaType === 'raw') {
return { rawMana: rawMana + refundAmount, elements };
}
return {
rawMana,
elements: {
...elements,
[recipe.manaType]: {
...elements[recipe.manaType],
current: Math.min(
elements[recipe.manaType].max,
elements[recipe.manaType].current + refundAmount,
),
},
},
};
}
// ─── Material Deduction ───────────────────────────────────────────────────────
export function deductMaterials(
recipe: FabricatorRecipe,
materials: Record<string, number>,
): Record<string, number> {
const newMaterials = { ...materials };
for (const [matId, amount] of Object.entries(recipe.materials)) {
newMaterials[matId] = (newMaterials[matId] || 0) - amount;
if (newMaterials[matId] <= 0) delete newMaterials[matId];
}
return newMaterials;
}
// ─── Progress Init ────────────────────────────────────────────────────────────
export function makeFabricatorProgress(
recipeId: string,
equipmentTypeId: string,
craftTime: number,
manaCost: number,
): EquipmentCraftingProgress {
return {
blueprintId: `fabricator-${recipeId}`,
equipmentTypeId,
progress: 0,
required: craftTime,
manaSpent: manaCost,
};
}