/** * @file Goddess crafting panel component for crafting recipes from sacred materials. * @copyright nhcarrigan * @license Naomi's Public License * @author Naomi Carrigan */ /* eslint-disable max-lines-per-function -- Complex component with many render paths */ /* eslint-disable max-nested-callbacks -- Nested recipe/material maps require nesting */ /* eslint-disable complexity -- Expansion preview fallback adds necessary branching */ import { type JSX, useState } from "react"; import { useGame } from "../../context/gameContext.js"; import { GODDESS_RECIPES } from "../../data/goddessCraftingRecipes.js"; import { GODDESS_MATERIALS } from "../../data/goddessMaterials.js"; import { cdnImage } from "../../utils/cdn.js"; const bonusLabel: Record = { click_power: "👆 Click Power", combat_power: "⚔️ Combat Power", essence_income: "✨ Essence Income", gold_income: "🪙 Gold Income", }; /** * Renders the goddess crafting panel for crafting recipes from sacred materials. * @returns The JSX element. */ const GoddessCraftingPanel = (): JSX.Element => { const { state, craftGoddessRecipe, formatNumber, goddessPreview } = useGame(); const [ activeZoneId, setActiveZoneId ] = useState(() => { return ( sessionStorage.getItem("elysium_goddess_craft_zone") ?? "goddess_celestial_garden" ); }); const [ pendingRecipeId, setPendingRecipeId ] = useState(null); if (state === null) { return (

{"Loading..."}

); } const goddess = state.goddess ?? goddessPreview; const playerMaterials = goddess?.exploration.materials ?? []; const craftedIds = goddess?.exploration.craftedRecipeIds ?? []; const goddessZones = goddess?.zones ?? []; const zoneRecipes = GODDESS_RECIPES.filter((recipe) => { return recipe.zoneId === activeZoneId; }); const zoneMaterials = GODDESS_MATERIALS.filter((material) => { return material.zoneId === activeZoneId; }); function getQuantity(materialId: string): number { return ( playerMaterials.find((playerMaterial) => { return playerMaterial.materialId === materialId; })?.quantity ?? 0 ); } function canAffordRecipe(recipeId: string): boolean { const recipe = GODDESS_RECIPES.find((candidateRecipe) => { return candidateRecipe.id === recipeId; }); if (recipe === undefined) { return false; } return recipe.requiredMaterials.every((request) => { return getQuantity(request.materialId) >= request.quantity; }); } function handleZoneSelect(zoneId: string): void { setActiveZoneId(zoneId); sessionStorage.setItem("elysium_goddess_craft_zone", zoneId); } async function handleCraft(recipeId: string): Promise { setPendingRecipeId(recipeId); try { await craftGoddessRecipe(recipeId); } finally { setPendingRecipeId(null); } } return (

{"⚗️ Sacred Crafting"}

{goddessZones.map((zone) => { const isLocked = zone.status === "locked"; function handleZoneClick(): void { handleZoneSelect(zone.id); } return ( ); })}

{"📦 Sacred Materials"}

{zoneMaterials.length === 0 ?

{"No materials in this zone."}

:
{zoneMaterials.map((material) => { const qty = getQuantity(material.id); return (
{material.name}
{material.name} {material.rarity}
{formatNumber(qty)}
); })}
}

{"📜 Sacred Recipes"}

{zoneRecipes.length === 0 ?

{"No recipes in this zone."}

:
{zoneRecipes.map((recipe) => { const crafted = craftedIds.includes(recipe.id); const affordable = canAffordRecipe(recipe.id); const isPending = pendingRecipeId === recipe.id; function handleCraftClick(): void { void handleCraft(recipe.id); } return (
{recipe.name}

{recipe.name}

{recipe.description}

{bonusLabel[recipe.bonus.type] ?? recipe.bonus.type} {"×"} {recipe.bonus.value.toFixed(2)}
{recipe.requiredMaterials.map((request) => { const have = getQuantity(request.materialId); const enough = have >= request.quantity; const matName = GODDESS_MATERIALS.find((mat) => { return mat.id === request.materialId; })?.name ?? request.materialId; return ( {matName} {": "} {formatNumber(have)} {"/"} {formatNumber(request.quantity)} ); })}
{crafted ? {"✅ Crafted"} : }
); })}
}
); }; export { GoddessCraftingPanel };