fix: apply Crafting Efficiency cost reduction to all fabrication paths
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m18s
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:
@@ -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];
|
||||
}
|
||||
|
||||
@@ -236,11 +236,16 @@ export function canCraftRecipe(
|
||||
materials: Record<string, number>,
|
||||
manaAmount: number,
|
||||
manaType?: string,
|
||||
costReduction?: number,
|
||||
): { canCraft: boolean; missingMaterials: Record<string, number>; missingMana: number } {
|
||||
const reduction = costReduction ?? 0;
|
||||
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 = reduction > 0
|
||||
? Math.max(1, Math.ceil(rawRequired * (1 - reduction / 100)))
|
||||
: rawRequired;
|
||||
const available = materials[matId] || 0;
|
||||
if (available < required) {
|
||||
missingMaterials[matId] = required - available;
|
||||
|
||||
@@ -10,6 +10,8 @@ import {
|
||||
deductMaterials,
|
||||
makeFabricatorProgress,
|
||||
refundFabricatorMana,
|
||||
getCraftingCostReduction,
|
||||
applyCostReduction,
|
||||
} from '../../crafting-fabricator';
|
||||
import { useManaStore } from '../manaStore';
|
||||
import { useCombatStore } from '../combatStore';
|
||||
@@ -72,11 +74,13 @@ export function cancelEquipmentCrafting(get: GetFn, set: SetFn): void {
|
||||
const refunded = refundFabricatorMana(recipe, manaRefund, rawMana, elements);
|
||||
useManaStore.setState({ rawMana: refunded.rawMana, elements: refunded.elements });
|
||||
|
||||
// Refund materials
|
||||
// Refund materials — use reduced amounts (what player actually paid)
|
||||
const reduction = getCraftingCostReduction();
|
||||
const currentMaterials = get().lootInventory.materials;
|
||||
const refundedMaterials = { ...currentMaterials };
|
||||
for (const [matId, amount] of Object.entries(recipe.materials)) {
|
||||
const refundAmount = Math.floor(amount * remainingFraction);
|
||||
for (const [matId, rawAmount] of Object.entries(recipe.materials)) {
|
||||
const reducedAmount = applyCostReduction(rawAmount, reduction);
|
||||
const refundAmount = Math.floor(reducedAmount * remainingFraction);
|
||||
if (refundAmount > 0) {
|
||||
refundedMaterials[matId] = (refundedMaterials[matId] || 0) + refundAmount;
|
||||
}
|
||||
@@ -132,6 +136,7 @@ export function startFabricatorCrafting(recipeId: string, get: GetFn, set: SetFn
|
||||
const deducted = deductFabricatorMana(recipe, rawMana, elements);
|
||||
if (!deducted) return false;
|
||||
|
||||
// deductMaterials already applies cost reduction internally via getCraftingCostReduction()
|
||||
const newMaterials = deductMaterials(recipe, state.lootInventory.materials);
|
||||
const progress = makeFabricatorProgress(recipeId, recipe.equipmentTypeId, recipe.craftTime, recipe.manaCost);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user