/** * @file Read-only panel displaying goddess quests grouped by zone. * @copyright nhcarrigan * @license Naomi's Public License * @author Naomi Carrigan */ /* eslint-disable max-lines-per-function -- Complex component with many render paths */ /* eslint-disable react/no-multi-comp -- QuestCard sub-component is tightly coupled */ import { useState, type JSX } from "react"; import { useGame } from "../../context/gameContext.js"; import type { GoddessQuest, GoddessQuestReward, GoddessZone, } from "@elysium/types"; /** * Formats a duration in seconds to a human-readable string. * @param seconds - The total number of seconds to format. * @returns The formatted duration string. */ const formatDuration = (seconds: number): string => { const secondsPerHour = 3600; const secondsPerMinute = 60; if (seconds >= secondsPerHour) { const hours = Math.floor(seconds / secondsPerHour); const remainderSeconds = seconds % secondsPerHour; const minutes = Math.floor(remainderSeconds / secondsPerMinute); return `${String(hours)}h ${String(minutes)}m`; } if (seconds >= secondsPerMinute) { const minutes = Math.floor(seconds / secondsPerMinute); const secs = seconds % secondsPerMinute; return `${String(minutes)}m ${String(secs)}s`; } return `${String(seconds)}s`; }; /** * Returns a human-readable label string for a goddess quest reward. * @param reward - The reward to describe. * @param formatNumber - The number formatter function. * @returns The label string, or an empty string for unknown types. */ const getRewardLabel = ( reward: GoddessQuestReward, formatNumber: (value: number)=> string, ): string => { if (reward.type === "prayers") { return `🙏 ${formatNumber(reward.amount ?? 0)} Prayers`; } if (reward.type === "divinity") { return `✨ ${formatNumber(reward.amount ?? 0)} Divinity`; } if (reward.type === "stardust") { return `⭐ ${formatNumber(reward.amount ?? 0)} Stardust`; } if (reward.type === "upgrade") { return "🔓 Upgrade Unlocked"; } if (reward.type === "disciple") { return "👤 New Disciple Tier"; } return "🛡️ Equipment Unlocked"; }; interface GoddessQuestCardProperties { readonly quest: GoddessQuest; readonly unlockHint: string | undefined; readonly zoneIsOpen: boolean; } /** * Renders a single goddess quest card (read-only). * @param props - The component properties. * @param props.quest - The goddess quest to display. * @param props.unlockHint - The name of the prerequisite quest, if locked. * @param props.zoneIsOpen - Whether the quest's zone is currently unlocked. * @returns The JSX element. */ const GoddessQuestCard = ({ quest, unlockHint, zoneIsOpen, }: GoddessQuestCardProperties): JSX.Element => { const { formatNumber } = useGame(); return (

{quest.name}

{quest.description}

{"⏱ "} {formatDuration(quest.durationSeconds)}

{quest.rewards.map((reward, rewardIndex) => { return {getRewardLabel(reward, formatNumber)} ; })}
{quest.status === "locked" && !zoneIsOpen && {"🔒 Zone Locked"} } {quest.status === "locked" && zoneIsOpen ? <> {"🔒 Locked"} {unlockHint !== undefined &&

{"📜 Complete: "} {unlockHint}

} : null } {quest.status === "available" && {"📋 Available"} } {quest.status === "active" && {"⏳ In Progress"} } {quest.status === "completed" && {"✅ Completed"} }
); }; /** * Renders the goddess quests panel with zone selection and quest list. * @returns The JSX element. */ const GoddessQuestsPanel = (): JSX.Element => { const { state } = useGame(); const [ activeZoneId, setActiveZoneId ] = useState(() => { return sessionStorage.getItem("elysium_goddess_quest_zone") ?? "goddess_celestial_garden"; }); if (state === null) { return (

{"Loading..."}

); } const goddessState = state.goddess; if (goddessState === undefined) { return (

{"Goddess expansion not yet unlocked."}

); } const { zones, quests } = goddessState; const activeZone = zones.find((zone: GoddessZone) => { return zone.id === activeZoneId; }); const zoneIsOpen = activeZone?.status === "unlocked"; const zoneQuests = quests.filter((quest: GoddessQuest) => { return quest.zoneId === activeZoneId; }); const questNameById = new Map( quests.map((quest: GoddessQuest) => { return [ quest.id, quest.name ]; }), ); const getUnlockHint = (quest: GoddessQuest): string | undefined => { if (quest.status !== "locked" || quest.prerequisiteIds.length === 0) { return undefined; } const [ prereqId ] = quest.prerequisiteIds; if (prereqId === undefined) { return undefined; } return questNameById.get(prereqId); }; function handleZoneSelect(zoneId: string): void { setActiveZoneId(zoneId); sessionStorage.setItem("elysium_goddess_quest_zone", zoneId); } const completedCount = zoneQuests.filter((quest: GoddessQuest) => { return quest.status === "completed"; }).length; return (

{"Goddess Quests"}

{zones.map((zone: GoddessZone) => { function handleClick(): void { handleZoneSelect(zone.id); } return ; })}
{activeZone !== undefined &&

{activeZone.description}

{String(completedCount)} {" / "} {String(zoneQuests.length)} {" quests completed"}

{activeZone.status === "locked" &&

{"🔒 This zone is locked. Defeat the required goddess boss"} {" to unlock it."}

}
}
{zoneQuests.length === 0 ?

{"No quests in this zone."}

: zoneQuests.map((quest: GoddessQuest) => { return ; }) }
); }; export { GoddessQuestsPanel };