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:
@@ -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}
|
||||
/>
|
||||
))
|
||||
)
|
||||
|
||||
@@ -8,6 +8,7 @@ import { canCraftRecipe } from '@/lib/game/data/fabricator-recipes';
|
||||
import { MANA_TYPE_LABELS } from '@/lib/game/data/fabricator-recipe-types';
|
||||
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';
|
||||
|
||||
interface MaterialRecipeCardProps {
|
||||
recipe: FabricatorRecipe;
|
||||
@@ -15,6 +16,7 @@ interface MaterialRecipeCardProps {
|
||||
rawMana: number;
|
||||
elementalMana: Record<string, { current: number; max: number; unlocked: boolean }>;
|
||||
onCraft: (recipe: FabricatorRecipe) => void;
|
||||
costReduction?: number;
|
||||
}
|
||||
|
||||
export function MaterialRecipeCard({
|
||||
@@ -23,11 +25,12 @@ export function MaterialRecipeCard({
|
||||
rawMana,
|
||||
elementalMana,
|
||||
onCraft,
|
||||
costReduction = 0,
|
||||
}: MaterialRecipeCardProps) {
|
||||
const pool = recipe.manaType === 'raw'
|
||||
? rawMana
|
||||
: (elementalMana[recipe.manaType]?.current ?? 0);
|
||||
const { canCraft } = canCraftRecipe(recipe, materials, pool, recipe.manaType);
|
||||
const { canCraft } = canCraftRecipe(recipe, materials, pool, recipe.manaType, costReduction);
|
||||
const resultDrop = recipe.resultMaterial ? LOOT_DROPS[recipe.resultMaterial] : null;
|
||||
const resultRarity = resultDrop ? LOOT_RARITY_COLORS[resultDrop.rarity] : null;
|
||||
|
||||
@@ -57,16 +60,20 @@ export function MaterialRecipeCard({
|
||||
{Object.keys(recipe.materials).length > 0 && (
|
||||
<>
|
||||
<div className="text-gray-500">Input Materials:</div>
|
||||
{Object.entries(recipe.materials).map(([matId, amount]) => {
|
||||
{Object.entries(recipe.materials).map(([matId, rawAmount]) => {
|
||||
const reducedAmount = applyCostReduction(rawAmount, costReduction);
|
||||
const available = materials[matId] || 0;
|
||||
const matDrop = LOOT_DROPS[matId];
|
||||
const hasEnough = available >= amount;
|
||||
const hasEnough = available >= reducedAmount;
|
||||
|
||||
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>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user