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
+30 -2
View File
@@ -6,6 +6,7 @@ import type { ElementState } from './types';
import type { FabricatorRecipe } from './data/fabricator-recipes';
import { FABRICATOR_RECIPES } from './data/fabricator-recipes';
import { useManaStore } from './stores/manaStore';
import { computeDisciplineEffects } from './effects/discipline-effects';
// ─── Lookup ───────────────────────────────────────────────────────────────────
@@ -13,6 +14,27 @@ export function getFabricatorRecipe(recipeId: string): FabricatorRecipe | undefi
return FABRICATOR_RECIPES.find(r => r.id === recipeId);
}
// ─── Crafting Cost Reduction ──────────────────────────────────────────────────
/**
* Returns the current crafting cost reduction percentage from discipline effects.
* Spec: actualCost = baseCost × (1 - craftingCostReduction / 100), capped at 75%.
*/
export function getCraftingCostReduction(): number {
const disciplineEffects = computeDisciplineEffects();
const raw = disciplineEffects.bonuses.craftingCostReduction || 0;
return Math.min(75, raw);
}
/**
* Apply cost reduction to a material amount, rounding up to ensure
* the player never pays more than the displayed reduced cost.
*/
export function applyCostReduction(baseAmount: number, costReduction: number): number {
if (costReduction <= 0) return baseAmount;
return Math.max(1, Math.ceil(baseAmount * (1 - costReduction / 100)));
}
// ─── Cost Check ───────────────────────────────────────────────────────────────
export interface FabricatorCostCheck {
@@ -26,11 +48,14 @@ export function checkFabricatorCosts(
materials: Record<string, number>,
rawMana: number,
elements: Record<string, ElementState>,
costReduction?: number,
): FabricatorCostCheck {
const reduction = costReduction ?? getCraftingCostReduction();
const missingMaterials: Record<string, number> = {};
let canCraft = true;
for (const [matId, required] of Object.entries(recipe.materials)) {
for (const [matId, rawRequired] of Object.entries(recipe.materials)) {
const required = applyCostReduction(rawRequired, reduction);
const available = materials[matId] || 0;
if (available < required) {
missingMaterials[matId] = required - available;
@@ -116,9 +141,12 @@ export function refundFabricatorMana(
export function deductMaterials(
recipe: FabricatorRecipe,
materials: Record<string, number>,
costReduction?: number,
): Record<string, number> {
const reduction = costReduction ?? getCraftingCostReduction();
const newMaterials = { ...materials };
for (const [matId, amount] of Object.entries(recipe.materials)) {
for (const [matId, rawAmount] of Object.entries(recipe.materials)) {
const amount = applyCostReduction(rawAmount, reduction);
newMaterials[matId] = (newMaterials[matId] || 0) - amount;
if (newMaterials[matId] <= 0) delete newMaterials[matId];
}