180 lines
6.8 KiB
TypeScript
180 lines
6.8 KiB
TypeScript
// ─── Equipment Crafting System ──────────────────────────────────────────────
|
|
// Equipment crafting functions
|
|
|
|
import type { EquipmentInstance, EquipmentCraftingProgress } from './types';
|
|
import { CRAFTING_RECIPES, canCraftRecipe, type CraftingRecipe } from './data/crafting-recipes';
|
|
import { EQUIPMENT_TYPES } from './data/equipment';
|
|
import { ok, fail, ErrorCode } from './utils/result';
|
|
import type { Result } from './utils/result';
|
|
import { HOURS_PER_TICK } from './constants';
|
|
|
|
const MANA_REFUND_RATE = 0.5;
|
|
|
|
// ─── Equipment Crafting Validation ──────────────────────────────────────────
|
|
|
|
export function canStartEquipmentCrafting(
|
|
blueprintId: string,
|
|
hasBlueprint: boolean,
|
|
materials: Record<string, number>,
|
|
currentMana: number,
|
|
currentAction: string
|
|
): { canCraft: boolean; reason?: string; recipe?: CraftingRecipe; missingMaterials?: Record<string, number>; missingMana?: number } {
|
|
if (currentAction !== 'meditate') return { canCraft: false, reason: 'Must be in meditate state' };
|
|
|
|
const recipe = CRAFTING_RECIPES[blueprintId];
|
|
if (!recipe) return { canCraft: false, reason: 'Invalid blueprint' };
|
|
if (!hasBlueprint) return { canCraft: false, reason: 'Blueprint not acquired' };
|
|
|
|
const { canCraft, missingMaterials } = canCraftRecipe(recipe, materials, currentMana);
|
|
if (canCraft) return { canCraft: true, recipe };
|
|
|
|
const missingManaAmount = Math.max(0, recipe.manaCost - currentMana);
|
|
return {
|
|
canCraft: false,
|
|
reason: missingManaAmount > 0 ? 'Insufficient mana' : 'Missing materials',
|
|
recipe,
|
|
missingMaterials,
|
|
missingMana: missingManaAmount > 0 ? missingManaAmount : undefined,
|
|
};
|
|
}
|
|
|
|
// ─── Equipment Crafting Execution ───────────────────────────────────────────
|
|
|
|
export interface CraftingInitResult {
|
|
recipe: CraftingRecipe;
|
|
newMaterials: Record<string, number>;
|
|
manaCost: number;
|
|
progress: EquipmentCraftingProgress;
|
|
}
|
|
|
|
export function initializeEquipmentCrafting(
|
|
blueprintId: string,
|
|
materials: Record<string, number>,
|
|
_currentMana: number
|
|
): CraftingInitResult {
|
|
const recipe = CRAFTING_RECIPES[blueprintId];
|
|
|
|
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 {
|
|
recipe,
|
|
newMaterials,
|
|
manaCost: recipe.manaCost,
|
|
progress: {
|
|
blueprintId,
|
|
equipmentTypeId: recipe.equipmentTypeId,
|
|
progress: 0,
|
|
required: recipe.craftTime,
|
|
manaSpent: recipe.manaCost,
|
|
},
|
|
};
|
|
}
|
|
|
|
// ─── Crafting Progress ──────────────────────────────────────────────────────
|
|
|
|
export interface CraftingTickResult {
|
|
progress: number;
|
|
isComplete: boolean;
|
|
}
|
|
|
|
export function calculateCraftingTick(currentProgress: number, required: number): CraftingTickResult {
|
|
const progress = currentProgress + HOURS_PER_TICK;
|
|
return { progress, isComplete: progress >= required };
|
|
}
|
|
|
|
// ─── Crafting Completion ───────────────────────────────────────────────────
|
|
|
|
const BASE_EQUIPMENT_QUALITY = 100;
|
|
|
|
export function completeEquipmentCrafting(
|
|
blueprintId: string,
|
|
recipe: CraftingRecipe
|
|
): Result<{ instanceId: string; instance: EquipmentInstance; logMessage: string }> {
|
|
const equipType = EQUIPMENT_TYPES[recipe.equipmentTypeId];
|
|
if (!equipType) return fail(ErrorCode.INVALID_EQUIPMENT_TYPE, `Invalid equipment type: ${recipe.equipmentTypeId}`);
|
|
|
|
const instanceId = `equip_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
return ok({
|
|
instanceId,
|
|
instance: {
|
|
instanceId,
|
|
typeId: recipe.equipmentTypeId,
|
|
name: recipe.name,
|
|
enchantments: [],
|
|
usedCapacity: 0,
|
|
totalCapacity: equipType.baseCapacity,
|
|
rarity: recipe.rarity,
|
|
quality: BASE_EQUIPMENT_QUALITY,
|
|
tags: [],
|
|
},
|
|
logMessage: `🔨 Crafted ${recipe.name}!`,
|
|
});
|
|
}
|
|
|
|
// ─── Crafting Cancellation ──────────────────────────────────────────────────
|
|
|
|
export interface CraftingCancelResult {
|
|
manaRefund: number;
|
|
logMessage: string;
|
|
}
|
|
|
|
export function cancelEquipmentCrafting(blueprintId: string, manaSpent: number): CraftingCancelResult {
|
|
const recipe = CRAFTING_RECIPES[blueprintId];
|
|
if (!recipe) return { manaRefund: 0, logMessage: 'Invalid crafting recipe.' };
|
|
|
|
const manaRefund = Math.floor(manaSpent * MANA_REFUND_RATE);
|
|
return { manaRefund, logMessage: `🚫 Equipment crafting cancelled. Refunded ${manaRefund} mana.` };
|
|
}
|
|
|
|
// ─── Recipe Information ─────────────────────────────────────────────────────
|
|
|
|
export function getRecipe(blueprintId: string): CraftingRecipe | null {
|
|
return CRAFTING_RECIPES[blueprintId] || null;
|
|
}
|
|
|
|
export function getCraftableRecipes(
|
|
blueprints: string[],
|
|
materials: Record<string, number>,
|
|
currentMana: number
|
|
): CraftingRecipe[] {
|
|
const craftable: CraftingRecipe[] = [];
|
|
for (const blueprintId of blueprints) {
|
|
const recipe = CRAFTING_RECIPES[blueprintId];
|
|
if (!recipe) continue;
|
|
if (canCraftRecipe(recipe, materials, currentMana).canCraft) craftable.push(recipe);
|
|
}
|
|
return craftable;
|
|
}
|
|
|
|
// ─── Material Management ────────────────────────────────────────────────────
|
|
|
|
export function deleteMaterials(materialId: string, amount: number, materials: Record<string, number>): {
|
|
newMaterials: Record<string, number>;
|
|
deleted: number;
|
|
} {
|
|
const currentAmount = materials[materialId] || 0;
|
|
const deleted = Math.min(amount, currentAmount);
|
|
const remaining = Math.max(0, currentAmount - amount);
|
|
const newMaterials = { ...materials };
|
|
if (remaining <= 0) {
|
|
delete newMaterials[materialId];
|
|
} else {
|
|
newMaterials[materialId] = remaining;
|
|
}
|
|
return { newMaterials, deleted };
|
|
}
|
|
|
|
export function getMaterialCount(materials: Record<string, number>, materialId: string): number {
|
|
return materials[materialId] || 0;
|
|
}
|
|
|
|
export function addMaterials(materials: Record<string, number>, materialId: string, amount: number): Record<string, number> {
|
|
const newMaterials = { ...materials };
|
|
newMaterials[materialId] = (newMaterials[materialId] || 0) + amount;
|
|
return newMaterials;
|
|
}
|