fix: apply Crafting Efficiency cost reduction to all fabrication paths
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m18s

- Add getCraftingCostReduction() and applyCostReduction() helpers in crafting-fabricator.ts
- Apply cost reduction in deductMaterials() and checkFabricatorCosts()
- Apply cost reduction in startFabricatorCrafting() and cancelEquipmentCrafting() pipeline
- Update canCraftRecipe() in fabricator-recipes.ts to accept costReduction param
- Update FabricatorSubTab and MaterialRecipeCard UIs to display discounted costs
- Spec formula: actualCost = ceil(baseCost × (1 - craftingCostReduction / 100))

Fixes #316
This commit is contained in:
2026-06-07 23:15:55 +02:00
parent a11ea065eb
commit 0e1e506213
7 changed files with 77 additions and 16 deletions
@@ -15,6 +15,7 @@ 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';
import { DebugName } from '@/components/game/debug/debug-context';
import { getCraftingCostReduction, applyCostReduction } from '@/lib/game/crafting-fabricator';
const BRANCH_RECIPE_IDS = new Set([
'oakStaff', 'arcanistStaff', 'battlestaff', 'arcanistCirclet', 'arcanistRobe',
@@ -40,6 +41,7 @@ function RecipeCard({
elementalMana,
onCraft,
isCrafting,
costReduction,
}: {
recipe: FabricatorRecipe;
materials: Record<string, number>;
@@ -47,6 +49,7 @@ function RecipeCard({
elementalMana: Record<string, { current: number; max: number; unlocked: boolean }>;
onCraft: (recipe: FabricatorRecipe) => void;
isCrafting: boolean;
costReduction: number;
}) {
const pool = recipe.manaType === 'raw'
? rawMana
@@ -56,6 +59,7 @@ function RecipeCard({
materials,
pool,
recipe.manaType,
costReduction,
);
const rarityStyle = LOOT_RARITY_COLORS[recipe.rarity];
@@ -82,17 +86,26 @@ function RecipeCard({
<Separator className="bg-gray-700 my-2" />
<div className="text-xs space-y-1">
<div className="text-gray-500">Materials:</div>
{Object.entries(recipe.materials).map(([matId, amount]) => {
<div className="text-gray-500 flex justify-between">
<span>Materials:</span>
{costReduction > 0 && (
<span className="text-cyan-400">-{costReduction}% cost</span>
)}
</div>
{Object.entries(recipe.materials).map(([matId, rawAmount]) => {
const reducedAmount = applyCostReduction(rawAmount, costReduction);
const available = materials[matId] || 0;
const hasEnough = available >= reducedAmount;
const matDrop = LOOT_DROPS[matId];
const hasEnough = available >= amount;
return (
<div key={matId} className="flex justify-between">
<span>{matDrop?.name ?? matId}</span>
<span className={hasEnough ? 'text-green-400' : 'text-red-400'}>
{available} / {amount}
{available} / {reducedAmount}
{costReduction > 0 && rawAmount !== reducedAmount && (
<span className="text-gray-500 ml-1">(was {rawAmount})</span>
)}
</span>
</div>
);
@@ -156,6 +169,7 @@ export function FabricatorSubTab() {
}, [selectedManaType, branchFilter, unlockedRecipes]);
const isCrafting = equipmentCraftingProgress !== null;
const costReduction = getCraftingCostReduction();
const materialRecipes = useMemo(() => MATERIAL_RECIPES, []);
@@ -300,6 +314,7 @@ export function FabricatorSubTab() {
elementalMana={elements}
onCraft={handleCraft}
isCrafting={isCrafting}
costReduction={costReduction}
/>
))
)
@@ -318,6 +333,7 @@ export function FabricatorSubTab() {
rawMana={rawMana}
elementalMana={elements}
onCraft={handleCraft}
costReduction={costReduction}
/>
))
)