generated from nhcarrigan/template
3e34701d32
Implements the three read-only vampire expansion display panels: - VampireZonesPanel: zone grid with lock state, boss/quest unlock requirements - VampireQuestsPanel: zone-filtered quest list with duration, rewards, progress badge - VampireAchievementsPanel: achievement cards with progress bars and ichor/soul shard rewards Wires all three into GameLayout, replacing the corresponding tab placeholders.
155 lines
4.2 KiB
TypeScript
155 lines
4.2 KiB
TypeScript
/**
|
|
* @file Vampire Zones panel β read-only view of all vampire realms.
|
|
* @copyright nhcarrigan
|
|
* @license Naomi's Public License
|
|
* @author Naomi Carrigan
|
|
*/
|
|
/* eslint-disable max-lines-per-function -- Complex panel with zone grid rendering */
|
|
/* eslint-disable react/no-multi-comp -- Sub-component is tightly coupled to the panel */
|
|
|
|
import { useGame } from "../../context/gameContext.js";
|
|
import type { VampireZone } from "@elysium/types";
|
|
import type { JSX } from "react";
|
|
|
|
interface ZoneCardProperties {
|
|
readonly zone: VampireZone;
|
|
readonly isLocked: boolean;
|
|
readonly unlockBossName: string | undefined;
|
|
readonly unlockQuestName: string | undefined;
|
|
}
|
|
|
|
/**
|
|
* Renders a single vampire zone card.
|
|
* @param props - The zone card properties.
|
|
* @param props.zone - The zone data.
|
|
* @param props.isLocked - Whether this zone is currently locked.
|
|
* @param props.unlockBossName - Name of the boss required to unlock, if any.
|
|
* @param props.unlockQuestName - Name of the quest required to unlock, if any.
|
|
* @returns The JSX element.
|
|
*/
|
|
const VampireZoneCard = ({
|
|
zone,
|
|
isLocked,
|
|
unlockBossName,
|
|
unlockQuestName,
|
|
}: ZoneCardProperties): JSX.Element => {
|
|
return (
|
|
<div className={`zone-card${isLocked
|
|
? " locked"
|
|
: ""}`}>
|
|
<div className="zone-card-header">
|
|
<span aria-hidden="true" className="zone-emoji">{zone.emoji}</span>
|
|
<h3 className="zone-name">{zone.name}</h3>
|
|
{isLocked
|
|
? <span aria-label="Locked" className="zone-lock-icon">{"π"}</span>
|
|
: null}
|
|
</div>
|
|
|
|
<p className="zone-description">{zone.description}</p>
|
|
|
|
{isLocked
|
|
&& (unlockBossName !== undefined || unlockQuestName !== undefined)
|
|
? <div className="zone-unlock-requirements">
|
|
<p className="zone-unlock-label">{"Unlock requirements:"}</p>
|
|
{unlockBossName === undefined
|
|
? null
|
|
: <p className="zone-unlock-item">
|
|
{"π©Έ Defeat: "}
|
|
{unlockBossName}
|
|
</p>
|
|
}
|
|
{unlockQuestName === undefined
|
|
? null
|
|
: <p className="zone-unlock-item">
|
|
{"π Complete: "}
|
|
{unlockQuestName}
|
|
</p>
|
|
}
|
|
</div>
|
|
: null}
|
|
|
|
{isLocked
|
|
? null
|
|
: <span className="zone-badge unlocked">{"π©Έ Unlocked"}</span>
|
|
}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
/**
|
|
* Renders the Vampire Zones panel showing all 18 vampire realms.
|
|
* @returns The JSX element.
|
|
*/
|
|
const VampireZonesPanel = (): JSX.Element => {
|
|
const { state } = useGame();
|
|
|
|
if (state === null) {
|
|
return (
|
|
<section className="panel">
|
|
<p>{"Loading..."}</p>
|
|
</section>
|
|
);
|
|
}
|
|
|
|
const { vampire } = state;
|
|
|
|
if (vampire === undefined) {
|
|
return (
|
|
<section className="panel">
|
|
<p>{"The Vampire expansion is not yet unlocked."}</p>
|
|
</section>
|
|
);
|
|
}
|
|
|
|
const { bosses: vampireBosses, quests: vampireQuests, zones } = vampire;
|
|
|
|
const defeatedBossIds = new Set(
|
|
vampireBosses.
|
|
filter((boss) => {
|
|
return boss.status === "defeated";
|
|
}).
|
|
map((boss) => {
|
|
return boss.id;
|
|
}),
|
|
);
|
|
|
|
return (
|
|
<section className="panel vampire-zones-panel">
|
|
<div className="panel-header">
|
|
<h2>{"πΊοΈ Vampire Zones"}</h2>
|
|
</div>
|
|
|
|
<div className="zone-grid">
|
|
{zones.map((zone) => {
|
|
const isLocked = zone.unlockBossId !== null
|
|
&& !defeatedBossIds.has(zone.unlockBossId);
|
|
|
|
const unlockBoss = zone.unlockBossId === null
|
|
? undefined
|
|
: vampireBosses.find((boss) => {
|
|
return boss.id === zone.unlockBossId;
|
|
});
|
|
|
|
const unlockQuest = zone.unlockQuestId === null
|
|
? undefined
|
|
: vampireQuests.find((quest) => {
|
|
return quest.id === zone.unlockQuestId;
|
|
});
|
|
|
|
return (
|
|
<VampireZoneCard
|
|
isLocked={isLocked}
|
|
key={zone.id}
|
|
unlockBossName={unlockBoss?.name}
|
|
unlockQuestName={unlockQuest?.name}
|
|
zone={zone}
|
|
/>
|
|
);
|
|
})}
|
|
</div>
|
|
</section>
|
|
);
|
|
};
|
|
|
|
export { VampireZonesPanel };
|