160 lines
6.0 KiB
TypeScript
160 lines
6.0 KiB
TypeScript
// ─── Crafting Helper Utilities ─────────────────────────────────────────────────
|
|
// Instance/ID generation and helper functions extracted from crafting-slice.ts
|
|
|
|
import type { EquipmentInstance, EnchantmentDesign, DesignEffect } from './types';
|
|
import { EQUIPMENT_TYPES, type EquipmentCategory, type EquipmentSlot } from './data/equipment';
|
|
import { ENCHANTMENT_EFFECTS, calculateEffectCapacityCost } from './data/enchantment-effects';
|
|
import { CRAFTING_RECIPES, canCraftRecipe, type CraftingRecipe } from './data/crafting-recipes';
|
|
|
|
// ─── Instance/ID Generation ──────────────────────────────────────────────────
|
|
|
|
export function generateInstanceId(): string {
|
|
return `equip_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
}
|
|
|
|
export function generateDesignId(): string {
|
|
return `design_${Date.now()}`;
|
|
}
|
|
|
|
// ─── Equipment Category & Type Helpers ───────────────────────────────────────
|
|
|
|
export function getEquipmentCategory(typeId: string): EquipmentCategory | null {
|
|
const type = EQUIPMENT_TYPES[typeId];
|
|
return type?.category || null;
|
|
}
|
|
|
|
export function getEquipmentType(typeId: string) {
|
|
return EQUIPMENT_TYPES[typeId] || null;
|
|
}
|
|
|
|
// ─── Capacity Calculation Helpers ────────────────────────────────────────────
|
|
|
|
export function calculateDesignCapacityCost(effects: DesignEffect[], efficiencyBonus: number = 0): number {
|
|
return effects.reduce((total, eff) => total + calculateEffectCapacityCost(eff.effectId, eff.stacks, efficiencyBonus), 0);
|
|
}
|
|
|
|
export function getAvailableCapacity(instance: EquipmentInstance): number {
|
|
return instance.totalCapacity - instance.usedCapacity;
|
|
}
|
|
|
|
export function designFitsInEquipment(design: EnchantmentDesign, instance: EquipmentInstance): boolean {
|
|
return instance.usedCapacity + design.totalCapacityUsed <= instance.totalCapacity;
|
|
}
|
|
|
|
// ─── Time Calculation Helpers ────────────────────────────────────────────────
|
|
|
|
export function calculateDesignTime(effects: DesignEffect[]): number {
|
|
let time = 1;
|
|
for (const eff of effects) {
|
|
const effectDef = ENCHANTMENT_EFFECTS[eff.effectId];
|
|
if (effectDef) {
|
|
time += 0.5 * eff.stacks;
|
|
}
|
|
}
|
|
return time;
|
|
}
|
|
|
|
export function calculatePrepTime(equipmentCapacity: number): number {
|
|
return 2 + Math.floor(equipmentCapacity / 50);
|
|
}
|
|
|
|
export function calculateApplicationTime(design: EnchantmentDesign): number {
|
|
return 2 + design.effects.reduce((total, eff) => total + eff.stacks, 0);
|
|
}
|
|
|
|
// ─── Mana Cost Calculation Helpers ───────────────────────────────────────────
|
|
|
|
export function calculatePrepManaCost(equipmentCapacity: number): number {
|
|
return equipmentCapacity * 10;
|
|
}
|
|
|
|
export function calculateApplicationManaPerHour(design: EnchantmentDesign): number {
|
|
return 20 + design.effects.reduce((total, eff) => total + eff.stacks * 5, 0);
|
|
}
|
|
|
|
export function calculateManaPerHourForPrep(equipmentCapacity: number, prepTime: number): number {
|
|
return calculatePrepManaCost(equipmentCapacity) / prepTime;
|
|
}
|
|
|
|
// ─── Recipe & Crafting Helpers ───────────────────────────────────────────────
|
|
|
|
export function checkRecipeMaterials(
|
|
recipe: CraftingRecipe,
|
|
materials: Record<string, number>
|
|
): { canCraft: boolean; missingMaterials: Record<string, number> } {
|
|
const missingMaterials: Record<string, number> = {};
|
|
let canCraft = true;
|
|
|
|
for (const [matId, amount] of Object.entries(recipe.materials)) {
|
|
const have = materials[matId] || 0;
|
|
if (have < amount) {
|
|
missingMaterials[matId] = amount - have;
|
|
canCraft = false;
|
|
}
|
|
}
|
|
|
|
return { canCraft, missingMaterials };
|
|
}
|
|
|
|
export function deductRecipeMaterials(
|
|
recipe: CraftingRecipe,
|
|
materials: Record<string, number>
|
|
): Record<string, number> {
|
|
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 newMaterials;
|
|
}
|
|
|
|
export function refundCraftMaterials(recipe: CraftingRecipe, refundRate: number = 0.5): Record<string, number> {
|
|
const refunds: Record<string, number> = {};
|
|
for (const [matId, amount] of Object.entries(recipe.materials)) {
|
|
refunds[matId] = Math.floor(amount * refundRate);
|
|
}
|
|
return refunds;
|
|
}
|
|
|
|
// ─── Validation Helpers ──────────────────────────────────────────────────────
|
|
|
|
export function canEquipInSlot(
|
|
instance: EquipmentInstance,
|
|
slot: EquipmentSlot,
|
|
currentlyEquipped: Record<EquipmentSlot, string | null>
|
|
): boolean {
|
|
const type = EQUIPMENT_TYPES[instance.typeId];
|
|
if (!type) return false;
|
|
|
|
const validSlots = type.category === 'accessory'
|
|
? ['accessory1', 'accessory2']
|
|
: [type.slot];
|
|
|
|
if (!validSlots.includes(slot)) return false;
|
|
if (currentlyEquipped[slot] === instance.instanceId) return true;
|
|
|
|
const isTwoHanded = type.twoHanded === true;
|
|
|
|
if (isTwoHanded) {
|
|
if (currentlyEquipped.mainHand || currentlyEquipped.offHand) {
|
|
return false;
|
|
}
|
|
if (slot !== 'mainHand') return false;
|
|
}
|
|
|
|
if (slot === 'offHand' && currentlyEquipped.mainHand) {
|
|
const mainHandType = EQUIPMENT_TYPES[currentlyEquipped.mainHand];
|
|
if (mainHandType?.twoHanded) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
export function isTwoHanded(typeId: string): boolean {
|
|
return EQUIPMENT_TYPES[typeId]?.twoHanded === true;
|
|
}
|