generated from nhcarrigan/template
d1d1f70c75
- Fix strict-boolean-expressions in 7 route files (runtime body validation) - Fix no-unnecessary-condition in profile.ts and offlineProgress.ts (defensive null checks) - Extend v8 ignore next-N counts in game.ts to reach 100% coverage - Add CI requirements to CLAUDE.md (lint + build + test must pass before commit) - Add manual verification checklist (verify.md) - Remove progress.md
347 lines
14 KiB
TypeScript
347 lines
14 KiB
TypeScript
/**
|
||
* @file About panel component displaying changelog and how-to-play guide.
|
||
* @copyright nhcarrigan
|
||
* @license Naomi's Public License
|
||
* @author Naomi Carrigan
|
||
*/
|
||
/* eslint-disable max-lines-per-function -- HOW_TO_PLAY data and render logic */
|
||
/* eslint-disable max-lines -- HOW_TO_PLAY data makes this file long */
|
||
import { type JSX, useEffect, useState } from "react";
|
||
import { getAbout } from "../../api/client.js";
|
||
import type { AboutResponse } from "@elysium/types";
|
||
|
||
const howToPlay = [
|
||
{
|
||
body:
|
||
"Hire adventurers to earn gold and essence automatically. Each tier is"
|
||
+ " more powerful than the last. Adventurers also contribute combat"
|
||
+ " power for boss fights — the more you recruit, the stronger your"
|
||
+ " party becomes.",
|
||
title: "⚔️ Adventurers",
|
||
},
|
||
{
|
||
body:
|
||
"Click the guild hall to earn gold manually. Upgrades and equipment can"
|
||
+ " dramatically increase your gold per click. Clicking is especially"
|
||
+ " powerful in the early game and when saving up for big purchases.",
|
||
title: "👆 Clicking",
|
||
},
|
||
{
|
||
body:
|
||
"Purchase upgrades to multiply the gold and essence output of specific"
|
||
+ " adventurer tiers, or boost your whole guild. Upgrades are permanent"
|
||
+ " for the current run and compound with each other.",
|
||
title: "🔧 Upgrades",
|
||
},
|
||
{
|
||
body:
|
||
"Send your guild on quests that complete over time and reward gold,"
|
||
+ " essence, crystals, equipment, and upgrades. Multiple quests can run"
|
||
+ " simultaneously. Completing quests also unlocks new zones.",
|
||
title: "📜 Quests",
|
||
},
|
||
{
|
||
body:
|
||
"Challenge zone bosses to earn large one-time rewards and unlock new"
|
||
+ " zones. Your party's combat power is based on the number and tier of"
|
||
+ " adventurers you've recruited. Defeated bosses cannot be re-fought,"
|
||
+ " but undefeated bosses regenerate HP over time.",
|
||
title: "👹 Boss Fights",
|
||
},
|
||
{
|
||
body:
|
||
"New zones unlock when you defeat the final boss AND complete the final"
|
||
+ " quest of the previous zone. Each zone contains new bosses and"
|
||
+ " quests with progressively greater rewards.",
|
||
title: "🗺️ Zones",
|
||
},
|
||
{
|
||
body:
|
||
"Earn equipment from boss drops and quest rewards. Each piece provides"
|
||
+ " bonuses to gold income, click power, or combat. Rarer equipment"
|
||
+ " provides stronger bonuses. Equip matching set pieces (2 or 3 of a"
|
||
+ " named set) to unlock escalating set bonuses shown at the top of the"
|
||
+ " Equipment panel.",
|
||
title: "🗡️ Equipment & Sets",
|
||
},
|
||
{
|
||
body:
|
||
"When you've progressed far enough, you can prestige to earn runestones"
|
||
+ " — a permanent currency that persists across all runs. Prestige"
|
||
+ " resets your current run but grants a production multiplier that"
|
||
+ " stacks with every prestige.",
|
||
title: "⭐ Prestige",
|
||
},
|
||
{
|
||
body:
|
||
"Spend runestones in the Prestige Shop on permanent upgrades that carry"
|
||
+ " over across all future runs. These upgrades multiply income, click"
|
||
+ " power, essence, and crystal gain — making each new run more powerful"
|
||
+ " than the last.",
|
||
title: "🔮 Runestones & Prestige Upgrades",
|
||
},
|
||
{
|
||
body:
|
||
"Purchase the Autonomous Ascension upgrade in the Prestige Shop"
|
||
+ " (100 runestones) to unlock the Auto-Prestige toggle. When enabled,"
|
||
+ " you will automatically ascend the moment you reach the prestige"
|
||
+ " threshold, using your current character name. Toggle it on and off"
|
||
+ " freely from the Prestige Shop.",
|
||
title: "⚙️ Auto-Prestige",
|
||
},
|
||
{
|
||
body:
|
||
"Earn achievements by hitting milestones — total gold earned, bosses"
|
||
+ " defeated, quests completed, and more. Achievements are purely"
|
||
+ " cosmetic and track your long-term progress across all prestige runs.",
|
||
title: "🏆 Achievements",
|
||
},
|
||
{
|
||
body:
|
||
"Complete daily challenges for bonus rewards including gold, essence,"
|
||
+ " crystals, and runestones. Challenges reset each day and vary in"
|
||
+ " difficulty. Completing all daily challenges gives an extra bonus"
|
||
+ " reward.",
|
||
title: "📅 Daily Challenges",
|
||
},
|
||
{
|
||
body:
|
||
"Send scouts to explore areas within each zone. Explorations run in"
|
||
+ " real-time and reward gold, essence, and crafting materials when"
|
||
+ " collected. Each area has a set duration — short explorations are"
|
||
+ " faster but longer ones offer rarer finds. A 📖 icon marks areas"
|
||
+ " you've collected from at least once, unlocking a Codex entry.",
|
||
title: "🗺️ Exploration",
|
||
},
|
||
{
|
||
body:
|
||
"Use materials gathered from exploration to craft permanent bonuses."
|
||
+ " Each recipe provides a multiplier to gold income, essence income,"
|
||
+ " click power, or combat power — all of which stack and persist across"
|
||
+ " prestige runs. Check the Crafting tab to see your material inventory"
|
||
+ " and available recipes per zone.",
|
||
title: "⚗️ Crafting",
|
||
},
|
||
{
|
||
body:
|
||
"Defeating bosses, completing quests, acquiring equipment, hiring"
|
||
+ " adventurers, purchasing upgrades, unlocking prestige upgrades,"
|
||
+ " discovering new zones, collecting from exploration areas, and"
|
||
+ " crafting recipes all permanently unlock lore entries in the Codex."
|
||
+ " A badge appears on the Codex tab and a toast notification pops up"
|
||
+ " each time new lore is discovered. Collect all 472 entries to build"
|
||
+ " a complete picture of the world of Elysium.",
|
||
title: "📖 Codex",
|
||
},
|
||
{
|
||
body:
|
||
"Visit the Character tab to write about your character and guild. Fill"
|
||
+ " in your character's name, pronouns, race, class, and backstory,"
|
||
+ " then create a guild with its own name and lore. Your character sheet"
|
||
+ " is visible on your public profile page.",
|
||
title: "📋 Character Sheet",
|
||
},
|
||
{
|
||
body:
|
||
"Earn Titles by reaching milestones — defeating bosses, completing"
|
||
+ " quests, prestiging, and more. Once unlocked, titles are yours"
|
||
+ " forever and are never lost on prestige or transcendence resets. Set"
|
||
+ " your active title from the Character tab to display it on your"
|
||
+ " character sheet and public profile.",
|
||
title: "🏅 Titles",
|
||
},
|
||
{
|
||
body:
|
||
"Defeat bosses to earn equipment drops: weapons, armour, and trinkets."
|
||
+ " Each item provides bonuses to gold income, combat power, or click"
|
||
+ " power. Only one item per slot can be equipped at a time — visit the"
|
||
+ " Equipment panel to manage your loadout. Your currently equipped"
|
||
+ " items are displayed on your character sheet and public profile.",
|
||
title: "🗡️ Equipment",
|
||
},
|
||
{
|
||
body:
|
||
"Compete with other adventurers on the public Leaderboards page!"
|
||
+ " Categories include Lifetime Gold, Bosses Defeated, Quests"
|
||
+ " Completed, Achievements, Prestige Count, Transcendence Count, and"
|
||
+ " Apotheosis Count. Click any player's row to view their character"
|
||
+ " sheet. You can opt out of appearing on leaderboards via the Privacy"
|
||
+ " section in your profile settings.",
|
||
title: "🏆 Leaderboards",
|
||
},
|
||
{
|
||
body:
|
||
"Log in every day to earn escalating rewards! Each consecutive day"
|
||
+ " awards more gold, and the 7th day of your streak grants bonus"
|
||
+ " crystals. Your streak resets if you miss a day. A week multiplier"
|
||
+ " increases all rewards the longer your overall streak runs. Your"
|
||
+ " current streak is displayed on your character sheet.",
|
||
title: "🔥 Daily Login Bonus",
|
||
},
|
||
{
|
||
body:
|
||
"Toggle automation in the Quests and Boss Encounters panels! Auto-Quest"
|
||
+ " automatically sends your party on the highest-zone available quest"
|
||
+ " as soon as one completes, skipping quests whose combat power"
|
||
+ " requirement isn't met. Auto-Boss automatically challenges the"
|
||
+ " highest available boss as soon as one is ready. Both can be toggled"
|
||
+ " on or off at any time using the 🤖 Auto button in each panel"
|
||
+ " header.",
|
||
title: "🤖 Auto-Quest & Auto-Boss",
|
||
},
|
||
{
|
||
body:
|
||
"Unlock companions by reaching certain milestones across all your runs."
|
||
+ " Each companion provides a powerful permanent bonus: increased"
|
||
+ " passive gold, click gold, boss damage, essence income, or reduced"
|
||
+ " quest time. You can only have one companion active at a time —"
|
||
+ " choose wisely based on your current strategy! Companions are"
|
||
+ " unlocked permanently once their condition is met and will never be"
|
||
+ " lost.",
|
||
title: "👥 Companions",
|
||
},
|
||
{
|
||
body:
|
||
"Your progress is automatically saved to the cloud every 30 seconds"
|
||
+ " whilst you play. You can also force a manual save at any time using"
|
||
+ " the sync button in the resource bar. Your save is protected by HMAC"
|
||
+ " validation to ensure data integrity.",
|
||
title: "☁️ Cloud Saves",
|
||
},
|
||
{
|
||
body:
|
||
"Transcendence is the ultimate prestige layer, unlocked by defeating"
|
||
+ " The Absolute One (requires Prestige 90). Transcending performs a"
|
||
+ " nuclear reset — wiping resources, prestige, runestones, upgrades,"
|
||
+ " and equipment — but grants Echoes based on your prestige count"
|
||
+ " (fewer prestiges = more Echoes). Echoes are permanent and survive"
|
||
+ " all future resets. Spend them in the Echo Shop on lasting"
|
||
+ " multipliers: passive income, combat power, prestige"
|
||
+ " quality-of-life, and Echo meta upgrades that amplify future Echo"
|
||
+ " yields.",
|
||
title: "🌌 Transcendence",
|
||
},
|
||
{
|
||
body:
|
||
"Apotheosis is the final act — a complete dissolution of everything you"
|
||
+ " have built, including your prestige and transcendence progress. It"
|
||
+ " is unlocked once you have purchased every Transcendence upgrade. In"
|
||
+ " exchange for this total reset, you receive the Apotheosis badge:"
|
||
+ " pure bragging rights, a mark of reaching the absolute pinnacle of"
|
||
+ " the game. Apotheosis can be achieved multiple times; each cycle"
|
||
+ " requires purchasing all Transcendence upgrades again. Your Codex"
|
||
+ " entries and lifetime profile statistics are always preserved.",
|
||
title: "✨ Apotheosis",
|
||
},
|
||
{
|
||
body:
|
||
"The Story tab contains 22 chapters that unlock as you progress. The"
|
||
+ " first 18 unlock when you defeat the final boss of each zone."
|
||
+ " Chapters 19 and 20 unlock after your first and fifth prestige"
|
||
+ " respectively. Chapter 21 unlocks on your first transcendence, and"
|
||
+ " Chapter 22 on your first apotheosis. Each chapter presents a"
|
||
+ " narrative moment and three choices — the choice you make is recorded"
|
||
+ " on your Character Sheet and shapes your guild's story. Story"
|
||
+ " progress is permanent and survives all resets.",
|
||
title: "📖 Story",
|
||
},
|
||
];
|
||
|
||
const formatDate = (dateString: string): string => {
|
||
return new Date(dateString).toLocaleDateString("en-GB", {
|
||
day: "numeric",
|
||
month: "short",
|
||
year: "numeric",
|
||
});
|
||
};
|
||
|
||
/**
|
||
* Renders the about panel with changelog and how-to-play sections.
|
||
* @returns The JSX element.
|
||
*/
|
||
const aboutPanel = (): JSX.Element => {
|
||
const [ about, setAbout ] = useState<AboutResponse | null>(null);
|
||
const [ error, setError ] = useState<string | null>(null);
|
||
const [ expandedRelease, setExpandedRelease ] = useState<string | null>(null);
|
||
|
||
useEffect(() => {
|
||
getAbout().
|
||
then(setAbout).
|
||
catch((caughtError: unknown) => {
|
||
setError(
|
||
caughtError instanceof Error
|
||
? caughtError.message
|
||
: "Failed to load about data.",
|
||
);
|
||
});
|
||
}, []);
|
||
|
||
return (
|
||
<section className="panel about-panel">
|
||
<h2>{"ℹ️ About"}</h2>
|
||
|
||
<h3 className="stats-section-header">{"📋 Changelog"}</h3>
|
||
{error !== null && <p className="about-error">{error}</p>}
|
||
{about === null && error === null
|
||
&& <p className="about-loading">{"Loading changelog..."}</p>
|
||
}
|
||
{about !== null && about.releases.length === 0
|
||
&& <p className="about-empty">{"No releases yet."}</p>
|
||
}
|
||
{about !== null && about.releases.length > 0
|
||
&& <ul className="about-releases">
|
||
{about.releases.map((release) => {
|
||
function handleToggle(): void {
|
||
setExpandedRelease(
|
||
expandedRelease === release.tag_name
|
||
? null
|
||
: release.tag_name,
|
||
);
|
||
}
|
||
return (
|
||
<li className="about-release" key={release.tag_name}>
|
||
<button
|
||
className="about-release-header"
|
||
onClick={handleToggle}
|
||
type="button"
|
||
>
|
||
<span className="about-release-tag">
|
||
{release.name.length > 0
|
||
? release.name
|
||
: release.tag_name}
|
||
</span>
|
||
<span className="about-release-date">
|
||
{formatDate(release.published_at)}
|
||
</span>
|
||
<span className="about-release-chevron">
|
||
{expandedRelease === release.tag_name
|
||
? "▲"
|
||
: "▼"}
|
||
</span>
|
||
</button>
|
||
{expandedRelease === release.tag_name
|
||
&& <pre className="about-release-body">{release.body}</pre>
|
||
}
|
||
</li>
|
||
);
|
||
})}
|
||
</ul>
|
||
}
|
||
|
||
<h3 className="stats-section-header">{"📖 How to Play"}</h3>
|
||
<ul className="about-how-to-play">
|
||
{howToPlay.map((section) => {
|
||
return (
|
||
<li className="about-htp-section" key={section.title}>
|
||
<h4 className="about-htp-title">{section.title}</h4>
|
||
<p className="about-htp-body">{section.body}</p>
|
||
</li>
|
||
);
|
||
})}
|
||
</ul>
|
||
</section>
|
||
);
|
||
};
|
||
|
||
export { aboutPanel as AboutPanel };
|