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
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m51s
This commit is contained in:
@@ -0,0 +1,225 @@
|
||||
// ─── Crafting Design System ─────────────────────────────────────────────────
|
||||
// Design system functions: calculateDesignTime, capacity cost, XP, etc.
|
||||
|
||||
import type { EnchantmentDesign, DesignEffect, AppliedEnchantment } from './types';
|
||||
import { calculateEnchantingXP } from './data/attunements';
|
||||
import { ENCHANTMENT_EFFECTS, calculateEffectCapacityCost } from './data/enchantment-effects';
|
||||
import { hasSpecial, SPECIAL_EFFECTS } from './special-effects';
|
||||
import { computeEffects } from './upgrade-effects';
|
||||
import { EQUIPMENT_TYPES, type EquipmentCategory } from './data/equipment';
|
||||
|
||||
// ─── Design Creation & Calculation ──────────────────────────────────────────
|
||||
|
||||
// Validate effects for a design against equipment category
|
||||
export function validateDesignEffects(
|
||||
effects: DesignEffect[],
|
||||
equipmentTypeId: string,
|
||||
enchantingLevel: number
|
||||
): { valid: boolean; reason?: string } {
|
||||
if (enchantingLevel < 1) {
|
||||
return { valid: false, reason: 'Requires enchanting skill level 1' };
|
||||
}
|
||||
|
||||
const equipType = EQUIPMENT_TYPES[equipmentTypeId];
|
||||
if (!equipType) {
|
||||
return { valid: false, reason: 'Invalid equipment type' };
|
||||
}
|
||||
const category = equipType.category;
|
||||
if (!category) {
|
||||
return { valid: false, reason: 'Invalid equipment category' };
|
||||
}
|
||||
|
||||
for (const eff of effects) {
|
||||
const effectDef = ENCHANTMENT_EFFECTS[eff.effectId];
|
||||
if (!effectDef) {
|
||||
return { valid: false, reason: `Unknown effect: ${eff.effectId}` };
|
||||
}
|
||||
if (!effectDef.allowedEquipmentCategories.includes(category)) {
|
||||
return { valid: false, reason: `Effect ${eff.effectId} not allowed on ${category}` };
|
||||
}
|
||||
if (eff.stacks > effectDef.maxStacks) {
|
||||
return { valid: false, reason: `Stacks exceed maximum for ${eff.effectId}` };
|
||||
}
|
||||
}
|
||||
|
||||
return { valid: true };
|
||||
}
|
||||
|
||||
// Create an enchantment design from validated inputs
|
||||
export function createEnchantmentDesign(
|
||||
name: string,
|
||||
equipmentType: string,
|
||||
effects: DesignEffect[],
|
||||
efficiencyBonus: number = 0
|
||||
): EnchantmentDesign {
|
||||
const totalCapacityUsed = calculateDesignCapacityCost(effects, efficiencyBonus);
|
||||
const designTime = calculateDesignTime(effects);
|
||||
|
||||
return {
|
||||
id: `design_${Date.now()}`,
|
||||
name,
|
||||
equipmentType,
|
||||
effects,
|
||||
totalCapacityUsed,
|
||||
designTime,
|
||||
created: Date.now(),
|
||||
};
|
||||
}
|
||||
|
||||
// ─── Capacity Cost Calculation ──────────────────────────────────────────────
|
||||
|
||||
export function calculateDesignCapacityCost(effects: DesignEffect[], efficiencyBonus: number = 0): number {
|
||||
return effects.reduce((total, eff) => total + calculateEffectCapacityCost(eff.effectId, eff.stacks, efficiencyBonus), 0);
|
||||
}
|
||||
|
||||
export function calculateTotalCapacityCost(design: EnchantmentDesign): number {
|
||||
return design.totalCapacityUsed;
|
||||
}
|
||||
|
||||
// ─── XP & Progression ───────────────────────────────────────────────────────
|
||||
|
||||
export function calculateEnchantingXpFromDesign(design: EnchantmentDesign): number {
|
||||
return calculateEnchantingXP(design.totalCapacityUsed);
|
||||
}
|
||||
|
||||
export function calculateXpFromInstanceEnchantments(
|
||||
instance: { enchantments: AppliedEnchantment[] }
|
||||
): number {
|
||||
let totalXp = 0;
|
||||
for (const ench of instance.enchantments) {
|
||||
const effectDef = ENCHANTMENT_EFFECTS[ench.effectId];
|
||||
const baseCost = effectDef?.baseCapacityCost || 0;
|
||||
totalXp += calculateEnchantingXP(baseCost * ench.stacks);
|
||||
}
|
||||
return totalXp;
|
||||
}
|
||||
|
||||
// ─── Design Time Calculations ──────────────────────────────────────────────
|
||||
|
||||
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 getDesignTimeWithHaste(
|
||||
effects: DesignEffect[],
|
||||
isRepeatDesign: boolean,
|
||||
computedEffects: any
|
||||
): number {
|
||||
let time = calculateDesignTime(effects);
|
||||
if (isRepeatDesign && hasSpecial(computedEffects, SPECIAL_EFFECTS.HASTY_ENCHANTER)) {
|
||||
time *= 0.75;
|
||||
}
|
||||
return time;
|
||||
}
|
||||
|
||||
// ─── Progress Calculations ──────────────────────────────────────────────────
|
||||
|
||||
export interface DesignProgressUpdate {
|
||||
progress: number;
|
||||
required: number;
|
||||
isComplete: boolean;
|
||||
timeBonus: number;
|
||||
}
|
||||
|
||||
export function calculateDesignProgress(
|
||||
currentProgress: number,
|
||||
required: number,
|
||||
computedEffects: any,
|
||||
isRepeatDesign: boolean
|
||||
): DesignProgressUpdate {
|
||||
let progress = currentProgress + 0.04;
|
||||
let timeBonus = 0;
|
||||
|
||||
if (isRepeatDesign && hasSpecial(computedEffects, SPECIAL_EFFECTS.HASTY_ENCHANTER)) {
|
||||
timeBonus = 0.04 * 0.25;
|
||||
progress += timeBonus;
|
||||
}
|
||||
|
||||
if (hasSpecial(computedEffects, SPECIAL_EFFECTS.INSTANT_DESIGNS) && Math.random() < 0.10) {
|
||||
progress = required;
|
||||
}
|
||||
|
||||
return { progress, required, isComplete: progress >= required, timeBonus };
|
||||
}
|
||||
|
||||
export function calculateSecondDesignProgress(
|
||||
currentProgress: number,
|
||||
required: number,
|
||||
computedEffects: any,
|
||||
isRepeatDesign: boolean
|
||||
): DesignProgressUpdate {
|
||||
return calculateDesignProgress(currentProgress, required, computedEffects, isRepeatDesign);
|
||||
}
|
||||
|
||||
export function isSecondDesignSlotAvailable(
|
||||
designProgress: any,
|
||||
designProgress2: any,
|
||||
hasEnchantMastery: boolean
|
||||
): boolean {
|
||||
if (!designProgress && !designProgress2) return true;
|
||||
if (!designProgress && designProgress2) return false;
|
||||
if (designProgress && !designProgress2 && hasEnchantMastery) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// ─── Auto-save Completed Design ────────────────────────────────────────────
|
||||
|
||||
export function createCompletedDesignFromProgress(
|
||||
progressData: {
|
||||
designId: string;
|
||||
name: string;
|
||||
equipmentType: string;
|
||||
effects: DesignEffect[];
|
||||
required: number;
|
||||
},
|
||||
efficiencyBonus: number = 0
|
||||
): EnchantmentDesign {
|
||||
const totalCapacityCost = calculateDesignCapacityCost(progressData.effects, efficiencyBonus);
|
||||
return {
|
||||
id: progressData.designId,
|
||||
name: progressData.name,
|
||||
equipmentType: progressData.equipmentType,
|
||||
effects: progressData.effects,
|
||||
totalCapacityUsed: totalCapacityCost,
|
||||
designTime: progressData.required,
|
||||
created: Date.now(),
|
||||
};
|
||||
}
|
||||
|
||||
// ─── Design Management ──────────────────────────────────────────────────────
|
||||
|
||||
export interface DesignWithCapacityInfo {
|
||||
design: EnchantmentDesign;
|
||||
fitsInEquipment: boolean;
|
||||
availableCapacity: number;
|
||||
}
|
||||
|
||||
export function filterDesignsByEquipment(
|
||||
designs: EnchantmentDesign[],
|
||||
equipment: { instanceId: string; totalCapacity: number; usedCapacity: number } | null
|
||||
): DesignWithCapacityInfo[] {
|
||||
if (!equipment) return [];
|
||||
return designs.map(design => ({
|
||||
design,
|
||||
fitsInEquipment: designFitsInEquipment(design, {
|
||||
...equipment,
|
||||
enchantments: [],
|
||||
rarity: 'common',
|
||||
quality: 100,
|
||||
typeId: '',
|
||||
name: '',
|
||||
} as any),
|
||||
availableCapacity: equipment.totalCapacity - equipment.usedCapacity,
|
||||
}));
|
||||
}
|
||||
|
||||
function designFitsInEquipment(design: EnchantmentDesign, instance: any): boolean {
|
||||
return (instance.usedCapacity || 0) + design.totalCapacityUsed <= instance.totalCapacity;
|
||||
}
|
||||
Reference in New Issue
Block a user