fix: SLOT_NAMES export and refactor: split crafting-slice.ts into modules
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m51s

This commit is contained in:
Refactoring Agent
2026-05-02 10:59:36 +02:00
parent dc38445225
commit c9ae2576f4
14 changed files with 2213 additions and 942 deletions
+159
View File
@@ -0,0 +1,159 @@
// ─── 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;
}