generated from nhcarrigan/template
078ae50e69
- Define EquipmentSet type + computeSetBonuses utility in packages/types - Add setId field to Equipment type and assign sets to 27 equipment items - Create 9 named equipment sets (Iron Vanguard → Eternal Throne) with 2pc/3pc bonuses - Apply set combat multiplier in boss route - Apply set gold/click multipliers in tick engine and click handler - Include set bonuses in anti-cheat delta validation - Show active set bonus strip + set badge per card in EquipmentPanel - Add boss first-kill bounty runestones (scaling 1–10 per boss tier) - Update AboutPanel and IDEAS.md
136 lines
6.4 KiB
TypeScript
136 lines
6.4 KiB
TypeScript
import { useEffect, useState } from "react";
|
||
import { getAbout } from "../../api/client.js";
|
||
import type { AboutResponse } from "@elysium/types";
|
||
|
||
const HOW_TO_PLAY = [
|
||
{
|
||
title: "⚔️ Adventurers",
|
||
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: "👆 Clicking",
|
||
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: "🔧 Upgrades",
|
||
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: "📜 Quests",
|
||
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: "👹 Boss Fights",
|
||
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: "🗺️ Zones",
|
||
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: "🗡️ Equipment & Sets",
|
||
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: "⭐ Prestige",
|
||
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. Name your prestige character to commemorate the run!",
|
||
},
|
||
{
|
||
title: "🔮 Runestones & Prestige Upgrades",
|
||
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: "🏆 Achievements",
|
||
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: "📅 Daily Challenges",
|
||
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: "☁️ Cloud Saves",
|
||
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.",
|
||
},
|
||
];
|
||
|
||
const formatDate = (dateStr: string): string =>
|
||
new Date(dateStr).toLocaleDateString("en-GB", {
|
||
day: "numeric",
|
||
month: "short",
|
||
year: "numeric",
|
||
});
|
||
|
||
export const AboutPanel = (): React.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((err: unknown) => {
|
||
setError(err instanceof Error ? err.message : "Failed to load about data.");
|
||
});
|
||
}, []);
|
||
|
||
return (
|
||
<section className="panel about-panel">
|
||
<h2>ℹ️ About</h2>
|
||
|
||
<div className="about-versions">
|
||
<div className="about-version-card">
|
||
<span className="about-version-label">🌐 Client Version</span>
|
||
<span className="about-version-value">{__WEB_VERSION__}</span>
|
||
</div>
|
||
<div className="about-version-card">
|
||
<span className="about-version-label">⚙️ API Version</span>
|
||
<span className="about-version-value">{about?.apiVersion ?? "Loading..."}</span>
|
||
</div>
|
||
</div>
|
||
|
||
<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) => (
|
||
<li key={release.tag_name} className="about-release">
|
||
<button
|
||
className="about-release-header"
|
||
type="button"
|
||
onClick={() => {
|
||
setExpandedRelease(
|
||
expandedRelease === release.tag_name ? null : release.tag_name,
|
||
);
|
||
}}
|
||
>
|
||
<span className="about-release-tag">{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">
|
||
{HOW_TO_PLAY.map((section) => (
|
||
<li key={section.title} className="about-htp-section">
|
||
<h4 className="about-htp-title">{section.title}</h4>
|
||
<p className="about-htp-body">{section.body}</p>
|
||
</li>
|
||
))}
|
||
</ul>
|
||
</section>
|
||
);
|
||
};
|