// ─── Guardian Utilities ───────────────────────────────────────────────────────── // Helper functions for guardian element resolution and name generation. import { ELEMENTS } from '../constants/elements'; // ─── Element Chain Resolution ────────────────────────────────────────────────── /** * Resolve the full component chain for an element. * Walks the recipe tree from ELEMENTS to collect all base elements + the element itself. * * Examples: * resolveUnlockChain('fire') → ['fire'] * resolveUnlockChain('metal') → ['fire', 'earth', 'metal'] * resolveUnlockChain('crystal') → ['earth', 'water', 'light', 'sand', 'crystal'] * resolveUnlockChain('stellar') → ['fire', 'light', 'stellar'] * resolveUnlockChain('void') → ['dark', 'death', 'void'] */ export function resolveUnlockChain(element: string): string[] { const result: string[] = []; const visited = new Set(); const queue: string[] = [element]; while (queue.length > 0) { const current = queue.shift()!; if (visited.has(current)) continue; visited.add(current); const def = ELEMENTS[current]; if (!def) continue; if (def.recipe) { for (const component of def.recipe) { if (!visited.has(component)) { queue.push(component); } } } else { // Base, utility, or raw element — no recipe, it's a leaf result.push(current); } } // Always include the element itself if (!result.includes(element)) { result.push(element); } return result; } /** * Resolve unlock chains for multiple elements (union, deduped). * Used when a guardian has multiple elements. * * Example: * resolveMultiUnlockChain(['fire', 'earth']) → ['fire', 'earth'] * resolveMultiUnlockChain(['metal', 'sand']) → ['fire', 'earth', 'water', 'metal', 'sand'] */ export function resolveMultiUnlockChain(elements: string[]): string[] { const all = new Set(); for (const el of elements) { for (const resolved of resolveUnlockChain(el)) { all.add(resolved); } } return Array.from(all); } // ─── Dynamic Guardian Floor Computation ──────────────────────────────────────── const TIER_CONFIG = [ // [startFloor, endFloor, tiersPerFloor, description] { start: 10, end: 80, spacing: 10 }, // Base + utility: floors 10-80 { start: 90, end: 120, spacing: 10 }, // Composite: floors 90-120 { start: 130, end: 160, spacing: 10 }, // Composite + Components { start: 170, end: 200, spacing: 10 }, // Exotic { start: 210, end: 240, spacing: 10 }, // Dual Element { start: 250, end: 290, spacing: 10 }, // Dual Composite + Components { start: 300, end: 340, spacing: 10 }, // Exotic + Components { start: 350, end: 390, spacing: 10 }, // Exotic + Composite + Components { start: 400, end: 450, spacing: 10 }, // 1 Exotic + 2 Composite + All Components ] as const; /** Get all guardian floors from a given start, dynamically computed. */ export function computeGuardianFloors(maxFloor: number = 450): number[] { const floors: number[] = []; for (const tier of TIER_CONFIG) { for (let f = tier.start; f <= Math.min(tier.end, maxFloor); f += tier.spacing) { floors.push(f); } } return floors.sort((a, b) => a - b); }